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> Signed-off-by: Satya Durga Srinivasu Prabhala <satyap@codeaurora.org> [kyan: trivial merge conflict resolution & compilation fixes] Signed-off-by: Kyle Yan <kyan@codeaurora.org> Signed-off-by: Jeevan Shriram <jshriram@codeaurora.org>
This commit is contained in:
parent
daf247770f
commit
be186fdfdc
4 changed files with 91 additions and 4 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -56,5 +56,8 @@ static inline bool on_irq_stack(unsigned long sp, int cpu)
|
|||
return (low <= sp && sp <= high);
|
||||
}
|
||||
|
||||
void arch_trigger_all_cpu_backtrace(void);
|
||||
#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -73,7 +73,8 @@ enum ipi_msg_type {
|
|||
IPI_CPU_STOP,
|
||||
IPI_TIMER,
|
||||
IPI_IRQ_WORK,
|
||||
IPI_WAKEUP
|
||||
IPI_WAKEUP,
|
||||
IPI_CPU_BACKTRACE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARM64_VHE
|
||||
|
|
@ -760,6 +761,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 wake-up interrupts"),
|
||||
S(IPI_CPU_BACKTRACE, "CPU backtrace"),
|
||||
};
|
||||
|
||||
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
|
||||
|
|
@ -846,6 +848,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_atomic();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
|
@ -900,6 +968,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
|
|||
break;
|
||||
#endif
|
||||
|
||||
case IPI_CPU_BACKTRACE:
|
||||
ipi_cpu_backtrace(cpu, regs);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -41,25 +41,37 @@ static inline void hardlockup_detector_disable(void) {}
|
|||
#ifdef arch_trigger_cpumask_backtrace
|
||||
static inline bool trigger_all_cpu_backtrace(void)
|
||||
{
|
||||
arch_trigger_cpumask_backtrace(cpu_online_mask, false);
|
||||
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
|
||||
arch_trigger_all_cpu_backtrace();
|
||||
else
|
||||
arch_trigger_cpumask_backtrace(cpu_online_mask, false);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool trigger_allbutself_cpu_backtrace(void)
|
||||
{
|
||||
arch_trigger_cpumask_backtrace(cpu_online_mask, true);
|
||||
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
|
||||
arch_trigger_all_cpu_backtrace();
|
||||
else
|
||||
arch_trigger_cpumask_backtrace(cpu_online_mask, true);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool trigger_cpumask_backtrace(struct cpumask *mask)
|
||||
{
|
||||
arch_trigger_cpumask_backtrace(mask, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool trigger_single_cpu_backtrace(int cpu)
|
||||
{
|
||||
arch_trigger_cpumask_backtrace(cpumask_of(cpu), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue