summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRohit Vaswani <rvaswani@codeaurora.org>2014-06-26 23:35:09 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:10:14 -0700
commitec6b98c86d668284f676443133d66d788e80b58f (patch)
tree8c4aff83fdde65edc80652e90110805f339142b1
parent99c91673cebeb5df45b9d2e53b9d36f0090efcd8 (diff)
ARM64: smp: implement arch_trigger_all_cpus_backtrace using IPI
Since ARM64 doesn't have an NMI, send an IPI to all other CPUs (current cpu prints the stack directly) to capture a backtrace. Change-Id: Ib90494123205b3bbaa0b244ccde6c7e40a560199 Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> [satyap: trivial merge conflict resolution & compilation fixes] Signed-off-by: Satya Durga Srinivasu Prabhala <satyap@codeaurora.org>
-rw-r--r--arch/arm64/include/asm/hardirq.h2
-rw-r--r--arch/arm64/include/asm/irq.h3
-rw-r--r--arch/arm64/kernel/smp.c72
-rw-r--r--include/linux/nmi.h9
4 files changed, 85 insertions, 1 deletions
diff --git a/arch/arm64/include/asm/hardirq.h b/arch/arm64/include/asm/hardirq.h
index 8740297dac77..1473fc2f7ab7 100644
--- a/arch/arm64/include/asm/hardirq.h
+++ b/arch/arm64/include/asm/hardirq.h
@@ -20,7 +20,7 @@
#include <linux/threads.h>
#include <asm/irq.h>
-#define NR_IPI 6
+#define NR_IPI 7
typedef struct {
unsigned int __softirq_pending;
diff --git a/arch/arm64/include/asm/irq.h b/arch/arm64/include/asm/irq.h
index 8e8d30684392..a8464c18ef88 100644
--- a/arch/arm64/include/asm/irq.h
+++ b/arch/arm64/include/asm/irq.h
@@ -12,4 +12,7 @@ static inline int nr_legacy_irqs(void)
return 0;
}
+void arch_trigger_all_cpu_backtrace(void);
+#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace
+
#endif
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index b76b4b096694..a2163d972b81 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -71,6 +71,7 @@ enum ipi_msg_type {
IPI_TIMER,
IPI_IRQ_WORK,
IPI_WAKEUP,
+ IPI_CPU_BACKTRACE,
};
/*
@@ -631,6 +632,7 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = {
S(IPI_TIMER, "Timer broadcast interrupts"),
S(IPI_IRQ_WORK, "IRQ work interrupts"),
S(IPI_WAKEUP, "CPU wakeup interrupts"),
+ S(IPI_CPU_BACKTRACE, "CPU backtrace"),
};
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
@@ -719,6 +721,72 @@ static void ipi_cpu_stop(unsigned int cpu, struct pt_regs *regs)
cpu_relax();
}
+static cpumask_t backtrace_mask;
+static DEFINE_RAW_SPINLOCK(backtrace_lock);
+
+/* "in progress" flag of arch_trigger_all_cpu_backtrace */
+static unsigned long backtrace_flag;
+
+static void smp_send_all_cpu_backtrace(void)
+{
+ unsigned int this_cpu = smp_processor_id();
+ int i;
+
+ if (test_and_set_bit(0, &backtrace_flag))
+ /*
+ * If there is already a trigger_all_cpu_backtrace() in progress
+ * (backtrace_flag == 1), don't output double cpu dump infos.
+ */
+ return;
+
+ cpumask_copy(&backtrace_mask, cpu_online_mask);
+ cpumask_clear_cpu(this_cpu, &backtrace_mask);
+
+ pr_info("Backtrace for cpu %d (current):\n", this_cpu);
+ dump_stack();
+
+ pr_info("\nsending IPI to all other CPUs:\n");
+ if (!cpumask_empty(&backtrace_mask))
+ smp_cross_call(&backtrace_mask, IPI_CPU_BACKTRACE);
+
+ /* Wait for up to 10 seconds for all other CPUs to do the backtrace */
+ for (i = 0; i < 10 * 1000; i++) {
+ if (cpumask_empty(&backtrace_mask))
+ break;
+ mdelay(1);
+ }
+
+ clear_bit(0, &backtrace_flag);
+ smp_mb__after_clear_bit();
+}
+
+/*
+ * ipi_cpu_backtrace - handle IPI from smp_send_all_cpu_backtrace()
+ */
+static void ipi_cpu_backtrace(unsigned int cpu, struct pt_regs *regs)
+{
+ if (cpumask_test_cpu(cpu, &backtrace_mask)) {
+ raw_spin_lock(&backtrace_lock);
+ pr_warn("IPI backtrace for cpu %d\n", cpu);
+ show_regs(regs);
+ raw_spin_unlock(&backtrace_lock);
+ cpumask_clear_cpu(cpu, &backtrace_mask);
+ }
+}
+
+#ifdef CONFIG_SMP
+void arch_trigger_all_cpu_backtrace(void)
+{
+ smp_send_all_cpu_backtrace();
+}
+#else
+void arch_trigger_all_cpu_backtrace(void)
+{
+ dump_stack();
+}
+#endif
+
+
/*
* Main handler for inter-processor interrupts
*/
@@ -768,6 +836,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
case IPI_WAKEUP:
break;
+ case IPI_CPU_BACKTRACE:
+ ipi_cpu_backtrace(cpu, regs);
+ break;
+
default:
pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
break;
diff --git a/include/linux/nmi.h b/include/linux/nmi.h
index 74385351935d..9aeddb80cfd3 100644
--- a/include/linux/nmi.h
+++ b/include/linux/nmi.h
@@ -41,13 +41,22 @@ static inline void hardlockup_detector_disable(void) {}
#ifdef arch_trigger_all_cpu_backtrace
static inline bool trigger_all_cpu_backtrace(void)
{
+ #if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+ arch_trigger_all_cpu_backtrace();
+ #else
arch_trigger_all_cpu_backtrace(true);
+ #endif
return true;
}
static inline bool trigger_allbutself_cpu_backtrace(void)
{
+ #if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+ arch_trigger_all_cpu_backtrace();
+ #else
arch_trigger_all_cpu_backtrace(false);
+ #endif
+
return true;
}