Skip to content

Commit

Permalink
PCI/MSI: Add pci_msi_vec_count()
Browse files Browse the repository at this point in the history
Device drivers can use this interface to obtain the maximum number of MSI
interrupts the device supports and use that number, e.g., in a subsequent
call to pci_enable_msi_block().

Signed-off-by: Alexander Gordeev <agordeev@redhat.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Tejun Heo <tj@kernel.org>
  • Loading branch information
Alexander Gordeev authored and Bjorn Helgaas committed Jan 4, 2014
1 parent 52179dc commit d1ac1d2
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 8 deletions.
15 changes: 15 additions & 0 deletions Documentation/PCI/MSI-HOWTO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,21 @@ on any interrupt for which it previously called request_irq().
Failure to do so results in a BUG_ON(), leaving the device with
MSI enabled and thus leaking its vector.

4.2.5 pci_msi_vec_count

int pci_msi_vec_count(struct pci_dev *dev)

This function could be used to retrieve the number of MSI vectors the
device requested (via the Multiple Message Capable register). The MSI
specification only allows the returned value to be a power of two,
up to a maximum of 2^5 (32).

If this function returns a negative number, it indicates the device is
not capable of sending MSIs.

If this function returns a positive number, it indicates the maximum
number of MSI interrupt vectors that could be allocated.

4.3 Using MSI-X

The MSI-X capability is much more flexible than the MSI capability.
Expand Down
41 changes: 33 additions & 8 deletions drivers/pci/msi.c
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,31 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
return 0;
}

/**
* pci_msi_vec_count - Return the number of MSI vectors a device can send
* @dev: device to report about
*
* This function returns the number of MSI vectors a device requested via
* Multiple Message Capable register. It returns a negative errno if the
* device is not capable sending MSI interrupts. Otherwise, the call succeeds
* and returns a power of two, up to a maximum of 2^5 (32), according to the
* MSI specification.
**/
int pci_msi_vec_count(struct pci_dev *dev)
{
int ret;
u16 msgctl;

if (!dev->msi_cap)
return -EINVAL;

pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl);
ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);

return ret;
}
EXPORT_SYMBOL(pci_msi_vec_count);

/**
* pci_enable_msi_block - configure device's MSI capability structure
* @dev: device to configure
Expand All @@ -858,13 +883,13 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
int pci_enable_msi_block(struct pci_dev *dev, int nvec)
{
int status, maxvec;
u16 msgctl;

if (!dev->msi_cap || dev->current_state != PCI_D0)
if (dev->current_state != PCI_D0)
return -EINVAL;

pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl);
maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);
maxvec = pci_msi_vec_count(dev);
if (maxvec < 0)
return maxvec;
if (nvec > maxvec)
return maxvec;

Expand All @@ -889,13 +914,13 @@ EXPORT_SYMBOL(pci_enable_msi_block);
int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec)
{
int ret, nvec;
u16 msgctl;

if (!dev->msi_cap || dev->current_state != PCI_D0)
if (dev->current_state != PCI_D0)
return -EINVAL;

pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl);
ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);
ret = pci_msi_vec_count(dev);
if (ret < 0)
return ret;

if (maxvec)
*maxvec = ret;
Expand Down
6 changes: 6 additions & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,11 @@ struct msix_entry {


#ifndef CONFIG_PCI_MSI
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)
{
return -ENOSYS;
Expand Down Expand Up @@ -1195,6 +1200,7 @@ static inline int pci_msi_enabled(void)
return 0;
}
#else
int pci_msi_vec_count(struct pci_dev *dev);
int pci_enable_msi_block(struct pci_dev *dev, int nvec);
int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec);
void pci_msi_shutdown(struct pci_dev *dev);
Expand Down

0 comments on commit d1ac1d2

Please sign in to comment.