[tip: irq/drivers] irqchip/mips-gic: Transfer interrupt mask state across clusters

From: tip-bot2 for Benoît Monin

Date: Thu Oct 15 2026 - 06:09:03 EST


The following commit has been merged into the irq/drivers branch of tip:

Commit-ID: a5f0dc656e89d0eb140547823a36212902cdf673
Gitweb: https://git.kernel.org/tip/a5f0dc656e89d0eb140547823a36212902cdf673
Author: Benoît Monin <benoit.monin@xxxxxxxxxxx>
AuthorDate: Tue, 29 Sep 2026 14:14:10 +02:00
Committer: Thomas Gleixner <tglx@xxxxxxxxxx>
CommitterDate: Thu, 15 Oct 2026 12:02:17 +02:00

irqchip/mips-gic: Transfer interrupt mask state across clusters

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 <benoit.monin@xxxxxxxxxxx>
Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxx>
Reviewed-by: Thomas Bogendoerfer <tsbogend@xxxxxxxxxxxxxxxx>
Link: https://patch.msgid.link/20260929-sync-gic-counters-v4-3-ec70c4b60434@xxxxxxxxxxx
---
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 f2ae60d..be38989 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)) {