Skip to content

Commit 44ed0f3

Browse files
committed
Merge tag 'irq-msi-2025-05-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull MSI updates from Thomas Gleixner: "Updates for the MSI subsystem (core code and PCI): - Switch the MSI descriptor locking to lock guards - Replace a broken and naive implementation of PCI/MSI-X control word updates in the PCI/TPH driver with a properly serialized variant in the PCI/MSI core code. - Remove the MSI descriptor abuse in the SCCI/UFS/QCOM driver by replacing the direct access to the MSI descriptors with the proper API function calls. People will never understand that APIs exist for a reason... - Provide core infrastructre for the upcoming PCI endpoint library extensions. Currently limited to ARM GICv3+, but in theory extensible to other architectures. - Provide a MSI domain::teardown() callback, which allows drivers to undo the effects of the prepare() callback. - Move the MSI domain::prepare() callback invocation to domain creation time to avoid redundant (and in case of ARM/GIC-V3-ITS confusing) invocations on every allocation. In combination with the new teardown callback this removes some ugly hacks in the GIC-V3-ITS driver, which pretended to work around the short comings of the core code so far. With this update the code is correct by design and implementation. - Make the irqchip MSI library globally available, provide a MSI parent domain creation helper and convert a bunch of (PCI/)MSI drivers over to the modern MSI parent mechanism. This is the first step to get rid of at least one incarnation of the three PCI/MSI management schemes. - The usual small cleanups and improvements" * tag 'irq-msi-2025-05-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (33 commits) PCI/MSI: Use bool for MSI enable state tracking PCI: tegra: Convert to MSI parent infrastructure PCI: xgene: Convert to MSI parent infrastructure PCI: apple: Convert to MSI parent infrastructure irqchip/msi-lib: Honour the MSI_FLAG_NO_AFFINITY flag irqchip/mvebu: Convert to msi_create_parent_irq_domain() helper irqchip/gic: Convert to msi_create_parent_irq_domain() helper genirq/msi: Add helper for creating MSI-parent irq domains irqchip: Make irq-msi-lib.h globally available irqchip/gic-v3-its: Use allocation size from the prepare call genirq/msi: Engage the .msi_teardown() callback on domain removal genirq/msi: Move prepare() call to per-device allocation irqchip/gic-v3-its: Implement .msi_teardown() callback genirq/msi: Add .msi_teardown() callback as the reverse of .msi_prepare() irqchip/gic-v3-its: Add support for device tree msi-map and msi-mask dt-bindings: PCI: pci-ep: Add support for iommu-map and msi-map irqchip/gic-v3-its: Set IRQ_DOMAIN_FLAG_MSI_IMMUTABLE for ITS irqdomain: Add IRQ_DOMAIN_FLAG_MSI_IMMUTABLE and irq_domain_is_msi_immutable() platform-msi: Add msi_remove_device_irq_domain() in platform_device_msi_free_irqs_all() genirq/msi: Rename msi_[un]lock_descs() ...
2 parents 2bd1bea + 4e7bca7 commit 44ed0f3

36 files changed

+607
-521
lines changed

Documentation/devicetree/bindings/pci/pci-ep.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@ properties:
1717
$nodename:
1818
pattern: "^pcie-ep@"
1919

20+
iommu-map:
21+
$ref: /schemas/types.yaml#/definitions/uint32-matrix
22+
items:
23+
items:
24+
- description: Device ID (see msi-map) base
25+
maximum: 0x7ffff
26+
- description: phandle to IOMMU
27+
- description: IOMMU specifier base (currently always 1 cell)
28+
- description: Number of Device IDs
29+
maximum: 0x80000
30+
31+
iommu-map-mask:
32+
description:
33+
A mask to be applied to each Device ID prior to being mapped to an
34+
IOMMU specifier per the iommu-map property.
35+
$ref: /schemas/types.yaml#/definitions/uint32
36+
maximum: 0x7ffff
37+
2038
max-functions:
2139
description: Maximum number of functions that can be configured
2240
$ref: /schemas/types.yaml#/definitions/uint8
@@ -35,6 +53,56 @@ properties:
3553
$ref: /schemas/types.yaml#/definitions/uint32
3654
enum: [ 1, 2, 3, 4 ]
3755

56+
msi-map:
57+
description: |
58+
Maps a Device ID to an MSI and associated MSI specifier data.
59+
60+
A PCI Endpoint (EP) can use MSI as a doorbell function. This is achieved by
61+
mapping the MSI controller's address into PCI BAR<n>. The PCI Root Complex
62+
can write to this BAR<n>, triggering the EP to generate IRQ. This notifies
63+
the EP-side driver of an event, eliminating the need for the driver to
64+
continuously poll for status changes.
65+
66+
However, the EP cannot rely on Requester ID (RID) because the RID is
67+
determined by the PCI topology of the host system. Since the EP may be
68+
connected to different PCI hosts, the RID can vary between systems and is
69+
therefore not a reliable identifier.
70+
71+
Each EP can support up to 8 physical functions and up to 65,536 virtual
72+
functions. To uniquely identify each child device, a device ID is defined
73+
as
74+
- Bits [2:0] for the function number (func)
75+
- Bits [18:3] for the virtual function index (vfunc)
76+
77+
The resulting device ID is computed as:
78+
79+
(func & 0x7) | (vfunc << 3)
80+
81+
The property is an arbitrary number of tuples of
82+
(device-id-base, msi, msi-base,length).
83+
84+
Any Device ID id in the interval [id-base, id-base + length) is
85+
associated with the listed MSI, with the MSI specifier
86+
(id - id-base + msi-base).
87+
$ref: /schemas/types.yaml#/definitions/uint32-matrix
88+
items:
89+
items:
90+
- description: The Device ID base matched by the entry
91+
maximum: 0x7ffff
92+
- description: phandle to msi-controller node
93+
- description: (optional) The msi-specifier produced for the first
94+
Device ID matched by the entry. Currently, msi-specifier is 0 or
95+
1 cells.
96+
- description: The length of consecutive Device IDs following the
97+
Device ID base
98+
maximum: 0x80000
99+
100+
msi-map-mask:
101+
description: A mask to be applied to each Device ID prior to being
102+
mapped to an msi-specifier per the msi-map property.
103+
$ref: /schemas/types.yaml#/definitions/uint32
104+
maximum: 0x7ffff
105+
38106
num-lanes:
39107
description: maximum number of lanes
40108
$ref: /schemas/types.yaml#/definitions/uint32

drivers/base/platform-msi.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,5 +95,6 @@ EXPORT_SYMBOL_GPL(platform_device_msi_init_and_alloc_irqs);
9595
void platform_device_msi_free_irqs_all(struct device *dev)
9696
{
9797
msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
98+
msi_remove_device_irq_domain(dev, MSI_DEFAULT_DOMAIN);
9899
}
99100
EXPORT_SYMBOL_GPL(platform_device_msi_free_irqs_all);

drivers/irqchip/irq-bcm2712-mip.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#include <linux/of_address.h>
1212
#include <linux/of_platform.h>
1313

14-
#include "irq-msi-lib.h"
14+
#include <linux/irqchip/irq-msi-lib.h>
1515

1616
#define MIP_INT_RAISE 0x00
1717
#define MIP_INT_CLEAR 0x10

drivers/irqchip/irq-gic-v2m.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
#include <linux/irqchip/arm-gic.h>
2727
#include <linux/irqchip/arm-gic-common.h>
2828

29-
#include "irq-msi-lib.h"
29+
#include <linux/irqchip/irq-msi-lib.h>
3030

3131
/*
3232
* MSI_TYPER:
@@ -261,23 +261,23 @@ static struct msi_parent_ops gicv2m_msi_parent_ops = {
261261

262262
static __init int gicv2m_allocate_domains(struct irq_domain *parent)
263263
{
264-
struct irq_domain *inner_domain;
264+
struct irq_domain_info info = {
265+
.ops = &gicv2m_domain_ops,
266+
.parent = parent,
267+
};
265268
struct v2m_data *v2m;
266269

267270
v2m = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
268271
if (!v2m)
269272
return 0;
270273

271-
inner_domain = irq_domain_create_hierarchy(parent, 0, 0, v2m->fwnode,
272-
&gicv2m_domain_ops, v2m);
273-
if (!inner_domain) {
274+
info.host_data = v2m;
275+
info.fwnode = v2m->fwnode;
276+
277+
if (!msi_create_parent_irq_domain(&info, &gicv2m_msi_parent_ops)) {
274278
pr_err("Failed to create GICv2m domain\n");
275279
return -ENOMEM;
276280
}
277-
278-
irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
279-
inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
280-
inner_domain->msi_parent_ops = &gicv2m_msi_parent_ops;
281281
return 0;
282282
}
283283

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

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <linux/pci.h>
99

1010
#include "irq-gic-common.h"
11-
#include "irq-msi-lib.h"
11+
#include <linux/irqchip/irq-msi-lib.h>
1212

1313
#define ITS_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
1414
MSI_FLAG_USE_DEF_CHIP_OPS | \
@@ -67,17 +67,6 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
6767
/* ITS specific DeviceID, as the core ITS ignores dev. */
6868
info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain->parent, pdev);
6969

