Skip to content

Commit

Permalink
PCI/MSI: Enable multiple MSIs with pci_enable_msi_block_auto()
Browse files Browse the repository at this point in the history
The new function pci_enable_msi_block_auto() tries to allocate
maximum possible number of MSIs up to the number the device
supports. It generalizes a pattern when pci_enable_msi_block()
is contiguously called until it succeeds or fails.

Opposite to pci_enable_msi_block() which takes the number of
MSIs to allocate as a input parameter,
pci_enable_msi_block_auto() could be used by device drivers to
obtain the number of assigned MSIs and the number of MSIs the
device supports.

Signed-off-by: Alexander Gordeev <[email protected]>
Acked-by: Bjorn Helgaas <[email protected]>
Cc: Suresh Siddha <[email protected]>
Cc: Yinghai Lu <[email protected]>
Cc: Matthew Wilcox <[email protected]>
Cc: Jeff Garzik <[email protected]>
Cc: Linus Torvalds <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Link: http://lkml.kernel.org/r/c3de2419df94a0f95ca1a6f755afc421486455e6.1353324359.git.agordeev@redhat.com
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
Alexander Gordeev authored and Ingo Molnar committed Jan 24, 2013
1 parent 51906e7 commit 08261d8
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 5 deletions.
37 changes: 32 additions & 5 deletions Documentation/PCI/MSI-HOWTO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,42 @@ on the number of vectors that can be allocated; pci_enable_msi_block()
returns as soon as it finds any constraint that doesn't allow the
call to succeed.

4.2.3 pci_disable_msi
4.2.3 pci_enable_msi_block_auto

int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *count)

This variation on pci_enable_msi() call allows a device driver to request
the maximum possible number of MSIs. The MSI specification only allows
interrupts to be allocated in powers of two, up to a maximum of 2^5 (32).

If this function returns a positive number, it indicates that it has
succeeded and the returned value is the number of allocated interrupts. In
this case, the function enables MSI on this device and updates dev->irq to
be the lowest of the new interrupts assigned to it. The other interrupts
assigned to the device are in the range dev->irq to dev->irq + returned
value - 1.

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.

If the device driver needs to know the number of interrupts the device
supports it can pass the pointer count where that number is stored. The
device driver must decide what action to take if pci_enable_msi_block_auto()
succeeds, but returns a value less than the number of interrupts supported.
If the device driver does not need to know the number of interrupts
supported, it can set the pointer count to NULL.

4.2.4 pci_disable_msi

void pci_disable_msi(struct pci_dev *dev)

This function should be used to undo the effect of pci_enable_msi() or
pci_enable_msi_block(). Calling it restores dev->irq to the pin-based
interrupt number and frees the previously allocated message signaled
interrupt(s). The interrupt may subsequently be assigned to another
device, so drivers should not cache the value of dev->irq.
pci_enable_msi_block() or pci_enable_msi_block_auto(). Calling it restores
dev->irq to the pin-based interrupt number and frees the previously
allocated message signaled interrupt(s). The interrupt may subsequently be
assigned to another device, so drivers should not cache the value of
dev->irq.

Before calling this function, a device driver must always call free_irq()
on any interrupt for which it previously called request_irq().
Expand Down
26 changes: 26 additions & 0 deletions drivers/pci/msi.c
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,32 @@ int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec)
}
EXPORT_SYMBOL(pci_enable_msi_block);

int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec)
{
int ret, pos, nvec;
u16 msgctl;

pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
if (!pos)
return -EINVAL;

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

if (maxvec)
*maxvec = ret;

do {
nvec = ret;
ret = pci_enable_msi_block(dev, nvec);
} while (ret > 0);

if (ret < 0)
return ret;
return nvec;
}
EXPORT_SYMBOL(pci_enable_msi_block_auto);

void pci_msi_shutdown(struct pci_dev *dev)
{
struct msi_desc *desc;
Expand Down
7 changes: 7 additions & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,12 @@ static inline int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec)
return -1;
}

static inline int
pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec)
{
return -1;
}

static inline void pci_msi_shutdown(struct pci_dev *dev)
{ }
static inline void pci_disable_msi(struct pci_dev *dev)
Expand Down Expand Up @@ -1132,6 +1138,7 @@ static inline int pci_msi_enabled(void)
}
#else
extern int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec);
extern int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec);
extern void pci_msi_shutdown(struct pci_dev *dev);
extern void pci_disable_msi(struct pci_dev *dev);
extern int pci_msix_table_size(struct pci_dev *dev);
Expand Down

0 comments on commit 08261d8

Please sign in to comment.