From: Markus Stockhausen markus.stockhausen@gmx.de
[ Upstream commit 2250db8628a0d8293ad2e0671138b848a185fba1 ]
Devices of the Realtek MIPS Otto platform use the official rtl-otto-timer as clock event generator and CPU clocksource. It is registered for each CPU startup via cpuhp_setup_state() and forces the affinity of the clockevent interrupts to the appropriate CPU via irq_force_affinity().
On the "smaller" devices with a vendor specific interrupt controller (supported by irq-realtek-rtl) the registration works fine. The "larger" RTL931x series is based on a MIPS interAptiv dual core with a MIPS GIC controller. Interrupt routing setup is cancelled because gic_set_affinity() does not accept the current (not yet online) CPU as a target.
Relax the checks by evaluating the force parameter that is provided for exactly this purpose like in other drivers. With this the affinity can be set as follows:
- force = false: allow to set affinity to any online cpu - force = true: allow to set affinity to any cpu
Co-developed-by: Sebastian Gottschall s.gottschall@dd-wrt.com Signed-off-by: Sebastian Gottschall s.gottschall@dd-wrt.com Signed-off-by: Markus Stockhausen markus.stockhausen@gmx.de Signed-off-by: Thomas Gleixner tglx@linutronix.de Link: https://lore.kernel.org/all/20250621054952.380374-1-markus.stockhausen@gmx.d... Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
Based on my analysis of the commit, here is my assessment:
**Backport Status: YES**
This commit should be backported to stable kernel trees for the following reasons:
1. **Fixes a real bug affecting users**: The commit fixes a specific issue where clockevent interrupt affinity setup fails on Realtek RTL931x devices with MIPS GIC controllers. Without this fix, the timer registration during CPU hotplug fails because `gic_set_affinity()` rejects setting affinity to CPUs that are not yet online.
2. **Small and contained fix**: The change is minimal - it only adds 4 lines of code to properly handle the `force` parameter that was already being passed but ignored. The logic change is straightforward: - When `force=false`: Use existing behavior (only allow online CPUs) - When `force=true`: Allow any CPU in the mask
3. **Follows established patterns**: The commit shows that multiple other timer drivers (`timer-rtl-otto.c`, `timer-tegra.c`, `exynos_mct.c`, etc.) use `irq_force_affinity()` during CPU hotplug, which eventually calls the chip's `set_affinity` handler with `force=true`. The MIPS GIC driver was incorrectly ignoring this parameter.
4. **No architectural changes**: This is purely a bug fix that makes the MIPS GIC driver honor the existing `force` parameter semantics. It doesn't introduce new features or change any architecture.
5. **Clear use case**: The commit message clearly explains that this affects real hardware (Realtek RTL931x series) and prevents proper timer initialization during CPU hotplug, which is a fundamental kernel operation.
6. **Low regression risk**: The change only affects behavior when `force=true` is explicitly passed, which is specifically for CPU hotplug scenarios. Normal interrupt affinity operations (with `force=false`) remain unchanged.
7. **Aligns with kernel conventions**: The kernel documentation for `irq_force_affinity()` explicitly states it's "Solely for low level cpu hotplug code, where we need to make per cpu interrupts affine before the cpu becomes online." This commit makes the MIPS GIC driver comply with this intended behavior.
The fix is essential for proper operation of affected hardware and follows the stable kernel criteria of being a important bugfix with minimal risk.
drivers/irqchip/irq-mips-gic.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index 34e8d09c12a0..19a57c5e2b2e 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -375,9 +375,13 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, /* * The GIC specifies that we can only route an interrupt to one VP(E), * ie. CPU in Linux parlance, at a time. Therefore we always route to - * the first online CPU in the mask. + * the first forced or online CPU in the mask. */ - cpu = cpumask_first_and(cpumask, cpu_online_mask); + if (force) + cpu = cpumask_first(cpumask); + else + cpu = cpumask_first_and(cpumask, cpu_online_mask); + if (cpu >= NR_CPUS) return -EINVAL;