Re: [PATCH 04/16] irqchip/crossbar: fix allocation and init cleanup

From: Radu Rendec

Date: Sun Aug 02 2026 - 11:22:31 EST


On Tue, 2026-07-14 at 21:24 +0800, Haofeng Li wrote:
> From: Haofeng Li <lihaofeng@xxxxxxxxxx>
>
> crossbar_domain_alloc() returns in the middle of a multi-IRQ
> allocation without freeing IRQs and crossbar slots allocated by
> earlier iterations.
>
> If irq_domain_create_hierarchy() fails after crossbar_of_init(), the
> global crossbar object and its maps and MMIO mapping are also leaked.
>
> Roll back prior IRQs through crossbar_domain_free(), and release all
> crossbar resources when domain creation fails.
>
> Fixes: 783d31863fb8 ("irqchip: crossbar: Convert dra7 crossbar to stacked domains")
>
> Signed-off-by: Haofeng Li <lihaofeng@xxxxxxxxxx>
> ---
>  drivers/irqchip/irq-crossbar.c | 23 +++++++++++++++++++++--
>  1 file changed, 21 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
> index 033b08782119..0ac5268aa8e4 100644
> --- a/drivers/irqchip/irq-crossbar.c
> +++ b/drivers/irqchip/irq-crossbar.c
> @@ -42,6 +42,9 @@ struct crossbar_device {
>  
>  static struct crossbar_device *cb;
>  
> +static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq,
> + unsigned int nr_irqs);
> +
>  static void crossbar_writel(int irq_no, int cb_no)
>  {
>   writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
> @@ -127,11 +130,14 @@ static int crossbar_domain_alloc(struct irq_domain *d, unsigned int virq,
>   for (i = 0; i < nr_irqs; i++) {
>   int err = allocate_gic_irq(d, virq + i, hwirq + i);
>  
> - if (err)
> + if (err) {
> + if (i)

I wouldn't bother checking if i != 0. This is a corner case, and it's
handled well, both inside crossbar_domain_free() itself and in
irq_domain_free_irqs_parent().

> + crossbar_domain_free(d, virq, i);
>   return err;
> + }
>  
>   irq_domain_set_hwirq_and_chip(d, virq + i, hwirq + i,
> -       &crossbar_chip, NULL);
> +      &crossbar_chip, NULL);

Why change the indentation here?

>   }
>  
>   return 0;
> @@ -336,6 +342,18 @@ static int __init crossbar_of_init(struct device_node *node)
>   return ret;
>  }
>  
> +static void __init crossbar_free(void)
> +{
> + if (!cb)
> + return;

cb cannot be NULL here. In fact, the only way this code can be reached
is after the call to irq_domain_create_hierarchy() in irqcrossbar_init()
which already dereferences cb.

> +
> + kfree(cb->register_offsets);
> + kfree(cb->irq_map);
> + iounmap(cb->crossbar_base);
> + kfree(cb);
> + cb = NULL;
> +}
> +
>  static int __init irqcrossbar_init(struct device_node *node,
>      struct device_node *parent)
>  {
> @@ -361,6 +379,7 @@ static int __init irqcrossbar_init(struct device_node *node,
>        of_fwnode_handle(node), &crossbar_domain_ops, NULL);
>   if (!domain) {
>   pr_err("%pOF: failed to allocated domain\n", node);
> + crossbar_free();

Why create a separate function and not do the cleanup here? It's not
called anywhere else, and the driver cannot be compiled as a loadable
module (and therefore doesn't need a global _free() function).

>   return -ENOMEM;
>   }
>