70-
/*
71-
* @domain->msi_domain_info->hwsize contains the size of the
72-
* MSI[-X] domain, but vector allocation happens one by one. This
73-
* needs some thought when MSI comes into play as the size of MSI
74-
* might be unknown at domain creation time and therefore set to
75-
* MSI_MAX_INDEX.
76-
*/
77-
msi_info = msi_get_domain_info(domain);
78-
if (msi_info->hwsize > nvec)
79-
nvec = msi_info->hwsize;
80-
8170
/*
8271
* Always allocate a power of 2, and special case device 0 for
8372
* broken systems where the DevID is not wired (and all devices
@@ -118,6 +107,14 @@ static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev,
118107
index++;
119108
} while (!ret);
120109

110+
if (ret) {
111+
struct device_node *np = NULL;
112+
113+
ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id);
114+
if (np)
115+
of_node_put(np);
116+
}
117+
121118
return ret;
122119
}
123120

@@ -143,14 +140,6 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
143140
/* ITS specific DeviceID, as the core ITS ignores dev. */
144141
info->scratchpad[0].ul = dev_id;
145142

146-
/*
147-
* @domain->msi_domain_info->hwsize contains the size of the device
148-
* domain, but vector allocation happens one by one.
149-
*/
150-
msi_info = msi_get_domain_info(domain);
151-
if (msi_info->hwsize > nvec)
152-
nvec = msi_info->hwsize;
153-
154143
/* Allocate at least 32 MSIs, and always as a power of 2 */
155144
nvec = max_t(int, 32, roundup_pow_of_two(nvec));
156145

@@ -159,6 +148,14 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
159148
dev, nvec, info);
160149
}
161150

151+
static void its_msi_teardown(struct irq_domain *domain, msi_alloc_info_t *info)
152+
{
153+
struct msi_domain_info *msi_info;
154+
155+
msi_info = msi_get_domain_info(domain->parent);
156+
msi_info->ops->msi_teardown(domain->parent, info);
157+
}
158+
162159
static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
163160
struct irq_domain *real_parent, struct msi_domain_info *info)
164161
{
@@ -182,6 +179,7 @@ static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
182179
* %MSI_MAX_INDEX.
183180
*/
184181
info->ops->msi_prepare = its_pci_msi_prepare;
182+
info->ops->msi_teardown = its_msi_teardown;
185183
break;
186184
case DOMAIN_BUS_DEVICE_MSI:
187185
case DOMAIN_BUS_WIRED_TO_MSI:
@@ -190,6 +188,7 @@ static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
190188
* size is also known at domain creation time.
191189
*/
192190
info->ops->msi_prepare = its_pmsi_prepare;
191+
info->ops->msi_teardown = its_msi_teardown;
193192
break;
194193
default:
195194
/* Confused. How did the lib return true? */

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

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
#include <asm/exception.h>
4242

4343
#include "irq-gic-common.h"
44-
#include "irq-msi-lib.h"
44+
#include <linux/irqchip/irq-msi-lib.h>
4545

4646
#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0)
4747
#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1)
@@ -3624,8 +3624,33 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
36243624
return err;
36253625
}
36263626

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

36313656
static int its_irq_gic_domain_alloc(struct irq_domain *domain,
@@ -3726,7 +3751,6 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
37263751
{
37273752
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
37283753
struct its_device *its_dev = irq_data_get_irq_chip_data(d);
3729-
struct its_node *its = its_dev->its;
37303754
int i;
37313755

37323756
bitmap_release_region(its_dev->event_map.lpi_map,
@@ -3740,26 +3764,6 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
37403764
irq_domain_reset_irq_data(data);
37413765
}
37423766

3743-
mutex_lock(&its->dev_alloc_lock);
3744-
3745-
/*
3746-
* If all interrupts have been freed, start mopping the
3747-
* floor. This is conditioned on the device not being shared.
3748-
*/
3749-
if (!its_dev->shared &&
3750-
bitmap_empty(its_dev->event_map.lpi_map,
3751-
its_dev->event_map.nr_lpis)) {
3752-
its_lpi_free(its_dev->event_map.lpi_map,
3753-
its_dev->event_map.lpi_base,
3754-
its_dev->event_map.nr_lpis);
3755-
3756-
/* Unmap device/itt */
3757-
its_send_mapd(its_dev, 0);
3758-
its_free_device(its_dev);
3759-
}
3760-
3761-
mutex_unlock(&its->dev_alloc_lock);
3762-
37633767
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
37643768
}
37653769

@@ -5122,7 +5126,12 @@ static void __init __iomem *its_map_one(struct resource *res, int *err)
51225126

51235127
static int its_init_domain(struct its_node *its)
51245128
{
5125-
struct irq_domain *inner_domain;
5129+
struct irq_domain_info dom_info = {
5130+
.fwnode = its->fwnode_handle,
5131+
.ops = &its_domain_ops,
5132+
.domain_flags = its->msi_domain_flags,
5133+
.parent = its_parent,
5134+
};
51265135
struct msi_domain_info *info;
51275136

51285137
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -5131,21 +5140,12 @@ static int its_init_domain(struct its_node *its)
51315140

51325141
info->ops = &its_msi_domain_ops;
51335142
info->data = its;
5143+
dom_info.host_data = info;
51345144

5135-
inner_domain = irq_domain_create_hierarchy(its_parent,
5136-
its->msi_domain_flags, 0,
5137-
its->fwnode_handle, &its_domain_ops,
5138-
info);
5139-
if (!inner_domain) {
5145+
if (!msi_create_parent_irq_domain(&dom_info, &gic_v3_its_msi_parent_ops)) {
51405146
kfree(info);
51415147
return -ENOMEM;
51425148
}
5143-
5144-
irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
5145-
5146-
inner_domain->msi_parent_ops = &gic_v3_its_msi_parent_ops;
5147-
inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
5148-
51495149
return 0;
51505150
}
51515151

@@ -5522,7 +5522,7 @@ static struct its_node __init *its_node_init(struct resource *res,
55225522
its->base = its_base;
55235523
its->phys_base = res->start;
55245524
its->get_msi_base = its_irq_get_msi_base;
5525-
its->msi_domain_flags = IRQ_DOMAIN_FLAG_ISOLATED_MSI;
5525+
its->msi_domain_flags = IRQ_DOMAIN_FLAG_ISOLATED_MSI | IRQ_DOMAIN_FLAG_MSI_IMMUTABLE;
55265526

55275527
its->numa_node = numa_node;
55285528
its->fwnode_handle = handle;

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

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
#include <linux/irqchip/arm-gic-v3.h>
2020

21-
#include "irq-msi-lib.h"
21+
#include <linux/irqchip/irq-msi-lib.h>
2222

2323
struct mbi_range {
2424
u32 spi_start;
@@ -206,17 +206,13 @@ static const struct msi_parent_ops gic_v3_mbi_msi_parent_ops = {
206206

207207
static int mbi_allocate_domain(struct irq_domain *parent)
208208
{
209-
struct irq_domain *nexus_domain;
209+
struct irq_domain_info info = {
210+
.fwnode = parent->fwnode,
211+
.ops = &mbi_domain_ops,
212+
.parent = parent,
213+
};
210214

211-
nexus_domain = irq_domain_create_hierarchy(parent, 0, 0, parent->fwnode,
212-
&mbi_domain_ops, NULL);
213-
if (!nexus_domain)
214-
return -ENOMEM;
215-
216-
irq_domain_update_bus_token(nexus_domain, DOMAIN_BUS_NEXUS);
217-
nexus_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
218-
nexus_domain->msi_parent_ops = &gic_v3_mbi_msi_parent_ops;
219-
return 0;
215+
return msi_create_parent_irq_domain(&info, &gic_v3_mbi_msi_parent_ops) ? 0 : -ENOMEM;
220216
}
221217

222218
int __init mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent)

drivers/irqchip/irq-imx-mu-msi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#include <linux/pm_domain.h>
2525
#include <linux/spinlock.h>
2626

27-
#include "irq-msi-lib.h"
27+
#include <linux/irqchip/irq-msi-lib.h>
2828

2929
#define IMX_MU_CHANS 4
3030

0 commit comments

Comments
 (0)