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

Oleksij Rempel posted 7 patches 3 weeks ago
There is a newer version of this series
[PATCH v5 4/7] gpio: gpiolib: fix allocation order in hierarchical IRQ domains
Posted by Oleksij Rempel 3 weeks ago
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@pengutronix.de>
Fixes: fdd61a013a24 ("gpio: Add support for hierarchical IRQ domains")
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
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@pengutronix.de/
---
 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