aboutsummaryrefslogtreecommitdiff
path: root/arch/arm64/kernel/smp.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm64/kernel/smp.c')
-rw-r--r--arch/arm64/kernel/smp.c32
1 files changed, 27 insertions, 5 deletions
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);