public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
From: Sasha Levin <sashal@kernel.org>
To: patches@lists.linux.dev, stable@vger.kernel.org
Cc: Nick Hu <nick.hu@sifive.com>,
	Thomas Gleixner <tglx@linutronix.de>,
	Yong-Xuan Wang <yongxuan.wang@sifive.com>,
	Cyan Yang <cyan.yang@sifive.com>,
	Anup Patel <anup@brainfault.org>,
	Nutty Liu <liujingqi@lanxincomputing.com>,
	Sasha Levin <sashal@kernel.org>,
	tglx@kernel.org, pjw@kernel.org, palmer@dabbelt.com,
	aou@eecs.berkeley.edu, linux-riscv@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH AUTOSEL 6.19-6.18] irqchip/riscv-imsic: Add a CPU pm notifier to restore the IMSIC on exit
Date: Wed, 11 Feb 2026 20:09:44 -0500	[thread overview]
Message-ID: <20260212010955.3480391-21-sashal@kernel.org> (raw)
In-Reply-To: <20260212010955.3480391-1-sashal@kernel.org>

From: Nick Hu <nick.hu@sifive.com>

[ Upstream commit f48b4bd0915bf61ac12b8c65c7939ebd03bc8abf ]

The IMSIC might be reset when the system enters a low power state, but on
exit nothing restores the registers, which prevents interrupt delivery.

Solve this by registering a CPU power management notifier, which restores
the IMSIC on exit.

Signed-off-by: Nick Hu <nick.hu@sifive.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Yong-Xuan Wang <yongxuan.wang@sifive.com>
Reviewed-by: Cyan Yang <cyan.yang@sifive.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Reviewed-by: Nutty Liu <liujingqi@lanxincomputing.com>
Link: https://patch.msgid.link/20251202-preserve-aplic-imsic-v3-1-1844fbf1fe92@sifive.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

Now I have a comprehensive understanding. Let me summarize the analysis.

---

## Detailed Analysis

### 1. Commit Message Analysis

The commit message is clear and directly describes a **bug fix**: "The
IMSIC might be reset when the system enters a low power state, but on
exit nothing restores the registers, which prevents interrupt delivery."
This is unambiguous — without the fix, **interrupt delivery is broken**
after returning from a low power state.

The patch is from Nick Hu at SiFive (a major RISC-V silicon vendor),
signed-off by Thomas Gleixner (the overall irqchip maintainer), and
reviewed by:
- Anup Patel (original IMSIC driver author/maintainer at Ventana Micro)
- Yong-Xuan Wang, Cyan Yang (SiFive reviewers)
- Nutty Liu (Lanxin Computing)

This level of review by domain experts demonstrates the fix is well-
vetted.

### 2. Code Change Analysis

The change is minimal (31 lines added, 8 removed, single file) and
consists of three logical parts:

**a) Refactoring — extracting `imsic_hw_states_init()`:**

The existing code from `imsic_starting_cpu()` that handles hardware
register initialization is moved into a new helper:

```126:147:drivers/irqchip/irq-riscv-imsic-early.c
static int imsic_starting_cpu(unsigned int cpu)
{
        /* Mark per-CPU IMSIC state as online */
        imsic_state_online();

        /* Enable per-CPU parent interrupt */
        enable_percpu_irq(imsic_parent_irq,
irq_get_trigger_type(imsic_parent_irq));

        /* Setup IPIs */
        imsic_ipi_starting_cpu();

        /*
  - Interrupts identities might have been enabled/disabled while
  - this CPU was not running so sync-up local enable/disable state.
         */
        imsic_local_sync_all(true);

        /* Enable local interrupt delivery */
        imsic_local_delivery(true);

        return 0;
}
```

The three operations (`imsic_ipi_starting_cpu()`,
`imsic_local_sync_all(true)`, `imsic_local_delivery(true)`) are
extracted into `imsic_hw_states_init()`, which is then called from both
`imsic_starting_cpu()` and the new PM notifier.

**b) Adding the CPU PM notifier:**

A new `imsic_pm_notifier` function handles `CPU_PM_EXIT` by calling
`imsic_hw_states_init()`. This is the **exact same pattern** used by
GICv3 (`irq-gic-v3.c:1482`) and GIC (`irq-gic.c`), which have been
stable for years.

**c) Registering the notifier:**

The `imsic_early_probe()` return is changed from `return 0` to `return
cpu_pm_register_notifier(&imsic_pm_notifier_block)`.

