[PATCH v6 4/7] gpio: gpiolib: split child IRQ setup in hierarchical alloc
From: Oleksij Rempel
Date: Sat Mar 21 2026 - 02:52:48 EST
Setting full child IRQ info before parent allocation can crash slow-bus
GPIO IRQ chips that proxy irq callbacks such as .irq_bus_lock to the
parent IRQ chip. At that point the parent IRQ is not allocated yet, so
parent->chip is still unset.
Moving the entire child descriptor setup after parent allocation avoids
that NULL dereference, but opens another failure window: if the hardware
interrupt is already pending, the child descriptor is still using
handle_bad_irq and the interrupt may storm before the descriptor is
fully configured.
Fix this by splitting the child IRQ initialization:
- install the top-level IRQ handler before parent allocation
- install the child chip and hwirq mapping after parent allocation
succeeds
In hierarchical IRQ domains, installing the flow handler before the
child chip is assigned is valid as long as an outer chip is already
present, which allows pending interrupts to avoid handle_bad_irq during
allocation.
This avoids the early slow-bus dereference while also preventing pending
interrupts from hitting handle_bad_irq during allocation.
Signed-off-by: Oleksij Rempel <o.rempel@xxxxxxxxxxxxxx>
Fixes: fdd61a013a24 ("gpio: Add support for hierarchical IRQ domains")
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
Tested-by: Tommaso Merciai <tommaso.merciai.xr@xxxxxxxxxxxxxx>
Link: https://lore.kernel.org/all/abPqGvy5FqJ0a0ug@tom-desktop
---
changes v6:
- reword commit message
- add Tested-by: Tommaso Merciai ...
- set irq_set_chip_data(irq, gc) instead of irq_set_handler_data(irq, gc)
changes v5:
- move this patch back to this series
- split irq_domain_set_info(). Set the handler and data before parent
allocation 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 | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 9550500e1690..b506a6c945f7 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1629,17 +1629,12 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
gpiochip_dbg(gc, "found parent hwirq %u\n", parent_hwirq);
/*
- * We set handle_bad_irq because the .set_type() should
- * always be invoked and set the right type of handler.
+ * Install the top-level flow handler early so a pending interrupt does
+ * not hit handle_bad_irq while the parent IRQ is being allocated. Defer
+ * child chip and hwirq assignment until parent allocation has completed.
*/
- irq_domain_set_info(d,
- irq,
- hwirq,
- gc->irq.chip,
- gc,
- girq->handler,
- NULL, NULL);
- irq_set_probe(irq);
+ irq_set_chip_data(irq, gc);
+ irq_set_handler(irq, girq->handler);
/* This parent only handles asserted level IRQs */
ret = girq->populate_parent_alloc_arg(gc, &gpio_parent_fwspec,
@@ -1657,12 +1652,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