[PATCH v3 3/3] PCI: rzg3s: Fix IRQ domain initialization error handling
Prabhakar <[email protected]>
| Newsgroups | org.kernel.vger.linux-renesas-soc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
From: Lad Prabhakar <[email protected]> rzg3s_pcie_init_irqdomain() installs chained handlers for the INTx parent interrupts before creating the INTx irqdomain and initializing MSI support. If any subsequent step fails, such as obtaining an INTx IRQ, creating the irqdomain, or initializing MSI, the error path returns without removing any chained handlers that were already installed. This leaves stale handler and data pointers attached to the parent interrupts. Add a helper to tear down the INTx IRQ setup, including removing the IRQ domain and clearing the chained handlers. Use it to unwind partially completed initialization and from the normal IRQ domain teardown path. Fixes: 7ef502fb35b28 ("PCI: Add Renesas RZ/G3S host controller driver") Cc: [email protected] Signed-off-by: Lad Prabhakar <[email protected]> --- v2->v3: - Added rzg3s_pcie_teardown_intx() helper to remove the irqdomain and clear chained handlers. - Updated commit message. v1->v2: - Restricted the code to max 80 columns. - Moved calling irq_domain_remove() under err_cleanup_intx label. --- drivers/pci/controller/pcie-rzg3s-host.c | 50 +++++++++++++++++------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index f51b5d7c1998..3f7393c49a08 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -1006,22 +1006,38 @@ static const struct irq_domain_ops rzg3s_pcie_intx_domain_ops = { .xlate = irq_domain_xlate_onetwocell, }; +static void rzg3s_pcie_teardown_intx(struct rzg3s_pcie_host *host, + int count) +{ + while (--count >= 0) { + irq_set_chained_handler_and_data(host->intx_irqs[count], NULL, + NULL); + } + + if (host->intx_domain) + irq_domain_remove(host->intx_domain); +} + static int rzg3s_pcie_init_irqdomain(struct rzg3s_pcie_host *host) { struct device *dev = host->dev; struct platform_device *pdev = to_platform_device(dev); + int i, ret; - for (int i = 0; i < PCI_NUM_INTX; i++) { + for (i = 0; i < PCI_NUM_INTX; i++) { char irq_name[5] = {0}; int irq; scnprintf(irq_name, ARRAY_SIZE(irq_name), "int%c", 'a' + i); irq = platform_get_irq_byname(pdev, irq_name); - if (irq < 0) - return dev_err_probe(dev, irq, - "Failed to parse and map INT%c IRQ\n", - 'A' + i); + if (irq < 0) { + ret = irq; + dev_err_probe(dev, ret, + "Failed to parse and map INT%c IRQ\n", + 'A' + i); + goto teardown_intx; + } host->intx_irqs[i] = irq; irq_set_chained_handler_and_data(irq, @@ -1033,21 +1049,27 @@ static int rzg3s_pcie_init_irqdomain(struct rzg3s_pcie_host *host) PCI_NUM_INTX, &rzg3s_pcie_intx_domain_ops, host); - if (!host->intx_domain) - return dev_err_probe(dev, -EINVAL, - "Failed to add irq domain for INTx IRQs\n"); + if (!host->intx_domain) { + ret = -EINVAL; + dev_err_probe(dev, ret, + "Failed to add irq domain for INTx IRQs\n"); + goto teardown_intx; + } irq_domain_update_bus_token(host->intx_domain, DOMAIN_BUS_WIRED); if (IS_ENABLED(CONFIG_PCI_MSI)) { - int ret = rzg3s_pcie_init_msi(host); + ret = rzg3s_pcie_init_msi(host); - if (ret) { - irq_domain_remove(host->intx_domain); - return ret; - } + if (ret) + goto teardown_intx; } return 0; + +teardown_intx: + rzg3s_pcie_teardown_intx(host, i); + + return ret; } static void rzg3s_pcie_teardown_irqdomain(struct rzg3s_pcie_host *host) @@ -1055,7 +1077,7 @@ static void rzg3s_pcie_teardown_irqdomain(struct rzg3s_pcie_host *host) if (IS_ENABLED(CONFIG_PCI_MSI)) rzg3s_pcie_teardown_msi(host); - irq_domain_remove(host->intx_domain); + rzg3s_pcie_teardown_intx(host, PCI_NUM_INTX); } static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host) -- 2.43.0