Re: [PATCH v3 1/5] irqchip/mips-gic: Fix unbalanced cm_core_lock in for_each_online_cpu_gic()

From: Thomas Bogendoerfer

Date: Mon Sep 28 2026 - 04:43:26 EST


On Mon, Sep 07, 2026 at 02:46:35PM +0200, Benoît Monin wrote:
> Commit d9e2ed610a60 ("irqchip/mips-gic: Support multi-cluster in
> for_each_online_cpu_gic()") added a gic_unlock_cluster() call to the
> macro's loop increment, which unconditionally invokes
> mips_cm_unlock_other() on multi-cluster systems. However nothing in the
> loop ever acquires the corresponding mips_cm_lock_other(), so on
> multi-cluster hardware every invocation of for_each_online_cpu_gic()
> releases an unheld per-CPU cm_core_lock.
>
> With CONFIG_PROVE_LOCKING this triggers a "bad unlock balance detected"
> warning at boot, e.g. from gic_irq_domain_map() while mapping local
> interrupts. Only the first occurrence is reported, since the first
> warning permanently disables lockdep (debug_locks = 0); the unbalanced
> release itself silently persists.
>
> Fix this by moving both the acquire and release into
> __gic_with_next_online_cpu() so they stay balanced. When advancing to a
> CPU in a remote cluster, lock the CM redirect block for that cluster via
> mips_cm_lock_other(); when leaving a remote cluster (or finishing the
> iteration) release it with mips_cm_unlock_other(). Local-cluster CPUs
> require no locking, so single-cluster systems are unaffected. This also
> makes the redirect region behave correctly when accessing local register
> blocks of CPUs in other clusters.
>
> Drop the now-unused gic_unlock_cluster() helper and its call from the
> for_each_online_cpu_gic() increment.
>
> Fixes: d9e2ed610a60 ("irqchip/mips-gic: Support multi-cluster in for_each_online_cpu_gic()")
> Signed-off-by: Benoît Monin <benoit.monin@xxxxxxxxxxx>
> ---
> drivers/irqchip/irq-mips-gic.c | 20 ++++++--------------
> 1 file changed, 6 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
> index 19a57c5e2b2e..3b31cbcbed6f 100644
> --- a/drivers/irqchip/irq-mips-gic.c
> +++ b/drivers/irqchip/irq-mips-gic.c
> @@ -70,6 +70,10 @@ static int __gic_with_next_online_cpu(int prev)
> {
> unsigned int cpu;
>
> + /* Release the redirect/other region lock to the previous CPU, if any. */
> + if (prev >= 0)
> + mips_cm_unlock_other();
> +
> /* Discover the next online CPU */
> cpu = cpumask_next(prev, cpu_online_mask);
>
> @@ -77,23 +81,12 @@ static int __gic_with_next_online_cpu(int prev)
> if (cpu >= nr_cpu_ids)
> return cpu;
>
> - /*
> - * Move the access lock to the next CPU's GIC local register block.
> - *
> - * Set GIC_VL_OTHER. Since the caller holds gic_lock nothing can
> - * clobber the written value.
> - */
> - write_gic_vl_other(mips_cm_vp_id(cpu));
> + /* Lock access to redirect/other region to the next CPU */
> + mips_cm_lock_other_cpu(cpu, CM_GCR_Cx_OTHER_BLOCK_LOCAL);
>
> return cpu;
> }
>
> -static inline void gic_unlock_cluster(void)
> -{
> - if (mips_cps_multicluster_cpus())
> - mips_cm_unlock_other();
> -}
> -
> /**
> * for_each_online_cpu_gic() - Iterate over online CPUs, access local registers
> * @cpu: An integer variable to hold the current CPU number
> @@ -108,7 +101,6 @@ static inline void gic_unlock_cluster(void)
> guard(raw_spinlock_irqsave)(gic_lock); \
> for ((cpu) = __gic_with_next_online_cpu(-1); \
> (cpu) < nr_cpu_ids; \
> - gic_unlock_cluster(), \
> (cpu) = __gic_with_next_online_cpu(cpu))
>
> /**
>
> --
> 2.55.0

Reviewed-by: Thomas Bogendoerfer <tsbogend@xxxxxxxxxxxxxxxx>

--
Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
good idea. [ RFC1925, 2.3 ]