Skip to content

Commit 331a1b3

Browse files
diandersctmarinas
authored andcommitted
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 8d539b8 ("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@FVFF77S0Q05N Co-developed-by: Sumit Garg <[email protected]> Signed-off-by: Sumit Garg <[email protected]> Co-developed-by: Mark Rutland <[email protected]> Signed-off-by: Mark Rutland <[email protected]> Reviewed-by: Stephen Boyd <[email protected]> Reviewed-by: Misono Tomohiro <[email protected]> Tested-by: Chen-Yu Tsai <[email protected]> Signed-off-by: Douglas Anderson <[email protected]> Link: https://lore.kernel.org/r/20230906090246.v13.4.Ie6c132b96ebbbcddbf6954b9469ed40a6960343c@changeid Signed-off-by: Catalin Marinas <[email protected]>
1 parent 2b2d0a7 commit 331a1b3

File tree

2 files changed

+78
-11
lines changed

2 files changed

+78
-11
lines changed

arch/arm64/include/asm/irq.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
#include <asm-generic/irq.h>
88

9+
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu);
10+
#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
11+
912
struct pt_regs;
1013

1114
int set_handle_irq(void (*handle_irq)(struct pt_regs *));

arch/arm64/kernel/smp.c

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <linux/kernel_stat.h>
3434
#include <linux/kexec.h>
3535
#include <linux/kvm_host.h>
36+
#include <linux/nmi.h>
3637

3738
#include <asm/alternative.h>
3839
#include <asm/atomic.h>
@@ -72,12 +73,18 @@ enum ipi_msg_type {
7273
IPI_CPU_CRASH_STOP,
7374
IPI_TIMER,
7475
IPI_IRQ_WORK,
75-
NR_IPI
76+
NR_IPI,
77+
/*
78+
* Any enum >= NR_IPI and < MAX_IPI is special and not tracable
79+
* with trace_ipi_*
80+
*/
81+
IPI_CPU_BACKTRACE = NR_IPI,
82+
MAX_IPI
7683
};
7784

7885
static int ipi_irq_base __read_mostly;
7986
static int nr_ipi __read_mostly = NR_IPI;
80-
static struct irq_desc *ipi_desc[NR_IPI] __read_mostly;
87+
static struct irq_desc *ipi_desc[MAX_IPI] __read_mostly;
8188

8289
static void ipi_setup(int cpu);
8390

@@ -845,6 +852,22 @@ static void __noreturn ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs
845852
#endif
846853
}
847854

855+
static void arm64_backtrace_ipi(cpumask_t *mask)
856+
{
857+
__ipi_send_mask(ipi_desc[IPI_CPU_BACKTRACE], mask);
858+
}
859+
860+
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
861+
{
862+
/*
863+
* NOTE: though nmi_trigger_cpumask_backtrace() has "nmi_" in the name,
864+
* nothing about it truly needs to be implemented using an NMI, it's
865+
* just that it's _allowed_ to work with NMIs. If ipi_should_be_nmi()
866+
* returned false our backtrace attempt will just use a regular IPI.
867+
*/
868+
nmi_trigger_cpumask_backtrace(mask, exclude_cpu, arm64_backtrace_ipi);
869+
}
870+
848871
/*
849872
* Main handler for inter-processor interrupts
850873
*/
@@ -888,6 +911,14 @@ static void do_handle_IPI(int ipinr)
888911
break;
889912
#endif
890913

914+
case IPI_CPU_BACKTRACE:
915+
/*
916+
* NOTE: in some cases this _won't_ be NMI context. See the
917+
* comment in arch_trigger_cpumask_backtrace().
918+
*/
919+
nmi_cpu_backtrace(get_irq_regs());
920+
break;
921+
891922
default:
892923
pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
893924
break;
@@ -909,15 +940,34 @@ static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
909940
__ipi_send_mask(ipi_desc[ipinr], target);
910941
}
911942

943+
static bool ipi_should_be_nmi(enum ipi_msg_type ipi)
944+
{
945+
if (!system_uses_irq_prio_masking())
946+
return false;
947+
948+
switch (ipi) {
949+
case IPI_CPU_BACKTRACE:
950+
return true;
951+
default:
952+
return false;
953+
}
954+
}
955+
912956
static void ipi_setup(int cpu)
913957
{
914958
int i;
915959

916960
if (WARN_ON_ONCE(!ipi_irq_base))
917961
return;
918962

919-
for (i = 0; i < nr_ipi; i++)
920-
enable_percpu_irq(ipi_irq_base + i, 0);
963+
for (i = 0; i < nr_ipi; i++) {
964+
if (ipi_should_be_nmi(i)) {
965+
prepare_percpu_nmi(ipi_irq_base + i);
966+
enable_percpu_nmi(ipi_irq_base + i, 0);
967+
} else {
968+
enable_percpu_irq(ipi_irq_base + i, 0);
969+
}
970+
}
921971
}
922972

923973
#ifdef CONFIG_HOTPLUG_CPU
@@ -928,24 +978,38 @@ static void ipi_teardown(int cpu)
928978
if (WARN_ON_ONCE(!ipi_irq_base))
929979
return;
930980

931-
for (i = 0; i < nr_ipi; i++)
932-
disable_percpu_irq(ipi_irq_base + i);
981+
for (i = 0; i < nr_ipi; i++) {
982+
if (ipi_should_be_nmi(i)) {
983+
disable_percpu_nmi(ipi_irq_base + i);
984+
teardown_percpu_nmi(ipi_irq_base + i);
985+
} else {
986+
disable_percpu_irq(ipi_irq_base + i);
987+
}
988+
}
933989
}
934990
#endif
935991

936992
void __init set_smp_ipi_range(int ipi_base, int n)
937993
{
938994
int i;
939995

940-
WARN_ON(n < NR_IPI);
941-
nr_ipi = min(n, NR_IPI);
996+
WARN_ON(n < MAX_IPI);
997+
nr_ipi = min(n, MAX_IPI);
942998

943999
for (i = 0; i < nr_ipi; i++) {
9441000
int err;
9451001

946-
err = request_percpu_irq(ipi_base + i, ipi_handler,
947-
"IPI", &cpu_number);
948-
WARN_ON(err);
1002+
if (ipi_should_be_nmi(i)) {
1003+
err = request_percpu_nmi(ipi_base + i, ipi_handler,
1004+
"IPI", &cpu_number);
1005+
WARN(err, "Could not request IPI %d as NMI, err=%d\n",
1006+
i, err);
1007+
} else {
1008+
err = request_percpu_irq(ipi_base + i, ipi_handler,
1009+
"IPI", &cpu_number);
1010+
WARN(err, "Could not request IPI %d as IRQ, err=%d\n",
1011+
i, err);
1012+
}
9491013

9501014
ipi_desc[i] = irq_to_desc(ipi_base + i);
9511015
irq_set_status_flags(ipi_base + i, IRQ_HIDDEN);

0 commit comments

Comments
 (0)