Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d44e2ad7 authored by Rohit Vaswani's avatar Rohit Vaswani Committed by Matt Wagantall
Browse files

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: default avatarRohit Vaswani <rvaswani@codeaurora.org>
[abhimany: fixup the nmi header to account for arm64]
Signed-off-by: default avatarAbhimanyu Kapur <abhimany@codeaurora.org>
parent b3385bd8
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@
#include <linux/threads.h>
#include <asm/irq.h>

#define NR_IPI	7
#define NR_IPI	8

typedef struct {
	unsigned int __softirq_pending;
+3 −0
Original line number Diff line number Diff line
@@ -7,4 +7,7 @@ extern void (*handle_arch_irq)(struct pt_regs *);
extern void migrate_irqs(void);
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));

void arch_trigger_all_cpu_backtrace(void);
#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace

#endif
+71 −0
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ enum ipi_msg_type {
	IPI_TIMER,
	IPI_IRQ_WORK,
	IPI_WAKEUP,
	IPI_CPU_BACKTRACE,
};

/*
@@ -497,6 +498,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)
@@ -581,6 +583,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);
	cpu_clear(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 (!cpus_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 (cpu_isset(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);
		cpu_clear(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
 */
@@ -634,6 +702,9 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
		irq_exit();
		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);
+2 −3
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ static inline bool watchdog_hardlockup_detector_is_enabled(void)
#ifdef arch_trigger_all_cpu_backtrace
static inline bool trigger_all_cpu_backtrace(void)
{
	#if defined(CONFIG_ARM)
	#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
	arch_trigger_all_cpu_backtrace();
	#else
	arch_trigger_all_cpu_backtrace(true);
@@ -58,13 +58,12 @@ static inline bool trigger_all_cpu_backtrace(void)
}
static inline bool trigger_allbutself_cpu_backtrace(void)
{
	#if defined(CONFIG_ARM)
	#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
	arch_trigger_all_cpu_backtrace();
	#else
	arch_trigger_all_cpu_backtrace(false);
	#endif


	return true;
}
#else