[PATCH 1/2] arm64: smp: Fix pseudo NMI issues w/ broken Mediatek FW

Douglas Anderson
2023-10-02T16:46:51Z

Some mediatek devices have the property
"mediatek,broken-save-restore-fw" in their GIC. This means that,
although the hardware supports pseudo-NMI, the firmware has a bug
that blocks enabling it. When we're in this state,
system_uses_irq_prio_masking() will return true but we'll fail to
actually enable the IRQ in the GIC.

Let's make the code handle this. We'll detect that we failed to
request an IPI as NMI and fallback to requesting it normally. Though
we expect that either all of our requests will fail or all will
succeed, it's just as cheap to keep a per-IPI bitmap and that keeps us
robust.

Fixes: 331a1b3a836c ("arm64: smp: Add arch support for backtrace using pseudo-NMI")
Reported-by: Chen-Yu Tsai <wenst@chromium.org>
Closes: https://issuetracker.google.com/issues/197061987#comment68
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---

arch/arm64/kernel/smp.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 814d9aa93b21..0a6002243a8c 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -87,6 +87,7 @@ enum ipi_msg_type {
static int ipi_irq_base __ro_after_init;
static int nr_ipi __ro_after_init = NR_IPI;
static struct irq_desc *ipi_desc[MAX_IPI] __ro_after_init;
+DECLARE_BITMAP(ipi_is_nmi, MAX_IPI);

static void ipi_setup(int cpu);

@@ -986,7 +987,7 @@ static void ipi_setup(int cpu)
return;

for (i = 0; i < nr_ipi; i++) {
- if (ipi_should_be_nmi(i)) {
+ if (test_bit(i, ipi_is_nmi)) {
prepare_percpu_nmi(ipi_irq_base + i);
enable_percpu_nmi(ipi_irq_base + i, 0);
} else {
@@ -1004,7 +1005,7 @@ static void ipi_teardown(int cpu)
return;

for (i = 0; i < nr_ipi; i++) {
- if (ipi_should_be_nmi(i)) {
+ if (test_bit(i, ipi_is_nmi)) {
disable_percpu_nmi(ipi_irq_base + i);
teardown_percpu_nmi(ipi_irq_base + i);
} else {
@@ -1022,17 +1023,21 @@ void __init set_smp_ipi_range(int ipi_base, int n)
nr_ipi = min(n, MAX_IPI);

for (i = 0; i < nr_ipi; i++) {
- int err;
+ int err = -EINVAL;

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 {
+ if (err)
+ pr_info_once("NMI unavailable; fallback to IRQ\n");
+ else
+ set_bit(i, ipi_is_nmi);
+ }
+
+ if (err) {
err = request_percpu_irq(ipi_base + i, ipi_handler,
"IPI", &cpu_number);
- WARN(err, "Could not request IPI %d as IRQ, err=%d\n",
+ WARN(err, "Could not request IPI %d, err=%d\n",
i, err);
}

--
2.42.0.582.g8ccd20d70d-goog