2013-03-25 18:50:53

by Mike Travis

[permalink] [raw]
Subject: [PATCH 15/15] x86/UV: Add call to KGDB/KDB from NMI handler

This patch restores the ability to enter KDB (and KGDB) from the UV
NMI handler. It utilizes the newly added kgdb_nmicallin function
to gain entry to KGDB/KDB by the master. The slaves still enter via
the standard kgdb_nmicallback function.

The handler also uses the new 'send_ready' pointer to tell KGDB/KDB
to signal the slaves when to proceed into the KGDB slave loop.

Cc: Alexander Gordeev <[email protected]>
Cc: Suresh Siddha <[email protected]>
Cc: "Michael S. Tsirkin" <[email protected]>
Cc: Steffen Persvold <[email protected]>
Reviewed-by: Dimitri Sivanich <[email protected]>
Signed-off-by: Mike Travis <[email protected]>
---
arch/x86/platform/uv/uv_nmi.c | 73 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 71 insertions(+), 2 deletions(-)

--- linux.orig/arch/x86/platform/uv/uv_nmi.c
+++ linux/arch/x86/platform/uv/uv_nmi.c
@@ -21,6 +21,8 @@

#include <linux/cpu.h>
#include <linux/delay.h>
+#include <linux/kgdb.h>
+#include <linux/kdb.h>
#include <linux/module.h>
#include <linux/nmi.h>
#include <linux/sched.h>
@@ -34,6 +36,7 @@
#include <asm/current.h>
#include <asm/kdebug.h>
#include <asm/local64.h>
+#include <asm/traps.h>
#include <asm/uv/uv.h>
#include <asm/uv/uv_hub.h>
#include <asm/uv/uv_mmrs.h>
@@ -521,6 +524,68 @@ static void uv_nmi_touch_watchdogs(void)
touch_nmi_watchdog();
}

+#ifdef CONFIG_KGDB_KDB
+
+/* Disable to force process dump instead of entering KDB or KGDB */
+static int uv_nmi_kdb_on = 1;
+module_param_named(kdb_on, uv_nmi_kdb_on, int, 0644);
+
+/* Call KDB from NMI handler */
+static void uv_call_kdb(int cpu, struct pt_regs *regs,
+ int master, unsigned long *flags)
+{
+ int ret;
+
+ if (master) {
+ /* call KGDB NMI handler as MASTER */
+ local_irq_restore(*flags);
+ ret = kgdb_nmicallin(cpu, X86_TRAP_NMI, regs,
+ &uv_nmi_slave_continue);
+ local_irq_save(*flags);
+
+ /*
+ * if KGDB/KDB did not handle the NMI, then signal slaves
+ * to do process dump instead.
+ */
+ if (ret) {
+ uv_nmi_dump_state(cpu, regs, 1);
+ return;
+ }
+ } else {
+ int sig;
+
+ /* wait for KGDB to say it's ready for slaves to enter */
+ do {
+ cpu_relax();
+ sig = atomic_read(&uv_nmi_slave_continue);
+ } while (!sig);
+
+ /*
+ * if KGDB/KDB did not handle the NMI for the master, then
+ * the master signals the slaves to do process dump instead.
+ */
+ if (sig == 2) {
+ uv_nmi_dump_state(cpu, regs, 0);
+ return;
+ }
+
+ /* call KGDB as slave */
+ local_irq_restore(*flags);
+ ret = kgdb_nmicallback(cpu, regs);
+ local_irq_save(*flags);
+ }
+ uv_nmi_sync_exit(master);
+}
+
+#else /* !CONFIG_KGDB_KDB */
+static inline void uv_call_kdb(int cpu, struct pt_regs *regs,
+ int master, unsigned long *flags)
+{
+ pr_err("UV: NMI error: KDB is not enabled in this kernel\n");
+ uv_nmi_dump_state(cpu, regs, master);
+}
+#endif /* !CONFIG_KGDB_KDB */
+
/*
* UV NMI handler
*/
@@ -547,8 +612,12 @@ int uv_handle_nmi(unsigned int reason, s
if (master && uv_nmi_kdump_requested)
uv_nmi_kdump(regs);

- /* Dump state of each cpu */
- uv_nmi_dump_state(cpu, regs, master);
+ /* Call KDB if enabled */
+ if (uv_nmi_kdb_on)
+ uv_call_kdb(cpu, regs, master, &flags);
+
+ else /* Otherwise dump state of each cpu */
+ uv_nmi_dump_state(cpu, regs, master);

/* Clear per_cpu "in nmi" flag */
atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_OUT);

--