[PATCH 02/16] irqchip/loongarch-ir: fix redirect free and alloc leaks

From: Haofeng Li

Date: Tue Jul 14 2026 - 08:29:38 EST


redirect_irde_free() ignores its irde argument and always frees
redirect_descs (node 0). Multi-node teardown therefore leaks non-zero
nodes.

On the alloc path, a failed redirect_table_alloc() after
irq_domain_alloc_irqs_parent() returns without freeing parent IRQs.
A mid-loop gpid allocation failure also leaks the current item (chip_data
is not set yet) and never releases the bitmap region obtained from
bitmap_find_free_region().

Free &irde->ird_table and &irde->inv_queue, free parent IRQs on table
alloc failure, kfree(item) on gpid failure, and release the full bitmap
region on the error path.

Fixes: 71619266e0a2 ("irqchip/loongarch-ir: Add IR (interrupt redirection) irqchip support")
Signed-off-by: Haofeng Li <lihaofeng@xxxxxxxxxx>
---
drivers/irqchip/irq-loongarch-ir.c | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c
index 21c649a89a70..695dfef6570a 100644
--- a/drivers/irqchip/irq-loongarch-ir.c
+++ b/drivers/irqchip/irq-loongarch-ir.c
@@ -219,6 +219,20 @@ static int redirect_table_alloc(int node, u32 nr_irqs)
return index;
}

+static void redirect_table_free_region(int node, int index, u32 nr_irqs)
+{
+ struct redirect_table *ird_table = &redirect_descs[node].ird_table;
+ int order = 0;
+
+ if (nr_irqs > 1) {
+ nr_irqs = __roundup_pow_of_two(nr_irqs);
+ order = ilog2(nr_irqs);
+ }
+
+ guard(raw_spinlock_irqsave)(&ird_table->lock);
+ bitmap_release_region(ird_table->bitmap, index, order);
+}
+
static void redirect_table_free(struct redirect_item *item)
{
struct redirect_table *ird_table = &item->irde->ird_table;
@@ -324,6 +338,7 @@ static int redirect_domain_alloc(struct irq_domain *domain, unsigned int virq,
index = redirect_table_alloc(node, nr_irqs);
if (index < 0) {
pr_err("Alloc redirect table entry failed\n");
+ irq_domain_free_irqs_parent(domain, virq, nr_irqs);
return -EINVAL;
}

@@ -347,6 +362,7 @@ static int redirect_domain_alloc(struct irq_domain *domain, unsigned int virq,
item->gpid = kzalloc_node(sizeof(*item->gpid), GFP_KERNEL, node);
if (!item->gpid) {
pr_err("Alloc redirect GPID failed\n");
+ kfree(item);
goto out_free_resources;
}
item->index = index + i;
@@ -361,6 +377,7 @@ static int redirect_domain_alloc(struct irq_domain *domain, unsigned int virq,

out_free_resources:
redirect_free_resources(domain, virq, nr_irqs);
+ redirect_table_free_region(node, index, nr_irqs);
irq_domain_free_irqs_common(domain, virq, nr_irqs);

return -ENOMEM;
@@ -436,8 +453,8 @@ static void redirect_irde_cfg(struct redirect_desc *irde)

static void __init redirect_irde_free(struct redirect_desc *irde)
{
- struct redirect_table *ird_table = &redirect_descs->ird_table;
- struct redirect_queue *inv_queue = &redirect_descs->inv_queue;
+ struct redirect_table *ird_table = &irde->ird_table;
+ struct redirect_queue *inv_queue = &irde->inv_queue;

if (ird_table->table) {
folio_put(virt_to_folio(ird_table->table));
--
2.25.1