Skip to content

Commit 3402636

Browse files
committed
PCI/MSI: Provide post-enable dynamic allocation interfaces for MSI-X
MSI-X vectors can be allocated after the initial MSI-X enablement, but this needs explicit support of the underlying interrupt domains. Provide a function to query the ability and functions to allocate/free individual vectors post-enable. The allocation can either request a specific index in the MSI-X table or with the index argument MSI_ANY_INDEX it allocates the next free vector. The return value is a struct msi_map which on success contains both index and the Linux interrupt number. In case of failure index is negative and the Linux interrupt number is 0. The allocation function is for a single MSI-X index at a time as that's sufficient for the most urgent use case VFIO to get rid of the 'disable MSI-X, reallocate, enable-MSI-X' cycle which is prone to lost interrupts and redirections to the legacy and obviously unhandled INTx. As single index allocation is also sufficient for the use cases Jason Gunthorpe pointed out: Allocation of a MSI-X or IMS vector for a network queue. See Link below. Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Kevin Tian <[email protected]> Acked-by: Bjorn Helgaas <[email protected]> Acked-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/all/[email protected] Link: https://lore.kernel.org/r/[email protected]
1 parent 73bd063 commit 3402636

File tree

3 files changed

+75
-1
lines changed

3 files changed

+75
-1
lines changed

drivers/pci/msi/api.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,73 @@ int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
112112
}
113113
EXPORT_SYMBOL(pci_enable_msix_range);
114114

115+
/**
116+
* pci_msix_can_alloc_dyn - Query whether dynamic allocation after enabling
117+
* MSI-X is supported
118+
*
119+
* @dev: PCI device to operate on
120+
*
121+
* Return: True if supported, false otherwise
122+
*/
123+
bool pci_msix_can_alloc_dyn(struct pci_dev *dev)
124+
{
125+
if (!dev->msix_cap)
126+
return false;
127+
128+
return pci_msi_domain_supports(dev, MSI_FLAG_PCI_MSIX_ALLOC_DYN, DENY_LEGACY);
129+
}
130+
EXPORT_SYMBOL_GPL(pci_msix_can_alloc_dyn);
131+
132+
/**
133+
* pci_msix_alloc_irq_at - Allocate an MSI-X interrupt after enabling MSI-X
134+
* at a given MSI-X vector index or any free vector index
135+
*
136+
* @dev: PCI device to operate on
137+
* @index: Index to allocate. If @index == MSI_ANY_INDEX this allocates
138+
* the next free index in the MSI-X table
139+
* @affdesc: Optional pointer to an affinity descriptor structure. NULL otherwise
140+
*
141+
* Return: A struct msi_map
142+
*
143+
* On success msi_map::index contains the allocated index (>= 0) and
144+
* msi_map::virq contains the allocated Linux interrupt number (> 0).
145+
*
146+
* On fail msi_map::index contains the error code and msi_map::virq
147+
* is set to 0.
148+
*/
149+
struct msi_map pci_msix_alloc_irq_at(struct pci_dev *dev, unsigned int index,
150+
const struct irq_affinity_desc *affdesc)
151+
{
152+
struct msi_map map = { .index = -ENOTSUPP };
153+
154+
if (!dev->msix_enabled)
155+
return map;
156+
157+
if (!pci_msix_can_alloc_dyn(dev))
158+
return map;
159+
160+
return msi_domain_alloc_irq_at(&dev->dev, MSI_DEFAULT_DOMAIN, index, affdesc, NULL);
161+
}
162+
EXPORT_SYMBOL_GPL(pci_msix_alloc_irq_at);
163+
164+
/**
165+
* pci_msix_free_irq - Free an interrupt on a PCI/MSIX interrupt domain
166+
* which was allocated via pci_msix_alloc_irq_at()
167+
*
168+
* @dev: The PCI device to operate on
169+
* @map: A struct msi_map describing the interrupt to free
170+
* as returned from the allocation function.
171+
*/
172+
void pci_msix_free_irq(struct pci_dev *dev, struct msi_map map)
173+
{
174+
if (WARN_ON_ONCE(map.index < 0 || map.virq <= 0))
175+
return;
176+
if (WARN_ON_ONCE(!pci_msix_can_alloc_dyn(dev)))
177+
return;
178+
msi_domain_free_irqs_range(&dev->dev, MSI_DEFAULT_DOMAIN, map.index, map.index);
179+
}
180+
EXPORT_SYMBOL_GPL(pci_msix_free_irq);
181+
115182
/**
116183
* pci_disable_msix() - Disable MSI-X interrupt mode on device
117184
* @dev: the PCI device to operate on

drivers/pci/msi/irqdomain.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ static const struct msi_domain_template pci_msix_template = {
225225
},
226226

227227
.info = {
228-
.flags = MSI_COMMON_FLAGS | MSI_FLAG_PCI_MSIX,
228+
.flags = MSI_COMMON_FLAGS | MSI_FLAG_PCI_MSIX |
229+
MSI_FLAG_PCI_MSIX_ALLOC_DYN,
229230
.bus_token = DOMAIN_BUS_PCI_DEVICE_MSIX,
230231
},
231232
};

include/linux/pci.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <linux/interrupt.h>
3939
#include <linux/io.h>
4040
#include <linux/resource_ext.h>
41+
#include <linux/msi_api.h>
4142
#include <uapi/linux/pci.h>
4243

4344
#include <linux/pci_ids.h>
@@ -1559,6 +1560,11 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs,
15591560
unsigned int max_vecs, unsigned int flags,
15601561
struct irq_affinity *affd);
15611562

1563+
bool pci_msix_can_alloc_dyn(struct pci_dev *dev);
1564+
struct msi_map pci_msix_alloc_irq_at(struct pci_dev *dev, unsigned int index,
1565+
const struct irq_affinity_desc *affdesc);
1566+
void pci_msix_free_irq(struct pci_dev *pdev, struct msi_map map);
1567+
15621568
void pci_free_irq_vectors(struct pci_dev *dev);
15631569
int pci_irq_vector(struct pci_dev *dev, unsigned int nr);
15641570
const struct cpumask *pci_irq_get_affinity(struct pci_dev *pdev, int vec);

0 commit comments

Comments
 (0)