### 3. Bug Mechanism and Severity

**The bug**: On RISC-V systems with SBI-based cpuidle (the standard CPU
idle mechanism), when a CPU enters a deep idle state, the SBI firmware
may power down the IMSIC. The `cpuidle-riscv-sbi.c` driver calls
`cpu_pm_enter()` before and `cpu_pm_exit()` after the idle transition.
`cpu_pm_exit()` fires `CPU_PM_EXIT` notifications to all registered
handlers. Without this patch, no handler exists for IMSIC, so:

1. `imsic_local_delivery` — the EIDELIVERY/EITHRESHOLD CSRs may be
   reset, disabling all interrupt delivery
2. `imsic_local_sync_all` — individual interrupt enable bits may be
   reset, leaving all interrupt sources disabled
3. `imsic_ipi_starting_cpu` — the IPI enable bit may be reset, breaking
   inter-processor interrupts

**Impact**: The CPU effectively becomes deaf to all interrupts — IPIs,
device interrupts, timer interrupts routed through IMSIC — after
returning from a deep idle state. This can cause:
- System hangs (no timer interrupts)
- IPI failures (scheduler hangs, RCU stalls)
- Device interrupt loss (I/O failures, network timeouts)

This bug affects any RISC-V system using IMSIC with cpuidle deep states
— which includes SiFive boards and other production RISC-V hardware.

### 4. Dependency Check

The patch is **self-contained**. It depends only on:
- `imsic_ipi_starting_cpu()` — exists in all stable trees with IMSIC
  (6.12.y+)
- `imsic_local_sync_all()` — exists in all stable trees with IMSIC
  (6.12.y+)
- `imsic_local_delivery()` — exists in all stable trees with IMSIC
  (6.12.y+)
- `cpu_pm_register_notifier()` — exists in all kernel versions (core
  kernel PM API)
- `cpuidle-riscv-sbi.c` — exists in all stable trees with IMSIC, and
  calls `cpu_pm_enter()`/`cpu_pm_exit()`

The companion APLIC patch (95a8ddde36601) touches a different driver
entirely and is not a dependency.

### 5. Backport Applicability

- **IMSIC driver exists**: 6.12.y through 6.19.y (added in v6.10)
- **Clean backport**: The `imsic_starting_cpu` function is byte-
  identical between the mainline pre-image and 6.12.y. Only minor
  context conflict in the `#include` section (6.12.y lacks
  `<linux/export.h>` that mainline has), trivially resolved.
- **Bug triggerable**: The cpuidle-riscv-sbi driver in 6.12.y already
  calls `cpu_pm_enter()`/`cpu_pm_exit()`, meaning the IMSIC registers
  get reset with no restoration.

### 6. Risk Assessment

**Very low risk**:
- The refactored code (`imsic_hw_states_init`) performs exactly the same
  operations as before — just from an additional call site
- The GICv3 driver has used this exact pattern (CPU PM notifier
  restoring interrupt controller state) for over a decade
- No behavior changes to the existing CPU hotplug path
- Well-reviewed by all key stakeholders

### 7. Conclusion

This is a clear-cut bug fix for a critical issue: **complete loss of
interrupt delivery** after RISC-V CPUs return from deep idle states. The
fix is:
- Small and surgical (31 lines added to a single file)
- Based on a well-established pattern (identical to GICv3/GIC CPU PM
  handling)
- Thoroughly reviewed by domain experts
- Self-contained with no dependencies on other patches
- Cleanly backportable to all stable trees containing IMSIC (6.12.y+)

**YES**

 drivers/irqchip/irq-riscv-imsic-early.c | 39 ++++++++++++++++++++-----
 1 file changed, 31 insertions(+), 8 deletions(-)

diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c
index 6bac67cc0b6d9..ba903fa689bd5 100644
--- a/drivers/irqchip/irq-riscv-imsic-early.c
+++ b/drivers/irqchip/irq-riscv-imsic-early.c
@@ -7,6 +7,7 @@
 #define pr_fmt(fmt) "riscv-imsic: " fmt
 #include <linux/acpi.h>
 #include <linux/cpu.h>
+#include <linux/cpu_pm.h>
 #include <linux/export.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
@@ -123,14 +124,8 @@ static void imsic_handle_irq(struct irq_desc *desc)
 	chained_irq_exit(chip, desc);
 }
 
-static int imsic_starting_cpu(unsigned int cpu)
+static void imsic_hw_states_init(void)
 {
-	/* Mark per-CPU IMSIC state as online */
-	imsic_state_online();
-
-	/* Enable per-CPU parent interrupt */
-	enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq));
-
 	/* Setup IPIs */
 	imsic_ipi_starting_cpu();
 
@@ -142,6 +137,18 @@ static int imsic_starting_cpu(unsigned int cpu)
 
 	/* Enable local interrupt delivery */
 	imsic_local_delivery(true);
+}
+
+static int imsic_starting_cpu(unsigned int cpu)
+{
+	/* Mark per-CPU IMSIC state as online */
+	imsic_state_online();
+
+	/* Enable per-CPU parent interrupt */
+	enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq));
+
+	/* Initialize the IMSIC registers to enable the interrupt delivery */
+	imsic_hw_states_init();
 
 	return 0;
 }
@@ -157,6 +164,22 @@ static int imsic_dying_cpu(unsigned int cpu)
 	return 0;
 }
 
+static int imsic_pm_notifier(struct notifier_block *self, unsigned long cmd, void *v)
+{
+	switch (cmd) {
+	case CPU_PM_EXIT:
+		/* Initialize the IMSIC registers to enable the interrupt delivery */
+		imsic_hw_states_init();
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block imsic_pm_notifier_block = {
+	.notifier_call = imsic_pm_notifier,
+};
+
 static int __init imsic_early_probe(struct fwnode_handle *fwnode)
 {
 	struct irq_domain *domain;
@@ -194,7 +217,7 @@ static int __init imsic_early_probe(struct fwnode_handle *fwnode)
 	cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_IMSIC_STARTING, "irqchip/riscv/imsic:starting",
 			  imsic_starting_cpu, imsic_dying_cpu);
 
-	return 0;
+	return cpu_pm_register_notifier(&imsic_pm_notifier_block);
 }
 
 static int __init imsic_early_dt_init(struct device_node *node, struct device_node *parent)
-- 
2.51.0


  parent reply	other threads:[~2026-02-12  1:10 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-12  1:09 [PATCH AUTOSEL 6.19-5.10] clocksource/drivers/sh_tmu: Always leave device running after probe Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.18] gendwarfksyms: Fix build on 32-bit hosts Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.18] arm64/ftrace,bpf: Fix partial regs after bpf_prog_run Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.18] bpftool: Fix dependencies for static build Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.12] perf/x86/msr: Add Airmont NP Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.18] genirq/cpuhotplug: Notify about affinity changes breaking the affinity mask Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-5.15] char: tpm: cr50: Remove IRQF_ONESHOT Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.6] crypto: hisilicon/qm - move the barrier before writing to the mailbox register Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.12] sched/debug: Fix updating of ppos on server write ops Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.18] perf/x86/intel: Add Airmont NP Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.18] bpf: Properly mark live registers for indirect jumps Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-5.10] mailbox: bcm-ferxrm-mailbox: Use default primary handler Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.18] perf/core: Fix slow perf_event_task_exit() with LBR callstacks Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.12] perf/x86/cstate: Add Airmont NP Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-5.10] clocksource/drivers/timer-integrator-ap: Add missing Kconfig dependency on OF Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-5.10] bpf: verifier improvement in 32bit shift sign extension pattern Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.12] bpf: Recognize special arithmetic shift in the verifier Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.12] bpf: crypto: Use the correct destructor kfunc type Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-5.10] pstore: ram_core: fix incorrect success return when vmap() fails Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.18] bpf: net_sched: Use the correct destructor kfunc type Sasha Levin
2026-02-12  1:09 ` Sasha Levin [this message]
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.1] PCI/MSI: Unmap MSI-X region on error Sasha Levin
2026-02-12  1:09 ` [PATCH AUTOSEL 6.19-6.18] rust: sync: Implement Unpin for ARef Sasha Levin
2026-02-12 12:11   ` Miguel Ojeda
2026-02-26 13:45     ` Sasha Levin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260212010955.3480391-21-sashal@kernel.org \
    --to=sashal@kernel.org \
    --cc=anup@brainfault.org \
    --cc=aou@eecs.berkeley.edu \
    --cc=cyan.yang@sifive.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=liujingqi@lanxincomputing.com \
    --cc=nick.hu@sifive.com \
    --cc=palmer@dabbelt.com \
    --cc=patches@lists.linux.dev \
    --cc=pjw@kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=tglx@kernel.org \
    --cc=tglx@linutronix.de \
    --cc=yongxuan.wang@sifive.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox