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: 322a906387 ("irqchip/mips-gic: Multi-cluster support")
Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Reviewed-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Link: https://patch.msgid.link/20260929-sync-gic-counters-v4-3-ec70c4b60434@bootlin.com
This commit is contained in:
Benoît Monin 2026-09-29 14:14:10 +02:00 committed by Thomas Gleixner
parent 1bae25dcc6
commit a5f0dc656e

View File

@ -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)) {