Skip to content

Commit c891f08

Browse files
author
Myron Stowe
committed
irqchip/gic-v3-its: Implement .msi_teardown() callback
JIRA: https://issues.redhat.com/browse/RHEL-120705 Upstream Status: 713335b commit 713335b Author: Marc Zyngier <maz@kernel.org> Date: Tue May 13 17:31:41 2025 +0100 irqchip/gic-v3-its: Implement .msi_teardown() callback The ITS driver currently nukes the structure representing an endpoint device translating via an ITS on freeing the last LPI allocated for it. That's an unfortunate state of affair, as it is pretty common for a driver to allocate a single MSI, do something clever, teardown this MSI, and reallocate a whole bunch of them. The NVME driver does exactly that, amongst others. What happens in that case is that the core code is accidentaly issuing another .msi_prepare() call, even if it shouldn't. This luckily cancels the above behaviour and hides the problem. In order to fix the core code, start by implementing the new .msi_teardown() callback. Nothing calls it yet, so a side effect is that the its_dev structure will not be freed and that the DID will stay mapped. Not a big deal, and this will be solved in following patches. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/all/20250513163144.2215824-3-maz@kernel.org Signed-off-by: Myron Stowe <mstowe@redhat.com>
1 parent 6db201b commit c891f08

File tree

3 files changed

+36
-23
lines changed

3 files changed

+36
-23
lines changed

drivers/irqchip/irq-gic-v3-its-msi-parent.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,14 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
167167
dev, nvec, info);
168168
}
169169

170+
static void its_msi_teardown(struct irq_domain *domain, msi_alloc_info_t *info)
171+
{
172+
struct msi_domain_info *msi_info;
173+
174+
msi_info = msi_get_domain_info(domain->parent);
175+
msi_info->ops->msi_teardown(domain->parent, info);
176+
}
177+
170178
static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
171179
struct irq_domain *real_parent, struct msi_domain_info *info)
172180
{
@@ -190,6 +198,7 @@ static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
190198
* %MSI_MAX_INDEX.
191199
*/
192200
info->ops->msi_prepare = its_pci_msi_prepare;
201+
info->ops->msi_teardown = its_msi_teardown;
193202
break;
194203
case DOMAIN_BUS_DEVICE_MSI:
195204
case DOMAIN_BUS_WIRED_TO_MSI:
@@ -198,6 +207,7 @@ static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
198207
* size is also known at domain creation time.
199208
*/
200209
info->ops->msi_prepare = its_pmsi_prepare;
210+
info->ops->msi_teardown = its_msi_teardown;
201211
break;
202212
default:
203213
/* Confused. How did the lib return true? */

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

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3618,8 +3618,33 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
36183618
return err;
36193619
}
36203620

3621+
static void its_msi_teardown(struct irq_domain *domain, msi_alloc_info_t *info)
3622+
{
3623+
struct its_device *its_dev = info->scratchpad[0].ptr;
3624+
3625+
guard(mutex)(&its_dev->its->dev_alloc_lock);
3626+
3627+
/* If the device is shared, keep everything around */
3628+
if (its_dev->shared)
3629+
return;
3630+
3631+
/* LPIs should have been already unmapped at this stage */
3632+
if (WARN_ON_ONCE(!bitmap_empty(its_dev->event_map.lpi_map,
3633+
its_dev->event_map.nr_lpis)))
3634+
return;
3635+
3636+
its_lpi_free(its_dev->event_map.lpi_map,
3637+
its_dev->event_map.lpi_base,
3638+
its_dev->event_map.nr_lpis);
3639+
3640+
/* Unmap device/itt, and get rid of the tracking */
3641+
its_send_mapd(its_dev, 0);
3642+
its_free_device(its_dev);
3643+
}
3644+
36213645
static struct msi_domain_ops its_msi_domain_ops = {
36223646
.msi_prepare = its_msi_prepare,
3647+
.msi_teardown = its_msi_teardown,
36233648
};
36243649

36253650
static int its_irq_gic_domain_alloc(struct irq_domain *domain,
@@ -3720,7 +3745,6 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
37203745
{
37213746
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
37223747
struct its_device *its_dev = irq_data_get_irq_chip_data(d);
3723-
struct its_node *its = its_dev->its;
37243748
int i;
37253749

37263750
bitmap_release_region(its_dev->event_map.lpi_map,
@@ -3734,26 +3758,6 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
37343758
irq_domain_reset_irq_data(data);
37353759
}
37363760

3737-
mutex_lock(&its->dev_alloc_lock);
3738-
3739-
/*
3740-
* If all interrupts have been freed, start mopping the
3741-
* floor. This is conditioned on the device not being shared.
3742-
*/
3743-
if (!its_dev->shared &&
3744-
bitmap_empty(its_dev->event_map.lpi_map,
3745-
its_dev->event_map.nr_lpis)) {
3746-
its_lpi_free(its_dev->event_map.lpi_map,
3747-
its_dev->event_map.lpi_base,
3748-
its_dev->event_map.nr_lpis);
3749-
3750-
/* Unmap device/itt */
3751-
its_send_mapd(its_dev, 0);
3752-
its_free_device(its_dev);
3753-
}
3754-
3755-
mutex_unlock(&its->dev_alloc_lock);
3756-
37573761
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
37583762
}
37593763

kernel/irq/msi.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -776,8 +776,7 @@ static int msi_domain_ops_prepare(struct irq_domain *domain, struct device *dev,
776776
return 0;
777777
}
778778

779-
static void msi_domain_ops_teardown(struct irq_domain *domain,
780-
msi_alloc_info_t *arg)
779+
static void msi_domain_ops_teardown(struct irq_domain *domain, msi_alloc_info_t *arg)
781780
{
782781
}
783782

0 commit comments

Comments
 (0)