Skip to content

Commit d96a828

Browse files
author
Myron Stowe
committed
genirq/msi: Move prepare() call to per-device allocation
JIRA: https://issues.redhat.com/browse/RHEL-120705 Upstream Status: 1396e89 commit 1396e89 Author: Marc Zyngier <maz@kernel.org> Date: Tue May 13 17:31:42 2025 +0100 genirq/msi: Move prepare() call to per-device allocation The current device MSI infrastructure is subtly broken, as it will issue an .msi_prepare() callback into the MSI controller driver every time it needs to allocate an MSI. That's pretty wrong, as the contract (or unwarranted assumption, depending who you ask) between the MSI controller and the core code is that .msi_prepare() is called exactly once per device. This leads to some subtle breakage in some MSI controller drivers, as it gives the impression that there are multiple endpoints sharing a bus identifier (RID in PCI parlance, DID for GICv3+). It implies that whatever allocation the ITS driver (for example) has done on behalf of these devices cannot be undone, as there is no way to track the shared state. This is particularly bad for wire-MSI devices, for which .msi_prepare() is called for each input line. To address this issue, move the call to .msi_prepare() to take place at the point of irq domain allocation, which is the only place that makes sense. The msi_alloc_info_t structure is made part of the msi_domain_template, so that its life-cycle is that of the domain as well. Finally, the msi_info::alloc_data field is made to point at this allocation tracking structure, ensuring that it is carried around the block. This is all pretty straightforward, except for the non-device-MSI leftovers, which still have to call .msi_prepare() at the old spot. One day... Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/all/20250513163144.2215824-4-maz@kernel.org Signed-off-by: Myron Stowe <mstowe@redhat.com>
1 parent c891f08 commit d96a828

File tree

2 files changed

+33
-4
lines changed

2 files changed

+33
-4
lines changed

include/linux/msi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,12 +516,14 @@ struct msi_domain_info {
516516
* @chip: Interrupt chip for this domain
517517
* @ops: MSI domain ops
518518
* @info: MSI domain info data
519+
* @alloc_info: MSI domain allocation data (architecture specific)
519520
*/
520521
struct msi_domain_template {
521522
char name[48];
522523
struct irq_chip chip;
523524
struct msi_domain_ops ops;
524525
struct msi_domain_info info;
526+
msi_alloc_info_t alloc_info;
525527
};
526528

527529
/*

kernel/irq/msi.c

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ struct msi_ctrl {
5858
static void msi_domain_free_locked(struct device *dev, struct msi_ctrl *ctrl);
5959
static unsigned int msi_domain_get_hwsize(struct device *dev, unsigned int domid);
6060
static inline int msi_sysfs_create_group(struct device *dev);
61-
61+
static int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
62+
int nvec, msi_alloc_info_t *arg);
6263

6364
/**
6465
* msi_alloc_desc - Allocate an initialized msi_desc
@@ -1004,6 +1005,7 @@ bool msi_create_device_irq_domain(struct device *dev, unsigned int domid,
10041005
bundle->info.ops = &bundle->ops;
10051006
bundle->info.data = domain_data;
10061007
bundle->info.chip_data = chip_data;
1008+
bundle->info.alloc_data = &bundle->alloc_info;
10071009

10081010
pops = parent->msi_parent_ops;
10091011
snprintf(bundle->name, sizeof(bundle->name), "%s%s-%s",
@@ -1042,11 +1044,18 @@ bool msi_create_device_irq_domain(struct device *dev, unsigned int domid,
10421044
if (!domain)
10431045
return false;
10441046

1047+
domain->dev = dev;
1048+
dev->msi.data->__domains[domid].domain = domain;
1049+
1050+
if (msi_domain_prepare_irqs(domain, dev, hwsize, &bundle->alloc_info)) {
1051+
dev->msi.data->__domains[domid].domain = NULL;
1052+
irq_domain_remove(domain);
1053+
return false;
1054+
}
1055+
10451056
/* @bundle and @fwnode_alloced are now in use. Prevent cleanup */
10461057
retain_and_null_ptr(bundle);
10471058
retain_and_null_ptr(fwnode_alloced);
1048-
domain->dev = dev;
1049-
dev->msi.data->__domains[domid].domain = domain;
10501059
return true;
10511060
}
10521061

@@ -1213,6 +1222,24 @@ static int msi_init_virq(struct irq_domain *domain, int virq, unsigned int vflag
12131222
return 0;
12141223
}
12151224

1225+
static int populate_alloc_info(struct irq_domain *domain, struct device *dev,
1226+
unsigned int nirqs, msi_alloc_info_t *arg)
1227+
{
1228+
struct msi_domain_info *info = domain->host_data;
1229+
1230+
/*
1231+
* If the caller has provided a template alloc info, use that. Once
1232+
* all users of msi_create_irq_domain() have been eliminated, this
1233+
* should be the only source of allocation information, and the
1234+
* prepare call below should be finally removed.
1235+
*/
1236+
if (!info->alloc_data)
1237+
return msi_domain_prepare_irqs(domain, dev, nirqs, arg);
1238+
1239+
*arg = *info->alloc_data;
1240+
return 0;
1241+
}
1242+
12161243
static int __msi_domain_alloc_irqs(struct device *dev, struct irq_domain *domain,
12171244
struct msi_ctrl *ctrl)
12181245
{
@@ -1225,7 +1252,7 @@ static int __msi_domain_alloc_irqs(struct device *dev, struct irq_domain *domain
12251252
unsigned long idx;
12261253
int i, ret, virq;
12271254

1228-
ret = msi_domain_prepare_irqs(domain, dev, ctrl->nirqs, &arg);
1255+
ret = populate_alloc_info(domain, dev, ctrl->nirqs, &arg);
12291256
if (ret)
12301257
return ret;
12311258

0 commit comments

Comments
 (0)