Commit 331a1b3a authored by Douglas Anderson's avatar Douglas Anderson Committed by Catalin Marinas

arm64: smp: Add arch support for backtrace using pseudo-NMI

Enable arch_trigger_cpumask_backtrace() support on arm64. This enables
things much like they are enabled on arm32 (including some of the
funky logic around NR_IPI, nr_ipi, and MAX_IPI) but with the
difference that, unlike arm32, we'll try to enable the backtrace to
use pseudo-NMI.

NOTE: this patch is a squash of the little bit of code adding the
ability to mark an IPI to try to use pseudo-NMI plus the little bit of
code to hook things up for kgdb. This approach was decided upon in the
discussion of v9 [1].

This patch depends on commit 8d539b84 ("nmi_backtrace: allow
excluding an arbitrary CPU") since that commit changed the prototype
of arch_trigger_cpumask_backtrace(), which this patch implements.

[1] https://lore.kernel.org/r/ZORY51mF4alI41G1@FVFF77S0Q05NCo-developed-by: default avatarSumit Garg <sumit.garg@linaro.org>
Signed-off-by: default avatarSumit Garg <sumit.garg@linaro.org>
Co-developed-by: default avatarMark Rutland <mark.rutland@arm.com>
Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
Reviewed-by: default avatarStephen Boyd <swboyd@chromium.org>
Reviewed-by: default avatarMisono Tomohiro <misono.tomohiro@fujitsu.com>
Tested-by: default avatarChen-Yu Tsai <wenst@chromium.org>
Signed-off-by: default avatarDouglas Anderson <dianders@chromium.org>
Link: https://lore.kernel.org/r/20230906090246.v13.4.Ie6c132b96ebbbcddbf6954b9469ed40a6960343c@changeidSigned-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 2b2d0a7a
...@@ -6,6 +6,9 @@ ...@@ -6,6 +6,9 @@
#include <asm-generic/irq.h> #include <asm-generic/irq.h>
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu);
#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
struct pt_regs; struct pt_regs;
int set_handle_irq(void (*handle_irq)(struct pt_regs *)); int set_handle_irq(void (*handle_irq)(struct pt_regs *));
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/kexec.h> #include <linux/kexec.h>
#include <linux/kvm_host.h> #include <linux/kvm_host.h>
#include <linux/nmi.h>
#include <asm/alternative.h> #include <asm/alternative.h>
#include <asm/atomic.h> #include <asm/atomic.h>
...@@ -72,12 +73,18 @@ enum ipi_msg_type { ...@@ -72,12 +73,18 @@ enum ipi_msg_type {
IPI_CPU_CRASH_STOP, IPI_CPU_CRASH_STOP,
IPI_TIMER, IPI_TIMER,
IPI_IRQ_WORK, IPI_IRQ_WORK,
NR_IPI NR_IPI,
/*
* Any enum >= NR_IPI and < MAX_IPI is special and not tracable
* with trace_ipi_*
*/
IPI_CPU_BACKTRACE = NR_IPI,
MAX_IPI
}; };
static int ipi_irq_base __read_mostly; static int ipi_irq_base __read_mostly;
static int nr_ipi __read_mostly = NR_IPI; static int nr_ipi __read_mostly = NR_IPI;
static struct irq_desc *ipi_desc[NR_IPI] __read_mostly; static struct irq_desc *ipi_desc[MAX_IPI] __read_mostly;
static void ipi_setup(int cpu); static void ipi_setup(int cpu);
...@@ -845,6 +852,22 @@ static void __noreturn ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs ...@@ -845,6 +852,22 @@ static void __noreturn ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs
#endif #endif
} }
static void arm64_backtrace_ipi(cpumask_t *mask)
{
__ipi_send_mask(ipi_desc[IPI_CPU_BACKTRACE], mask);
}
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
{
/*
* NOTE: though nmi_trigger_cpumask_backtrace() has "nmi_" in the name,
* nothing about it truly needs to be implemented using an NMI, it's
* just that it's _allowed_ to work with NMIs. If ipi_should_be_nmi()
* returned false our backtrace attempt will just use a regular IPI.
*/
nmi_trigger_cpumask_backtrace(mask, exclude_cpu, arm64_backtrace_ipi);
}
/* /*
* Main handler for inter-processor interrupts * Main handler for inter-processor interrupts
*/ */
...@@ -888,6 +911,14 @@ static void do_handle_IPI(int ipinr) ...@@ -888,6 +911,14 @@ static void do_handle_IPI(int ipinr)
break; break;
#endif #endif
case IPI_CPU_BACKTRACE:
/*
* NOTE: in some cases this _won't_ be NMI context. See the
* comment in arch_trigger_cpumask_backtrace().
*/
nmi_cpu_backtrace(get_irq_regs());
break;
default: default:
pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
break; break;
...@@ -909,6 +940,19 @@ static void smp_cross_call(const struct cpumask *target, unsigned int ipinr) ...@@ -909,6 +940,19 @@ static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
__ipi_send_mask(ipi_desc[ipinr], target); __ipi_send_mask(ipi_desc[ipinr], target);
} }
static bool ipi_should_be_nmi(enum ipi_msg_type ipi)
{
if (!system_uses_irq_prio_masking())
return false;
switch (ipi) {
case IPI_CPU_BACKTRACE:
return true;
default:
return false;
}
}
static void ipi_setup(int cpu) static void ipi_setup(int cpu)
{ {
int i; int i;
...@@ -916,8 +960,14 @@ static void ipi_setup(int cpu) ...@@ -916,8 +960,14 @@ static void ipi_setup(int cpu)
if (WARN_ON_ONCE(!ipi_irq_base)) if (WARN_ON_ONCE(!ipi_irq_base))
return; return;
for (i = 0; i < nr_ipi; i++) for (i = 0; i < nr_ipi; i++) {
if (ipi_should_be_nmi(i)) {
prepare_percpu_nmi(ipi_irq_base + i);
enable_percpu_nmi(ipi_irq_base + i, 0);
} else {
enable_percpu_irq(ipi_irq_base + i, 0); enable_percpu_irq(ipi_irq_base + i, 0);
}
}
} }
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
...@@ -928,8 +978,14 @@ static void ipi_teardown(int cpu) ...@@ -928,8 +978,14 @@ static void ipi_teardown(int cpu)
if (WARN_ON_ONCE(!ipi_irq_base)) if (WARN_ON_ONCE(!ipi_irq_base))
return; return;
for (i = 0; i < nr_ipi; i++) for (i = 0; i < nr_ipi; i++) {
if (ipi_should_be_nmi(i)) {
disable_percpu_nmi(ipi_irq_base + i);
teardown_percpu_nmi(ipi_irq_base + i);
} else {
disable_percpu_irq(ipi_irq_base + i); disable_percpu_irq(ipi_irq_base + i);
}
}
} }
#endif #endif
...@@ -937,15 +993,23 @@ void __init set_smp_ipi_range(int ipi_base, int n) ...@@ -937,15 +993,23 @@ void __init set_smp_ipi_range(int ipi_base, int n)
{ {
int i; int i;
WARN_ON(n < NR_IPI); WARN_ON(n < MAX_IPI);
nr_ipi = min(n, NR_IPI); nr_ipi = min(n, MAX_IPI);
for (i = 0; i < nr_ipi; i++) { for (i = 0; i < nr_ipi; i++) {
int err; int err;
if (ipi_should_be_nmi(i)) {
err = request_percpu_nmi(ipi_base + i, ipi_handler,
"IPI", &cpu_number);
WARN(err, "Could not request IPI %d as NMI, err=%d\n",
i, err);
} else {
err = request_percpu_irq(ipi_base + i, ipi_handler, err = request_percpu_irq(ipi_base + i, ipi_handler,
"IPI", &cpu_number); "IPI", &cpu_number);
WARN_ON(err); WARN(err, "Could not request IPI %d as IRQ, err=%d\n",
i, err);
}
ipi_desc[i] = irq_to_desc(ipi_base + i); ipi_desc[i] = irq_to_desc(ipi_base + i);
irq_set_status_flags(ipi_base + i, IRQ_HIDDEN); irq_set_status_flags(ipi_base + i, IRQ_HIDDEN);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment