Skip to content

Commit 2cbd4ab

Browse files
Sticklyman1936gregkh
authored andcommitted
irqchip/gic-v5: Support range allocation for LPIs
commit eb6f6d523813ead9dc2799194a2839d42c049734 upstream. The per-IPI parent allocation loop returns immediately on failure and leaks any parent interrupts allocated by earlier iterations. The GICv5 LPI domain now owns LPI allocation and teardown internally, but its irq_domain callbacks still reject requests where nr_irqs is greater than one. This forces child domains to allocate and free LPIs one at a time even when the interrupt core requests a contiguous range. Handle multi-interrupt allocation and teardown in the LPI domain by iterating over the requested range and unwinding any partially allocated state on failure. Allocate the parent LPIs for the IPI domain with a single range request as well, which cures the leakage problem. Fixes: 0f01013 ("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-3-sascha.bischoff@arm.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent e6550b1 commit 2cbd4ab

1 file changed

Lines changed: 42 additions & 35 deletions

File tree

drivers/irqchip/irq-gic-v5.c

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -746,48 +746,54 @@ static void gicv5_irq_lpi_domain_free(struct irq_domain *domain, unsigned int vi
746746
{
747747
struct irq_data *d;
748748

749-
if (WARN_ON_ONCE(nr_irqs != 1))
750-
return;
751-
752-
d = irq_domain_get_irq_data(domain, virq);
749+
for (unsigned int i = 0; i < nr_irqs; i++, virq++) {
750+
d = irq_domain_get_irq_data(domain, virq);
753751

754-
release_lpi(d->hwirq);
752+
release_lpi(d->hwirq);
755753

756-
irq_set_handler(virq, NULL);
757-
irq_domain_reset_irq_data(d);
754+
irq_set_handler(virq, NULL);
755+
irq_domain_reset_irq_data(d);
756+
}
758757
}
759758

760759
static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq,
761760
unsigned int nr_irqs, void *arg)
762761
{
763762
irq_hw_number_t hwirq;
764763
struct irq_data *irqd;
764+
unsigned int i;
765765
int ret;
766766

767-
if (WARN_ON_ONCE(nr_irqs != 1))
768-
return -EINVAL;
769-
770-
ret = alloc_lpi();
771-
if (ret < 0)
772-
return ret;
773-
hwirq = ret;
767+
for (i = 0; i < nr_irqs; i++) {
768+
ret = alloc_lpi();
769+
if (ret < 0)
770+
goto out_free_lpis;
771+
hwirq = ret;
772+
773+
ret = gicv5_irs_iste_alloc(hwirq);
774+
if (ret < 0) {
775+
/* Undo partial state first, then clean up the rest */
776+
release_lpi(hwirq);
777+
goto out_free_lpis;
778+
}
774779

775-
irqd = irq_domain_get_irq_data(domain, virq);
780+
irqd = irq_domain_get_irq_data(domain, virq + i);
776781

777-
irq_domain_set_info(domain, virq, hwirq, &gicv5_lpi_irq_chip, NULL,
778-
handle_fasteoi_irq, NULL, NULL);
779-
irqd_set_single_target(irqd);
782+
irq_domain_set_info(domain, virq + i, hwirq, &gicv5_lpi_irq_chip,
783+
NULL, handle_fasteoi_irq, NULL, NULL);
784+
irqd_set_single_target(irqd);
780785

781-
ret = gicv5_irs_iste_alloc(hwirq);
782-
if (ret < 0) {
783-
release_lpi(hwirq);
784-
return ret;
786+
gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
787+
gicv5_lpi_config_reset(irqd);
785788
}
786789

787-
gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
788-
gicv5_lpi_config_reset(irqd);
789-
790790
return 0;
791+
792+
out_free_lpis:
793+
if (i)
794+
gicv5_irq_lpi_domain_free(domain, virq, i);
795+
796+
return ret;
791797
}
792798

793799
static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = {
@@ -813,21 +819,21 @@ static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain, unsigned int vi
813819
unsigned int nr_irqs, void *arg)
814820
{
815821
struct irq_data *irqd;
816-
int ret, i;
822+
int ret;
817823

818-
for (i = 0; i < nr_irqs; i++) {
819-
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, NULL);
820-
if (ret)
821-
return ret;
824+
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
825+
if (ret)
826+
return ret;
822827

823-
irqd = irq_domain_get_irq_data(domain, virq + i);
828+
for (unsigned int i = 0; i < nr_irqs; i++, virq++) {
829+
irqd = irq_domain_get_irq_data(domain, virq);
824830

825-
irq_domain_set_hwirq_and_chip(domain, virq + i, i,
826-
&gicv5_ipi_irq_chip, NULL);
831+
irq_domain_set_hwirq_and_chip(domain, virq, i,
832+
&gicv5_ipi_irq_chip, NULL);
827833

828834
irqd_set_single_target(irqd);
829835

830-
irq_set_handler(virq + i, handle_percpu_irq);
836+
irq_set_handler(virq, handle_percpu_irq);
831837
}
832838

833839
return 0;
@@ -847,8 +853,9 @@ static void gicv5_irq_ipi_domain_free(struct irq_domain *domain, unsigned int vi
847853

848854
irq_set_handler(virq + i, NULL);
849855
irq_domain_reset_irq_data(d);
850-
irq_domain_free_irqs_parent(domain, virq + i, 1);
851856
}
857+
858+
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
852859
}
853860

854861
static const struct irq_domain_ops gicv5_irq_ipi_domain_ops = {

0 commit comments

Comments
 (0)