Skip to content

Commit dec85d2

Browse files
Sticklyman1936Thomas Gleixner
authored andcommitted
irqchip/gic-v5: Move LPI allocation into the LPI domain
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: 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-2-sascha.bischoff@arm.com
1 parent 254f496 commit dec85d2

3 files changed

Lines changed: 28 additions & 42 deletions

File tree

drivers/irqchip/irq-gic-v5-its.c

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -929,8 +929,8 @@ static void gicv5_its_free_eventid(struct gicv5_its_dev *its_dev, u32 event_id_b
929929
static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
930930
unsigned int nr_irqs, void *arg)
931931
{
932-
u32 device_id, event_id_base, lpi;
933932
struct gicv5_its_dev *its_dev;
933+
u32 device_id, event_id_base;
934934
msi_alloc_info_t *info = arg;
935935
irq_hw_number_t hwirq;
936936
struct irq_data *irqd;
@@ -949,16 +949,8 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi
949949
device_id = its_dev->device_id;
950950

951951
for (i = 0; i < nr_irqs; i++) {
952-
ret = gicv5_alloc_lpi();
953-
if (ret < 0) {
954-
pr_debug("Failed to find free LPI!\n");
955-
goto out_free_irqs;
956-
}
957-
lpi = ret;
958-
959-
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
952+
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, NULL);
960953
if (ret) {
961-
gicv5_free_lpi(lpi);
962954
goto out_free_irqs;
963955
}
964956

@@ -983,7 +975,6 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi
983975
out_free_irqs:
984976
while (--i >= 0) {
985977
irqd = irq_domain_get_irq_data(domain, virq + i);
986-
gicv5_free_lpi(irqd->parent_data->hwirq);
987978
irq_domain_reset_irq_data(irqd);
988979
irq_domain_free_irqs_parent(domain, virq + i, 1);
989980
}
@@ -1013,7 +1004,6 @@ static void gicv5_its_irq_domain_free(struct irq_domain *domain, unsigned int vi
10131004
for (i = 0; i < nr_irqs; i++) {
10141005
d = irq_domain_get_irq_data(domain, virq + i);
10151006

1016-
gicv5_free_lpi(d->parent_data->hwirq);
10171007
irq_domain_reset_irq_data(d);
10181008
irq_domain_free_irqs_parent(domain, virq + i, 1);
10191009
}

drivers/irqchip/irq-gic-v5.c

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,6 @@ static void release_lpi(u32 lpi)
5959
ida_free(&lpi_ida, lpi);
6060
}
6161

62-
int gicv5_alloc_lpi(void)
63-
{
64-
return alloc_lpi();
65-
}
66-
67-
void gicv5_free_lpi(u32 lpi)
68-
{
69-
release_lpi(lpi);
70-
}
71-
7262
static void gicv5_ppi_priority_init(void)
7363
{
7464
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)
806796
gicv5_lpi_irq_write_pending_state(d, false);
807797
}
808798

799+
static void gicv5_irq_lpi_domain_free(struct irq_domain *domain, unsigned int virq,
800+
unsigned int nr_irqs)
801+
{
802+
struct irq_data *d;
803+
804+
if (WARN_ON_ONCE(nr_irqs != 1))
805+
return;
806+
807+
d = irq_domain_get_irq_data(domain, virq);
808+
809+
release_lpi(d->hwirq);
810+
811+
irq_set_handler(virq, NULL);
812+
irq_domain_reset_irq_data(d);
813+
}
814+
809815
static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq,
810816
unsigned int nr_irqs, void *arg)
811817
{
812818
irq_hw_number_t hwirq;
813819
struct irq_data *irqd;
814-
u32 *lpi = arg;
815820
int ret;
816821

817822
if (WARN_ON_ONCE(nr_irqs != 1))
818823
return -EINVAL;
819824

820-
hwirq = *lpi;
825+
ret = alloc_lpi();
826+
if (ret < 0)
827+
return ret;
828+
hwirq = ret;
821829

822830
irqd = irq_domain_get_irq_data(domain, virq);
823831

@@ -826,8 +834,10 @@ static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int vi
826834
irqd_set_single_target(irqd);
827835

828836
ret = gicv5_irs_iste_alloc(hwirq);
829-
if (ret < 0)
837+
if (ret < 0) {
838+
release_lpi(hwirq);
830839
return ret;
840+
}
831841

832842
gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
833843
gicv5_lpi_config_reset(irqd);
@@ -837,7 +847,7 @@ static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int vi
837847

838848
static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = {
839849
.alloc = gicv5_irq_lpi_domain_alloc,
840-
.free = gicv5_irq_domain_free,
850+
.free = gicv5_irq_lpi_domain_free,
841851
};
842852

843853
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
859869
{
860870
struct irq_data *irqd;
861871
int ret, i;
862-
u32 lpi;
863872

864873
for (i = 0; i < nr_irqs; i++) {
865-
ret = gicv5_alloc_lpi();
866-
if (ret < 0)
874+
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, NULL);
875+
if (ret)
867876
return ret;
868877

869-
lpi = ret;
870-
871-
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
872-
if (ret) {
873-
gicv5_free_lpi(lpi);
874-
return ret;
875-
}
876-
877878
irqd = irq_domain_get_irq_data(domain, virq + i);
878879

879880
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
899900
if (!d)
900901
return;
901902

902-
gicv5_free_lpi(d->parent_data->hwirq);
903-
904903
irq_set_handler(virq + i, NULL);
905904
irq_domain_reset_irq_data(d);
906905
irq_domain_free_irqs_parent(domain, virq + i, 1);

include/linux/irqchip/arm-gic-v5.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,9 +425,6 @@ struct gicv5_its_itt_cfg {
425425
void gicv5_init_lpis(u32 max);
426426
void gicv5_deinit_lpis(void);
427427

428-
int gicv5_alloc_lpi(void);
429-
void gicv5_free_lpi(u32 lpi);
430-
431428
void __init gicv5_its_of_probe(struct device_node *parent);
432429
void __init gicv5_its_acpi_probe(void);
433430
#endif

0 commit comments

Comments
 (0)