From a5f0dc656e89d0eb140547823a36212902cdf673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Monin?= Date: Tue, 29 Sep 2026 14:14:10 +0200 Subject: [PATCH] irqchip/mips-gic: Transfer interrupt mask state across clusters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an interrupt's affinity is moved to a CPU in another cluster, gic_set_affinity() updates the routing (GIC_SH_MAP_VP) and trigger type in the destination cluster, but never touched the interrupt's mask state. The interrupt mask is per-cluster. After such a move the interrupt may be left disabled in the destination cluster, so it never fires despite being correctly routed to its new VP. Move the mask state along with the interrupt: disable it in the old cluster while clearing the route so it is no longer delivered, then configure the trigger type in the new cluster and re-enable it there if it was enabled in the old cluster. Fixes: 322a90638768 ("irqchip/mips-gic: Multi-cluster support") Signed-off-by: BenoƮt Monin Signed-off-by: Thomas Gleixner Reviewed-by: Thomas Bogendoerfer Link: https://patch.msgid.link/20260929-sync-gic-counters-v4-3-ec70c4b60434@bootlin.com --- drivers/irqchip/irq-mips-gic.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index f2ae60d39d66..be38989d8e73 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -368,6 +368,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq); unsigned int cpu, cl, old_cpu, old_cl; unsigned long flags; + bool enabled; /* * The GIC specifies that we can only route an interrupt to one VP(E), @@ -389,15 +390,20 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, raw_spin_lock_irqsave(&gic_lock, flags); /* - * If we're moving affinity between clusters, stop routing the - * interrupt to any VP(E) in the old cluster. + * If we're moving affinity between clusters, save the interrupt's + * mask state, stop routing it to any VP(E) in the old cluster and + * disable it there so it is no longer delivered. */ if (cl != old_cl) { if (gic_irq_lock_cluster(d)) { + enabled = read_gic_redir_mask(irq); write_gic_redir_map_vp(irq, 0); + write_gic_redir_rmask(irq); mips_cm_unlock_other(); } else { + enabled = read_gic_mask(irq); write_gic_map_vp(irq, 0); + write_gic_rmask(irq); } } @@ -409,10 +415,20 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, /* * If we're moving affinity between clusters, configure the interrupt - * trigger type in the new cluster. + * trigger type and, if it was enabled in the old cluster, enable it + * in the new one. */ - if (cl != old_cl) + if (cl != old_cl) { gic_set_type_locked(d, irqd_get_trigger_type(d)); + if (enabled) { + if (gic_irq_lock_cluster(d)) { + write_gic_redir_smask(irq); + mips_cm_unlock_other(); + } else { + write_gic_smask(irq); + } + } + } /* Route the interrupt to its new VP(E) */ if (gic_irq_lock_cluster(d)) {