Merge branch 'pci/msi' into next
* pci/msi: vfio: Use pci_enable_msi_range() and pci_enable_msix_range() ahci: Use pci_enable_msi_range() instead of pci_enable_msi_block() ahci: Fix broken fallback to single MSI mode PCI/MSI: Add pci_enable_msi_exact() and pci_enable_msix_exact() PCI/MSI: Fix cut-and-paste errors in documentation PCI/MSI: Add pci_enable_msi() documentation back PCI/MSI: Fix pci_msix_vec_count() htmldocs failure PCI/MSI: Fix leak of msi_attrs PCI/MSI: Check kmalloc() return value, fix leak of name
This commit is contained in:
commit
c128856b48
5 changed files with 155 additions and 26 deletions
|
|
@ -82,7 +82,19 @@ Most of the hard work is done for the driver in the PCI layer. It simply
|
||||||
has to request that the PCI layer set up the MSI capability for this
|
has to request that the PCI layer set up the MSI capability for this
|
||||||
device.
|
device.
|
||||||
|
|
||||||
4.2.1 pci_enable_msi_range
|
4.2.1 pci_enable_msi
|
||||||
|
|
||||||
|
int pci_enable_msi(struct pci_dev *dev)
|
||||||
|
|
||||||
|
A successful call allocates ONE interrupt to the device, regardless
|
||||||
|
of how many MSIs the device supports. The device is switched from
|
||||||
|
pin-based interrupt mode to MSI mode. The dev->irq number is changed
|
||||||
|
to a new number which represents the message signaled interrupt;
|
||||||
|
consequently, this function should be called before the driver calls
|
||||||
|
request_irq(), because an MSI is delivered via a vector that is
|
||||||
|
different from the vector of a pin-based interrupt.
|
||||||
|
|
||||||
|
4.2.2 pci_enable_msi_range
|
||||||
|
|
||||||
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
|
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
|
||||||
|
|
||||||
|
|
@ -147,6 +159,11 @@ static int foo_driver_enable_msi(struct pci_dev *pdev, int nvec)
|
||||||
return pci_enable_msi_range(pdev, nvec, nvec);
|
return pci_enable_msi_range(pdev, nvec, nvec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Note, unlike pci_enable_msi_exact() function, which could be also used to
|
||||||
|
enable a particular number of MSI-X interrupts, pci_enable_msi_range()
|
||||||
|
returns either a negative errno or 'nvec' (not negative errno or 0 - as
|
||||||
|
pci_enable_msi_exact() does).
|
||||||
|
|
||||||
4.2.1.3 Single MSI mode
|
4.2.1.3 Single MSI mode
|
||||||
|
|
||||||
The most notorious example of the request type described above is
|
The most notorious example of the request type described above is
|
||||||
|
|
@ -158,7 +175,27 @@ static int foo_driver_enable_single_msi(struct pci_dev *pdev)
|
||||||
return pci_enable_msi_range(pdev, 1, 1);
|
return pci_enable_msi_range(pdev, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
4.2.2 pci_disable_msi
|
Note, unlike pci_enable_msi() function, which could be also used to
|
||||||
|
enable the single MSI mode, pci_enable_msi_range() returns either a
|
||||||
|
negative errno or 1 (not negative errno or 0 - as pci_enable_msi()
|
||||||
|
does).
|
||||||
|
|
||||||
|
4.2.3 pci_enable_msi_exact
|
||||||
|
|
||||||
|
int pci_enable_msi_exact(struct pci_dev *dev, int nvec)
|
||||||
|
|
||||||
|
This variation on pci_enable_msi_range() call allows a device driver to
|
||||||
|
request exactly 'nvec' MSIs.
|
||||||
|
|
||||||
|
If this function returns a negative number, it indicates an error and
|
||||||
|
the driver should not attempt to request any more MSI interrupts for
|
||||||
|
this device.
|
||||||
|
|
||||||
|
By contrast with pci_enable_msi_range() function, pci_enable_msi_exact()
|
||||||
|
returns zero in case of success, which indicates MSI interrupts have been
|
||||||
|
successfully allocated.
|
||||||
|
|
||||||
|
4.2.4 pci_disable_msi
|
||||||
|
|
||||||
void pci_disable_msi(struct pci_dev *dev)
|
void pci_disable_msi(struct pci_dev *dev)
|
||||||
|
|
||||||
|
|
@ -172,7 +209,7 @@ on any interrupt for which it previously called request_irq().
|
||||||
Failure to do so results in a BUG_ON(), leaving the device with
|
Failure to do so results in a BUG_ON(), leaving the device with
|
||||||
MSI enabled and thus leaking its vector.
|
MSI enabled and thus leaking its vector.
|
||||||
|
|
||||||
4.2.3 pci_msi_vec_count
|
4.2.4 pci_msi_vec_count
|
||||||
|
|
||||||
int pci_msi_vec_count(struct pci_dev *dev)
|
int pci_msi_vec_count(struct pci_dev *dev)
|
||||||
|
|
||||||
|
|
@ -257,7 +294,7 @@ possible, likely up to the limit returned by pci_msix_vec_count() function:
|
||||||
|
|
||||||
static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec)
|
static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec)
|
||||||
{
|
{
|
||||||
return pci_enable_msi_range(adapter->pdev, adapter->msix_entries,
|
return pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
|
||||||
1, nvec);
|
1, nvec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,7 +306,7 @@ In this case the function could look like this:
|
||||||
|
|
||||||
static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec)
|
static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec)
|
||||||
{
|
{
|
||||||
return pci_enable_msi_range(adapter->pdev, adapter->msix_entries,
|
return pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
|
||||||
FOO_DRIVER_MINIMUM_NVEC, nvec);
|
FOO_DRIVER_MINIMUM_NVEC, nvec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,10 +319,15 @@ parameters:
|
||||||
|
|
||||||
static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec)
|
static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec)
|
||||||
{
|
{
|
||||||
return pci_enable_msi_range(adapter->pdev, adapter->msix_entries,
|
return pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
|
||||||
nvec, nvec);
|
nvec, nvec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Note, unlike pci_enable_msix_exact() function, which could be also used to
|
||||||
|
enable a particular number of MSI-X interrupts, pci_enable_msix_range()
|
||||||
|
returns either a negative errno or 'nvec' (not negative errno or 0 - as
|
||||||
|
pci_enable_msix_exact() does).
|
||||||
|
|
||||||
4.3.1.3 Specific requirements to the number of MSI-X interrupts
|
4.3.1.3 Specific requirements to the number of MSI-X interrupts
|
||||||
|
|
||||||
As noted above, there could be devices that can not operate with just any
|
As noted above, there could be devices that can not operate with just any
|
||||||
|
|
@ -332,7 +374,64 @@ Note how pci_enable_msix_range() return value is analized for a fallback -
|
||||||
any error code other than -ENOSPC indicates a fatal error and should not
|
any error code other than -ENOSPC indicates a fatal error and should not
|
||||||
be retried.
|
be retried.
|
||||||
|
|
||||||
4.3.2 pci_disable_msix
|
4.3.2 pci_enable_msix_exact
|
||||||
|
|
||||||
|
int pci_enable_msix_exact(struct pci_dev *dev,
|
||||||
|
struct msix_entry *entries, int nvec)
|
||||||
|
|
||||||
|
This variation on pci_enable_msix_range() call allows a device driver to
|
||||||
|
request exactly 'nvec' MSI-Xs.
|
||||||
|
|
||||||
|
If this function returns a negative number, it indicates an error and
|
||||||
|
the driver should not attempt to allocate any more MSI-X interrupts for
|
||||||
|
this device.
|
||||||
|
|
||||||
|
By contrast with pci_enable_msix_range() function, pci_enable_msix_exact()
|
||||||
|
returns zero in case of success, which indicates MSI-X interrupts have been
|
||||||
|
successfully allocated.
|
||||||
|
|
||||||
|
Another version of a routine that enables MSI-X mode for a device with
|
||||||
|
specific requirements described in chapter 4.3.1.3 might look like this:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assume 'minvec' and 'maxvec' are non-zero
|
||||||
|
*/
|
||||||
|
static int foo_driver_enable_msix(struct foo_adapter *adapter,
|
||||||
|
int minvec, int maxvec)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
minvec = roundup_pow_of_two(minvec);
|
||||||
|
maxvec = rounddown_pow_of_two(maxvec);
|
||||||
|
|
||||||
|
if (minvec > maxvec)
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
rc = pci_enable_msix_exact(adapter->pdev,
|
||||||
|
adapter->msix_entries, maxvec);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -ENOSPC is the only error code allowed to be analyzed
|
||||||
|
*/
|
||||||
|
if (rc == -ENOSPC) {
|
||||||
|
if (maxvec == 1)
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
|
maxvec /= 2;
|
||||||
|
|
||||||
|
if (minvec > maxvec)
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
|
goto retry;
|
||||||
|
} else if (rc < 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxvec;
|
||||||
|
}
|
||||||
|
|
||||||
|
4.3.3 pci_disable_msix
|
||||||
|
|
||||||
void pci_disable_msix(struct pci_dev *dev)
|
void pci_disable_msix(struct pci_dev *dev)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1151,13 +1151,13 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
|
||||||
static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
|
static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
|
||||||
struct ahci_host_priv *hpriv)
|
struct ahci_host_priv *hpriv)
|
||||||
{
|
{
|
||||||
int rc, nvec;
|
int nvec;
|
||||||
|
|
||||||
if (hpriv->flags & AHCI_HFLAG_NO_MSI)
|
if (hpriv->flags & AHCI_HFLAG_NO_MSI)
|
||||||
goto intx;
|
goto intx;
|
||||||
|
|
||||||
rc = pci_msi_vec_count(pdev);
|
nvec = pci_msi_vec_count(pdev);
|
||||||
if (rc < 0)
|
if (nvec < 0)
|
||||||
goto intx;
|
goto intx;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1165,19 +1165,19 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
|
||||||
* Message mode could be enforced. In this case assume that advantage
|
* Message mode could be enforced. In this case assume that advantage
|
||||||
* of multipe MSIs is negated and use single MSI mode instead.
|
* of multipe MSIs is negated and use single MSI mode instead.
|
||||||
*/
|
*/
|
||||||
if (rc < n_ports)
|
if (nvec < n_ports)
|
||||||
goto single_msi;
|
goto single_msi;
|
||||||
|
|
||||||
nvec = rc;
|
nvec = pci_enable_msi_range(pdev, nvec, nvec);
|
||||||
rc = pci_enable_msi_block(pdev, nvec);
|
if (nvec == -ENOSPC)
|
||||||
if (rc)
|
goto single_msi;
|
||||||
|
else if (nvec < 0)
|
||||||
goto intx;
|
goto intx;
|
||||||
|
|
||||||
return nvec;
|
return nvec;
|
||||||
|
|
||||||
single_msi:
|
single_msi:
|
||||||
rc = pci_enable_msi(pdev);
|
if (pci_enable_msi(pdev))
|
||||||
if (rc)
|
|
||||||
goto intx;
|
goto intx;
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -545,9 +545,15 @@ static int populate_msi_sysfs(struct pci_dev *pdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
list_for_each_entry(entry, &pdev->msi_list, list) {
|
list_for_each_entry(entry, &pdev->msi_list, list) {
|
||||||
char *name = kmalloc(20, GFP_KERNEL);
|
char *name = kmalloc(20, GFP_KERNEL);
|
||||||
msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
|
if (!name)
|
||||||
if (!msi_dev_attr)
|
|
||||||
goto error_attrs;
|
goto error_attrs;
|
||||||
|
|
||||||
|
msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
|
||||||
|
if (!msi_dev_attr) {
|
||||||
|
kfree(name);
|
||||||
|
goto error_attrs;
|
||||||
|
}
|
||||||
|
|
||||||
sprintf(name, "%d", entry->irq);
|
sprintf(name, "%d", entry->irq);
|
||||||
sysfs_attr_init(&msi_dev_attr->attr);
|
sysfs_attr_init(&msi_dev_attr->attr);
|
||||||
msi_dev_attr->attr.name = name;
|
msi_dev_attr->attr.name = name;
|
||||||
|
|
@ -589,6 +595,7 @@ error_attrs:
|
||||||
++count;
|
++count;
|
||||||
msi_attr = msi_attrs[count];
|
msi_attr = msi_attrs[count];
|
||||||
}
|
}
|
||||||
|
kfree(msi_attrs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -959,7 +966,6 @@ EXPORT_SYMBOL(pci_disable_msi);
|
||||||
/**
|
/**
|
||||||
* pci_msix_vec_count - return the number of device's MSI-X table entries
|
* pci_msix_vec_count - return the number of device's MSI-X table entries
|
||||||
* @dev: pointer to the pci_dev data structure of MSI-X device function
|
* @dev: pointer to the pci_dev data structure of MSI-X device function
|
||||||
|
|
||||||
* This function returns the number of device's MSI-X table entries and
|
* This function returns the number of device's MSI-X table entries and
|
||||||
* therefore the number of MSI-X vectors device is capable of sending.
|
* therefore the number of MSI-X vectors device is capable of sending.
|
||||||
* It returns a negative errno if the device is not capable of sending MSI-X
|
* It returns a negative errno if the device is not capable of sending MSI-X
|
||||||
|
|
|
||||||
|
|
@ -482,15 +482,19 @@ static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix)
|
||||||
for (i = 0; i < nvec; i++)
|
for (i = 0; i < nvec; i++)
|
||||||
vdev->msix[i].entry = i;
|
vdev->msix[i].entry = i;
|
||||||
|
|
||||||
ret = pci_enable_msix(pdev, vdev->msix, nvec);
|
ret = pci_enable_msix_range(pdev, vdev->msix, 1, nvec);
|
||||||
if (ret) {
|
if (ret < nvec) {
|
||||||
|
if (ret > 0)
|
||||||
|
pci_disable_msix(pdev);
|
||||||
kfree(vdev->msix);
|
kfree(vdev->msix);
|
||||||
kfree(vdev->ctx);
|
kfree(vdev->ctx);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = pci_enable_msi_block(pdev, nvec);
|
ret = pci_enable_msi_range(pdev, 1, nvec);
|
||||||
if (ret) {
|
if (ret < nvec) {
|
||||||
|
if (ret > 0)
|
||||||
|
pci_disable_msi(pdev);
|
||||||
kfree(vdev->ctx);
|
kfree(vdev->ctx);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1171,8 +1171,23 @@ void msi_remove_pci_irq_vectors(struct pci_dev *dev);
|
||||||
void pci_restore_msi_state(struct pci_dev *dev);
|
void pci_restore_msi_state(struct pci_dev *dev);
|
||||||
int pci_msi_enabled(void);
|
int pci_msi_enabled(void);
|
||||||
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec);
|
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec);
|
||||||
|
static inline int pci_enable_msi_exact(struct pci_dev *dev, int nvec)
|
||||||
|
{
|
||||||
|
int rc = pci_enable_msi_range(dev, nvec, nvec);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
|
int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
|
||||||
int minvec, int maxvec);
|
int minvec, int maxvec);
|
||||||
|
static inline int pci_enable_msix_exact(struct pci_dev *dev,
|
||||||
|
struct msix_entry *entries, int nvec)
|
||||||
|
{
|
||||||
|
int rc = pci_enable_msix_range(dev, entries, nvec, nvec);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#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 int pci_enable_msi_block(struct pci_dev *dev, int nvec)
|
static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec)
|
||||||
|
|
@ -1191,9 +1206,14 @@ static inline int pci_msi_enabled(void) { return 0; }
|
||||||
static inline int pci_enable_msi_range(struct pci_dev *dev, int minvec,
|
static inline int pci_enable_msi_range(struct pci_dev *dev, int minvec,
|
||||||
int maxvec)
|
int maxvec)
|
||||||
{ return -ENOSYS; }
|
{ return -ENOSYS; }
|
||||||
|
static inline int pci_enable_msi_exact(struct pci_dev *dev, int nvec)
|
||||||
|
{ return -ENOSYS; }
|
||||||
static inline int pci_enable_msix_range(struct pci_dev *dev,
|
static inline int pci_enable_msix_range(struct pci_dev *dev,
|
||||||
struct msix_entry *entries, int minvec, int maxvec)
|
struct msix_entry *entries, int minvec, int maxvec)
|
||||||
{ return -ENOSYS; }
|
{ return -ENOSYS; }
|
||||||
|
static inline int pci_enable_msix_exact(struct pci_dev *dev,
|
||||||
|
struct msix_entry *entries, int nvec)
|
||||||
|
{ return -ENOSYS; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PCIEPORTBUS
|
#ifdef CONFIG_PCIEPORTBUS
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue