[PATCH v5 4/7] gpio: gpiolib: fix allocation order in hierarchical IRQ domains

From: Oleksij Rempel

Date: Mon Mar 16 2026 - 10:11:15 EST


Allocate parent IRQs after setting the basic IRQ handler to avoid NULL
pointer dereferences and RCU stalls.

In gpiochip_hierarchy_irq_domain_alloc(), calling irq_domain_set_info()
before parent allocation causes a NULL pointer dereference for
slow-bus (SPI/I2C) IRQ chips because the child proxies .irq_bus_lock
to a parent->chip that is not yet populated.

Conversely, moving the entire configuration after parent allocation
causes RCU stalls if a hardware interrupt is pending; the unconfigured
descriptor defaults to handle_bad_irq and fails to acknowledge the
interrupt.

Fix this by splitting the initialization: set the handler and data
before parent allocation to handle pending events, but defer setting
the chip and hardware IRQ info until the parent is fully allocated.

Signed-off-by: Oleksij Rempel <o.rempel@xxxxxxxxxxxxxx>
Fixes: fdd61a013a24 ("gpio: Add support for hierarchical IRQ domains")
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
---
changes v5:
- move this patch back to this series
- split irq_domain_set_info(). Set the handler and data before parent
allocatio and set the chip and hardware IRQ info after parent
allocation.
- previous version:
https://lore.kernel.org/all/20260309134920.1918294-5-o.rempel@xxxxxxxxxxxxxx/
---
drivers/gpio/gpiolib.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 9550500e1690..3ee1a403ccf8 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1632,14 +1632,8 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
* We set handle_bad_irq because the .set_type() should
* always be invoked and set the right type of handler.
*/
- irq_domain_set_info(d,
- irq,
- hwirq,
- gc->irq.chip,
- gc,
- girq->handler,
- NULL, NULL);
- irq_set_probe(irq);
+ irq_set_handler(irq, girq->handler);
+ irq_set_handler_data(irq, gc);

/* This parent only handles asserted level IRQs */
ret = girq->populate_parent_alloc_arg(gc, &gpio_parent_fwspec,
@@ -1657,12 +1651,17 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
*/
if (irq_domain_is_msi(d->parent) && (ret == -EEXIST))
ret = 0;
- if (ret)
+ if (ret) {
gpiochip_err(gc,
"failed to allocate parent hwirq %d for hwirq %lu\n",
parent_hwirq, hwirq);
+ return ret;
+ }

- return ret;
+ irq_domain_set_hwirq_and_chip(d, irq, hwirq, gc->irq.chip, gc);
+ irq_set_probe(irq);
+
+ return 0;
}

static unsigned int gpiochip_child_offset_to_irq_noop(struct gpio_chip *gc,
--
2.47.3