From 41d10cf0b562e8c680101b8b3972982a06ed5b0e Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Fri, 11 Sep 2015 16:13:16 +0100 Subject: arm64: Implement IPI_CPU_BACKTRACE using pseudo-NMIs Recently arm64 gained the capability to (optionally) mask interrupts using the GIC PMR rather than the CPU PSR. That allows us to introduce an NMI-like means to handle backtrace requests. This provides a useful debug aid by allowing the kernel to robustly show a backtrace for every processor in the system when, for example, we hang trying to acquire a spin lock. Signed-off-by: Daniel Thompson --- arch/arm64/kernel/smp.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'arch/arm64/kernel/smp.c') diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 206dceb725fc..103731f3d74f 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -863,11 +863,15 @@ void handle_IPI(int ipinr, struct pt_regs *regs) #endif case IPI_CPU_BACKTRACE: - printk_nmi_enter(); - irq_enter(); + if (in_nmi()) { + printk_nmi_enter(); + irq_enter(); + } nmi_cpu_backtrace(regs); - irq_exit(); - printk_nmi_exit(); + if (in_nmi()) { + irq_exit(); + printk_nmi_exit(); + } break; #ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL @@ -934,13 +938,31 @@ int setup_profiling_timer(unsigned int multiplier) return -EINVAL; } +/* + * IPI_CPU_BACKTRACE is either implemented either as a normal IRQ or, + * if the hardware can supports it, using a pseudo-NMI. + * + * The mechanism used to implement pseudo-NMI means that in both cases + * testing if the backtrace IPI is disabled requires us to check the + * PSR I bit. However in the later case we cannot use irqs_disabled() + * to check the I bit because, when the pseudo-NMI is active that + * function examines the GIC PMR instead. + */ +static unsigned long nmi_disabled(void) +{ + unsigned long flags; + + asm volatile("mrs %0, daif" : "=r"(flags) :: "memory"); + return flags & PSR_I_BIT; +} + static void raise_nmi(cpumask_t *mask) { /* * Generate the backtrace directly if we are running in a * calling context that is not preemptible by the backtrace IPI. */ - if (cpumask_test_cpu(smp_processor_id(), mask) && irqs_disabled()) + if (cpumask_test_cpu(smp_processor_id(), mask) && nmi_disabled()) nmi_cpu_backtrace(NULL); smp_cross_call(mask, IPI_CPU_BACKTRACE); -- cgit v1.2.3