]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
irqchip/gic-v5: Move LPI allocation into the LPI domain
authorSascha Bischoff <Sascha.Bischoff@arm.com>
Wed, 6 May 2026 09:37:02 +0000 (09:37 +0000)
committerThomas Gleixner <tglx@kernel.org>
Mon, 11 May 2026 12:56:03 +0000 (14:56 +0200)
The IPI and ITS MSI domains currently allocate and release LPIs
directly, then pass the selected LPI ID to the parent LPI domain. This
leaks the LPI domain's allocation policy into its child domains and
forces each child to duplicate part of the parent domain's teardown.

Make the LPI domain allocate LPIs in its .alloc() callback and release
them in a matching .free() callback. Child domains can then request a
parent interrupt without passing an implementation-specific LPI ID,
and the LPI lifetime is tied to the domain that owns the LPI
namespace.

Remove the gicv5_alloc_lpi() and gicv5_free_lpi() wrappers now that no
external caller needs to manage LPIs directly.

This is a preparatory change for an actual leakage problem in the
allocation code and therefore tagged with the same Fixes tag.

Fixes: 0f0101325876 ("irqchip/gic-v5: Add GICv5 LPI/IPI support")
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Reviewed-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260506093634.382062-2-sascha.bischoff@arm.com
drivers/irqchip/irq-gic-v5-its.c
drivers/irqchip/irq-gic-v5.c
include/linux/irqchip/arm-gic-v5.h

index 36a8d1368f0e44567b499e5f6b5de3922f3c3238..36d03f82ef68477a19c05183c23fc1453a73ef5a 100644 (file)
@@ -929,8 +929,8 @@ static void gicv5_its_free_eventid(struct gicv5_its_dev *its_dev, u32 event_id_b
 static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
                                      unsigned int nr_irqs, void *arg)
 {
-       u32 device_id, event_id_base, lpi;
        struct gicv5_its_dev *its_dev;
+       u32 device_id, event_id_base;
        msi_alloc_info_t *info = arg;
        irq_hw_number_t hwirq;
        struct irq_data *irqd;
@@ -949,16 +949,8 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi
        device_id = its_dev->device_id;
 
        for (i = 0; i < nr_irqs; i++) {
-               ret = gicv5_alloc_lpi();
-               if (ret < 0) {
-                       pr_debug("Failed to find free LPI!\n");
-                       goto out_free_irqs;
-               }
-               lpi = ret;
-
-               ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
+               ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, NULL);
                if (ret) {
-                       gicv5_free_lpi(lpi);
                        goto out_free_irqs;
                }
 
@@ -983,7 +975,6 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi
 out_free_irqs:
        while (--i >= 0) {
                irqd = irq_domain_get_irq_data(domain, virq + i);
-               gicv5_free_lpi(irqd->parent_data->hwirq);
                irq_domain_reset_irq_data(irqd);
                irq_domain_free_irqs_parent(domain, virq + i, 1);
        }
@@ -1013,7 +1004,6 @@ static void gicv5_its_irq_domain_free(struct irq_domain *domain, unsigned int vi
        for (i = 0; i < nr_irqs; i++) {
                d = irq_domain_get_irq_data(domain, virq + i);
 
-               gicv5_free_lpi(d->parent_data->hwirq);
                irq_domain_reset_irq_data(d);
                irq_domain_free_irqs_parent(domain, virq + i, 1);
        }
index 6b0903be8ebfd3a05460410e813807623876c6d3..15a2a04398d2558902cb95564e8f93c753f314f6 100644 (file)
@@ -59,16 +59,6 @@ static void release_lpi(u32 lpi)
        ida_free(&lpi_ida, lpi);
 }
 
-int gicv5_alloc_lpi(void)
-{
-       return alloc_lpi();
-}
-
-void gicv5_free_lpi(u32 lpi)
-{
-       release_lpi(lpi);
-}
-
 static void gicv5_ppi_priority_init(void)
 {
        write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR0_EL1);
@@ -806,18 +796,36 @@ static void gicv5_lpi_config_reset(struct irq_data *d)
        gicv5_lpi_irq_write_pending_state(d, false);
 }
 
+static void gicv5_irq_lpi_domain_free(struct irq_domain *domain, unsigned int virq,
+                                     unsigned int nr_irqs)
+{
+       struct irq_data *d;
+
+       if (WARN_ON_ONCE(nr_irqs != 1))
+               return;
+
+       d = irq_domain_get_irq_data(domain, virq);
+
+       release_lpi(d->hwirq);
+
+       irq_set_handler(virq, NULL);
+       irq_domain_reset_irq_data(d);
+}
+
 static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq,
                                      unsigned int nr_irqs, void *arg)
 {
        irq_hw_number_t hwirq;
        struct irq_data *irqd;
-       u32 *lpi = arg;
        int ret;
 
        if (WARN_ON_ONCE(nr_irqs != 1))
                return -EINVAL;
 
-       hwirq = *lpi;
+       ret = alloc_lpi();
+       if (ret < 0)
+               return ret;
+       hwirq = ret;
 
        irqd = irq_domain_get_irq_data(domain, virq);
 
@@ -826,8 +834,10 @@ static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int vi
        irqd_set_single_target(irqd);
 
        ret = gicv5_irs_iste_alloc(hwirq);
-       if (ret < 0)
+       if (ret < 0) {
+               release_lpi(hwirq);
                return ret;
+       }
 
        gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
        gicv5_lpi_config_reset(irqd);
@@ -837,7 +847,7 @@ static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int vi
 
 static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = {
        .alloc  = gicv5_irq_lpi_domain_alloc,
-       .free   = gicv5_irq_domain_free,
+       .free   = gicv5_irq_lpi_domain_free,
 };
 
 void __init gicv5_init_lpi_domain(void)
@@ -859,21 +869,12 @@ static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain, unsigned int vi
 {
        struct irq_data *irqd;
        int ret, i;
-       u32 lpi;
 
        for (i = 0; i < nr_irqs; i++) {
-               ret = gicv5_alloc_lpi();
-               if (ret < 0)
+               ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, NULL);
+               if (ret)
                        return ret;
 
-               lpi = ret;
-
-               ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
-               if (ret) {
-                       gicv5_free_lpi(lpi);
-                       return ret;
-               }
-
                irqd = irq_domain_get_irq_data(domain, virq + i);
 
                irq_domain_set_hwirq_and_chip(domain, virq + i, i,
@@ -899,8 +900,6 @@ static void gicv5_irq_ipi_domain_free(struct irq_domain *domain, unsigned int vi
                if (!d)
                        return;
 
-               gicv5_free_lpi(d->parent_data->hwirq);
-
                irq_set_handler(virq + i, NULL);
                irq_domain_reset_irq_data(d);
                irq_domain_free_irqs_parent(domain, virq + i, 1);
index 40d2fce682940ab2adb2bc547e4765243888b8b5..f78787e654f4c6c53e6dac83e6296d9f41d225f8 100644 (file)
@@ -425,9 +425,6 @@ struct gicv5_its_itt_cfg {
 void gicv5_init_lpis(u32 max);
 void gicv5_deinit_lpis(void);
 
-int gicv5_alloc_lpi(void);
-void gicv5_free_lpi(u32 lpi);
-
 void __init gicv5_its_of_probe(struct device_node *parent);
 void __init gicv5_its_acpi_probe(void);
 #endif