mirror of
https://github.com/ukui/kernel.git
synced 2026-03-09 10:07:04 -07:00
PCI: Provide sensible IRQ vector alloc/free routines
Add a function to allocate and free a range of interrupt vectors, using MSI-X, MSI or legacy vectors (in that order) based on the capabilities of the underlying device and PCIe complex. Additionally a new helper is provided to get the Linux IRQ number for given device-relative vector so that the drivers don't need to allocate their own arrays to keep track of the vectors for the multi vector MSI-X case. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Alexander Gordeev <agordeev@redhat.com>
This commit is contained in:
committed by
Bjorn Helgaas
parent
3ac020e0ca
commit
aff171641d
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2003-2004 Intel
|
* Copyright (C) 2003-2004 Intel
|
||||||
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
|
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
|
||||||
|
* Copyright (C) 2016 Christoph Hellwig.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
@@ -1121,6 +1122,94 @@ int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(pci_enable_msix_range);
|
EXPORT_SYMBOL(pci_enable_msix_range);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_alloc_irq_vectors - allocate multiple IRQs for a device
|
||||||
|
* @dev: PCI device to operate on
|
||||||
|
* @min_vecs: minimum number of vectors required (must be >= 1)
|
||||||
|
* @max_vecs: maximum (desired) number of vectors
|
||||||
|
* @flags: flags or quirks for the allocation
|
||||||
|
*
|
||||||
|
* Allocate up to @max_vecs interrupt vectors for @dev, using MSI-X or MSI
|
||||||
|
* vectors if available, and fall back to a single legacy vector
|
||||||
|
* if neither is available. Return the number of vectors allocated,
|
||||||
|
* (which might be smaller than @max_vecs) if successful, or a negative
|
||||||
|
* error code on error. If less than @min_vecs interrupt vectors are
|
||||||
|
* available for @dev the function will fail with -ENOSPC.
|
||||||
|
*
|
||||||
|
* To get the Linux IRQ number used for a vector that can be passed to
|
||||||
|
* request_irq() use the pci_irq_vector() helper.
|
||||||
|
*/
|
||||||
|
int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
|
||||||
|
unsigned int max_vecs, unsigned int flags)
|
||||||
|
{
|
||||||
|
int vecs = -ENOSPC;
|
||||||
|
|
||||||
|
if (!(flags & PCI_IRQ_NOMSIX)) {
|
||||||
|
vecs = pci_enable_msix_range(dev, NULL, min_vecs, max_vecs);
|
||||||
|
if (vecs > 0)
|
||||||
|
return vecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags & PCI_IRQ_NOMSI)) {
|
||||||
|
vecs = pci_enable_msi_range(dev, min_vecs, max_vecs);
|
||||||
|
if (vecs > 0)
|
||||||
|
return vecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* use legacy irq if allowed */
|
||||||
|
if (!(flags & PCI_IRQ_NOLEGACY) && min_vecs == 1)
|
||||||
|
return 1;
|
||||||
|
return vecs;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(pci_alloc_irq_vectors);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_free_irq_vectors - free previously allocated IRQs for a device
|
||||||
|
* @dev: PCI device to operate on
|
||||||
|
*
|
||||||
|
* Undoes the allocations and enabling in pci_alloc_irq_vectors().
|
||||||
|
*/
|
||||||
|
void pci_free_irq_vectors(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
pci_disable_msix(dev);
|
||||||
|
pci_disable_msi(dev);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(pci_free_irq_vectors);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_irq_vector - return Linux IRQ number of a device vector
|
||||||
|
* @dev: PCI device to operate on
|
||||||
|
* @nr: device-relative interrupt vector index (0-based).
|
||||||
|
*/
|
||||||
|
int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
|
||||||
|
{
|
||||||
|
if (dev->msix_enabled) {
|
||||||
|
struct msi_desc *entry;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for_each_pci_msi_entry(entry, dev) {
|
||||||
|
if (i == nr)
|
||||||
|
return entry->irq;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev->msi_enabled) {
|
||||||
|
struct msi_desc *entry = first_pci_msi_entry(dev);
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(nr >= entry->nvec_used))
|
||||||
|
return -EINVAL;
|
||||||
|
} else {
|
||||||
|
if (WARN_ON_ONCE(nr > 0))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev->irq + nr;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(pci_irq_vector);
|
||||||
|
|
||||||
struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
|
struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
|
||||||
{
|
{
|
||||||
return to_pci_dev(desc->dev);
|
return to_pci_dev(desc->dev);
|
||||||
|
|||||||
@@ -1237,6 +1237,10 @@ resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno);
|
|||||||
int pci_set_vga_state(struct pci_dev *pdev, bool decode,
|
int pci_set_vga_state(struct pci_dev *pdev, bool decode,
|
||||||
unsigned int command_bits, u32 flags);
|
unsigned int command_bits, u32 flags);
|
||||||
|
|
||||||
|
#define PCI_IRQ_NOLEGACY (1 << 0) /* don't use legacy interrupts */
|
||||||
|
#define PCI_IRQ_NOMSI (1 << 1) /* don't use MSI interrupts */
|
||||||
|
#define PCI_IRQ_NOMSIX (1 << 2) /* don't use MSI-X interrupts */
|
||||||
|
|
||||||
/* kmem_cache style wrapper around pci_alloc_consistent() */
|
/* kmem_cache style wrapper around pci_alloc_consistent() */
|
||||||
|
|
||||||
#include <linux/pci-dma.h>
|
#include <linux/pci-dma.h>
|
||||||
@@ -1284,6 +1288,11 @@ static inline int pci_enable_msix_exact(struct pci_dev *dev,
|
|||||||
return rc;
|
return rc;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
|
||||||
|
unsigned int max_vecs, unsigned int flags);
|
||||||
|
void pci_free_irq_vectors(struct pci_dev *dev);
|
||||||
|
int pci_irq_vector(struct pci_dev *dev, unsigned int nr);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static inline int pci_msi_vec_count(struct pci_dev *dev) { return -ENOSYS; }
|
static inline int pci_msi_vec_count(struct pci_dev *dev) { return -ENOSYS; }
|
||||||
static inline void pci_msi_shutdown(struct pci_dev *dev) { }
|
static inline void pci_msi_shutdown(struct pci_dev *dev) { }
|
||||||
@@ -1307,6 +1316,24 @@ static inline int pci_enable_msix_range(struct pci_dev *dev,
|
|||||||
static inline int pci_enable_msix_exact(struct pci_dev *dev,
|
static inline int pci_enable_msix_exact(struct pci_dev *dev,
|
||||||
struct msix_entry *entries, int nvec)
|
struct msix_entry *entries, int nvec)
|
||||||
{ return -ENOSYS; }
|
{ return -ENOSYS; }
|
||||||
|
static inline int pci_alloc_irq_vectors(struct pci_dev *dev,
|
||||||
|
unsigned int min_vecs, unsigned int max_vecs,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
if (min_vecs > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
static inline void pci_free_irq_vectors(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
|
||||||
|
{
|
||||||
|
if (WARN_ON_ONCE(nr > 0))
|
||||||
|
return -EINVAL;
|
||||||
|
return dev->irq;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PCIEPORTBUS
|
#ifdef CONFIG_PCIEPORTBUS
|
||||||
|
|||||||
Reference in New Issue
Block a user