Skip to content

Commit eb6f6d5

Browse files
Sticklyman1936Thomas Gleixner
authored andcommitted
irqchip/gic-v5: Support range allocation for LPIs
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
1 parent dec85d2 commit eb6f6d5

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
@@ -801,48 +801,54 @@ static void gicv5_irq_lpi_domain_free(struct irq_domain *domain, unsigned int vi
801801
{
802802
struct irq_data *d;
803803

804-
if (WARN_ON_ONCE(nr_irqs != 1))
805-
return;
806-
807-
d = irq_domain_get_irq_data(domain, virq);
804+
for (unsigned int i = 0; i < nr_irqs; i++, virq++) {
805+
d = irq_domain_get_irq_data(domain, virq);
808806

809-
release_lpi(d->hwirq);
807+
release_lpi(d->hwirq);
810808

811-
irq_set_handler(virq, NULL);
812-
irq_domain_reset_irq_data(d);
809+
irq_set_handler(virq, NULL);
810+
irq_domain_reset_irq_data(d);
811+
}
813812
}
814813

815814
static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq,
816815
unsigned int nr_irqs, void *arg)
817816
{
818817
irq_hw_number_t hwirq;
819818
struct irq_data *irqd;
819+
unsigned int i;
820820
int ret;
821821

822-
if (WARN_ON_ONCE(nr_irqs != 1))
823-
return -EINVAL;
824-
825-
ret = alloc_lpi();
826-
if (ret < 0)
827-
return ret;
828-
hwirq = ret;
822+
for (i = 0; i < nr_irqs; i++) {
823+
ret = alloc_lpi();
824+
if (ret < 0)
825+
goto out_free_lpis;
826+
hwirq = ret;
827+
828+
ret = gicv5_irs_iste_alloc(hwirq);
829+
if (ret < 0) {
830+
/* Undo partial state first, then clean up the rest */
831+
release_lpi(hwirq);
832+
goto out_free_lpis;
833+
}
829834

830-
irqd = irq_domain_get_irq_data(domain, virq);
835+
irqd = irq_domain_get_irq_data(domain, virq + i);
831836

832-
irq_domain_set_info(domain, virq, hwirq, &gicv5_lpi_irq_chip, NULL,
833-
handle_fasteoi_irq, NULL, NULL);
834-
irqd_set_single_target(irqd);
837+
irq_domain_set_info(domain, virq + i, hwirq, &gicv5_lpi_irq_chip,
838+
NULL, handle_fasteoi_irq, NULL, NULL);
839+
irqd_set_single_target(irqd);
835840

836-
ret = gicv5_irs_iste_alloc(hwirq);
837-
if (ret < 0) {
838-
release_lpi(hwirq);
839-
return ret;
841+
gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
842+
gicv5_lpi_config_reset(irqd);
840843
}
841844

842-
gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
843-
gicv5_lpi_config_reset(irqd);
844-
845845
return 0;
846+
847+
out_free_lpis:
848+
if (i)
849+
gicv5_irq_lpi_domain_free(domain, virq, i);
850+
851+
return ret;
846852
}
847853

848854
static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = {
@@ -868,21 +874,21 @@ static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain, unsigned int vi
868874
unsigned int nr_irqs, void *arg)
869875
{
870876
struct irq_data *irqd;
871-
int ret, i;
877+
int ret;
872878

873-
for (i = 0; i < nr_irqs; i++) {
874-
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, NULL);
875-
if (ret)
876-
return ret;
879+
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
880+
if (ret)
881+
return ret;
877882

878-
irqd = irq_domain_get_irq_data(domain, virq + i);
883+
for (unsigned int i = 0; i < nr_irqs; i++, virq++) {
884+
irqd = irq_domain_get_irq_data(domain, virq);
879885

880-
irq_domain_set_hwirq_and_chip(domain, virq + i, i,
881-
&gicv5_ipi_irq_chip, NULL);
886+
irq_domain_set_hwirq_and_chip(domain, virq, i,
887+
&gicv5_ipi_irq_chip, NULL);
882888

883889
irqd_set_single_target(irqd);
884890

885-
irq_set_handler(virq + i, handle_percpu_irq);
891+
irq_set_handler(virq, handle_percpu_irq);
886892
}
887893

888894
return 0;
@@ -902,8 +908,9 @@ static void gicv5_irq_ipi_domain_free(struct irq_domain *domain, unsigned int vi
902908

903909
irq_set_handler(virq + i, NULL);
904910
irq_domain_reset_irq_data(d);
905-
irq_domain_free_irqs_parent(domain, virq + i, 1);
906911
}
912+
913+
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
907914
}
908915

909916
static const struct irq_domain_ops gicv5_irq_ipi_domain_ops = {

0 commit comments

Comments
 (0)