[PATCH 11/16] irqchip/loongson-eiointc: preserve live state on cascade failure

From: Haofeng Li

Date: Tue Jul 14 2026 - 09:30:51 EST


From: Haofeng Li <lihaofeng@xxxxxxxxxx>

eiointc_init() publishes priv through the IRQ domain, chained
handlers, eiointc_priv[], syscore operations, and CPU hotplug state.
If subsequent cascade initialization fails, jumping to
out_free_handle frees priv and its fwnode while all of those users
remain live.

Cascade initialization may also have created PCH PIC, MSI, or LPC
child domains before reporting an error, so removing the EIOINTC
parent at that point is not a safe rollback.

After eiointc_init() succeeds, keep the published controller alive and
propagate the cascade error without freeing it. Before ownership is
transferred, validate the parent mapping and dispose it if
eiointc_init() fails. Preserve the actual error code on all cleanup
paths.

Fixes: a3f1132c4c6b ("irqchip/loongson-eiointc: Add DT init support")

Signed-off-by: Haofeng Li <lihaofeng@xxxxxxxxxx>
---
drivers/irqchip/irq-loongson-eiointc.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
index a9e8b481d31d..d2310a1ebbd6 100644
--- a/drivers/irqchip/irq-loongson-eiointc.c
+++ b/drivers/irqchip/irq-loongson-eiointc.c
@@ -580,7 +580,7 @@ static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq,
int __init eiointc_acpi_init(struct irq_domain *parent,
struct acpi_madt_eio_pic *acpi_eiointc)
{
- int parent_irq, ret;
+ int parent_irq, ret = -ENOMEM;
struct eiointc_priv *priv;
int node;

@@ -599,10 +599,12 @@ int __init eiointc_acpi_init(struct irq_domain *parent,
priv->node = acpi_eiointc->node;
priv->parent_hwirq = acpi_eiointc->cascade;
parent_irq = irq_create_mapping(parent, acpi_eiointc->cascade);
+ if (!parent_irq)
+ goto out_free_handle;

ret = eiointc_init(priv, parent_irq, acpi_eiointc->node_map);
if (ret < 0)
- goto out_free_handle;
+ goto out_dispose_mapping;

if (cpu_has_flatmode)
node = early_cpu_to_node(acpi_eiointc->node * CORES_PER_EIO_NODE);
@@ -612,18 +614,17 @@ int __init eiointc_acpi_init(struct irq_domain *parent,
acpi_set_vec_parent(node, priv->eiointc_domain, msi_group);

ret = acpi_cascade_irqdomain_init();
- if (ret < 0)
- goto out_free_handle;
-
return ret;

+out_dispose_mapping:
+ irq_dispose_mapping(parent_irq);
out_free_handle:
irq_domain_free_fwnode(priv->domain_handle);
priv->domain_handle = NULL;
out_free_priv:
kfree(priv);

- return -ENOMEM;
+ return ret;
}

static int __init eiointc_of_init(struct device_node *of_node,
--
2.25.1