diff options
| author | Rohit Vaswani <rvaswani@codeaurora.org> | 2014-06-26 23:35:09 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:10:14 -0700 |
| commit | ec6b98c86d668284f676443133d66d788e80b58f (patch) | |
| tree | 8c4aff83fdde65edc80652e90110805f339142b1 | |
| parent | 99c91673cebeb5df45b9d2e53b9d36f0090efcd8 (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.h | 2 | ||||
| -rw-r--r-- | arch/arm64/include/asm/irq.h | 3 | ||||
| -rw-r--r-- | arch/arm64/kernel/smp.c | 72 | ||||
| -rw-r--r-- | include/linux/nmi.h | 9 |
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; } |
