Skip to content

Commit e6550b1

Browse files
Sticklyman1936gregkh
authored andcommitted
irqchip/gic-v5: Move LPI allocation into the LPI domain
commit dec85d2fbd20de3711a71e65397dfdb40c3fa953 upstream. 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 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 84ff9ae commit e6550b1

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
@@ -927,8 +927,8 @@ static void gicv5_its_free_eventid(struct gicv5_its_dev *its_dev, u32 event_id_b
927927
static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
928928
unsigned int nr_irqs, void *arg)
929929
{
930-
u32 device_id, event_id_base, lpi;
931930
struct gicv5_its_dev *its_dev;
931+
u32 device_id, event_id_base;
932932
msi_alloc_info_t *info = arg;
933933
irq_hw_number_t hwirq;
934934
struct irq_data *irqd;
@@ -947,16 +947,8 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi
947947
device_id = its_dev->device_id;
948948

949949
for (i = 0; i < nr_irqs; i++) {
950-
ret = gicv5_alloc_lpi();
951-
if (ret < 0) {
952-
pr_debug("Failed to find free LPI!\n");
953-
goto out_free_irqs;
954-
}
955-
lpi = ret;
956-
957-
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
950+
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, NULL);
958951
if (ret) {
959-
gicv5_free_lpi(lpi);
960952
goto out_free_irqs;
961953
}
962954

@@ -981,7 +973,6 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi
981973
out_free_irqs:
982974
while (--i >= 0) {
983975
irqd = irq_domain_get_irq_data(domain, virq + i);
984-
gicv5_free_lpi(irqd->parent_data->hwirq);
985976
irq_domain_reset_irq_data(irqd);
986977
irq_domain_free_irqs_parent(domain, virq + i, 1);
987978
}
@@ -1011,7 +1002,6 @@ static void gicv5_its_irq_domain_free(struct irq_domain *domain, unsigned int vi
10111002
for (i = 0; i < nr_irqs; i++) {
10121003
d = irq_domain_get_irq_data(domain, virq + i);
10131004

1014-
gicv5_free_lpi(d->parent_data->hwirq);
10151005
irq_domain_reset_irq_data(d);
10161006
irq_domain_free_irqs_parent(domain, virq + i, 1);
10171007
}

drivers/irqchip/irq-gic-v5.c

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

61-
int gicv5_alloc_lpi(void)
62-
{
63-
return alloc_lpi();
64-
}
65-
66-
void gicv5_free_lpi(u32 lpi)
67-
{
68-
release_lpi(lpi);
69-
}
70-
7161
static void gicv5_ppi_priority_init(void)
7262
{
7363
write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR0_EL1);
@@ -751,18 +741,36 @@ static void gicv5_lpi_config_reset(struct irq_data *d)
751741
gicv5_lpi_irq_write_pending_state(d, false);
752742
}
753743

744+
static void gicv5_irq_lpi_domain_free(struct irq_domain *domain, unsigned int virq,
745+
unsigned int nr_irqs)
746+
{
747+
struct irq_data *d;
748+
749+
if (WARN_ON_ONCE(nr_irqs != 1))
750+
return;
751+
752+
d = irq_domain_get_irq_data(domain, virq);
753+
754+
release_lpi(d->hwirq);
755+
756+
irq_set_handler(virq, NULL);
757+
irq_domain_reset_irq_data(d);
758+
}
759+
754760
static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq,
755761
unsigned int nr_irqs, void *arg)
756762
{
757763
irq_hw_number_t hwirq;
758764
struct irq_data *irqd;
759-
u32 *lpi = arg;
760765
int ret;
761766

762767
if (WARN_ON_ONCE(nr_irqs != 1))
763768
return -EINVAL;
764769

765-
hwirq = *lpi;
770+
ret = alloc_lpi();
771+
if (ret < 0)
772+
return ret;
773+
hwirq = ret;
766774

767775
irqd = irq_domain_get_irq_data(domain, virq);
768776

@@ -771,8 +779,10 @@ static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int vi
771779
irqd_set_single_target(irqd);
772780

773781
ret = gicv5_irs_iste_alloc(hwirq);
774-
if (ret < 0)
782+
if (ret < 0) {
783+
release_lpi(hwirq);
775784
return ret;
785+
}
776786

777787
gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
778788
gicv5_lpi_config_reset(irqd);
@@ -782,7 +792,7 @@ static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int vi
782792

783793
static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = {
784794
.alloc = gicv5_irq_lpi_domain_alloc,
785-
.free = gicv5_irq_domain_free,
795+
.free = gicv5_irq_lpi_domain_free,
786796
};
787797

788798
void __init gicv5_init_lpi_domain(void)
@@ -804,21 +814,12 @@ static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain, unsigned int vi
804814
{
805815
struct irq_data *irqd;
806816
int ret, i;
807-
u32 lpi;
808817

809818
for (i = 0; i < nr_irqs; i++) {
810-
ret = gicv5_alloc_lpi();
811-
if (ret < 0)
819+
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, NULL);
820+
if (ret)
812821
return ret;
813822

814-
lpi = ret;
815-
816-
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
817-
if (ret) {
818-
gicv5_free_lpi(lpi);
819-
return ret;
820-
}
821-
822823
irqd = irq_domain_get_irq_data(domain, virq + i);
823824

824825
irq_domain_set_hwirq_and_chip(domain, virq + i, i,
@@ -844,8 +845,6 @@ static void gicv5_irq_ipi_domain_free(struct irq_domain *domain, unsigned int vi
844845
if (!d)
845846
return;
846847

847-
gicv5_free_lpi(d->parent_data->hwirq);
848-
849848
irq_set_handler(virq + i, NULL);
850849
irq_domain_reset_irq_data(d);
851850
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
@@ -387,8 +387,5 @@ struct gicv5_its_itt_cfg {
387387
void gicv5_init_lpis(u32 max);
388388
void gicv5_deinit_lpis(void);
389389

390-
int gicv5_alloc_lpi(void);
391-
void gicv5_free_lpi(u32 lpi);
392-
393390
void __init gicv5_its_of_probe(struct device_node *parent);
394391
#endif

0 commit comments

Comments
 (0)