Skip to content

Commit

Permalink
PCI: PCIe portdrv: Aviod using service devices with wrong interrupts
Browse files Browse the repository at this point in the history
The PCI Express port driver should not attempt to register service
devices that require the ability to generate interrupts if generating
interrupts is not possible.  Namely, if the port has no interrupt pin
configured and we cannot set up MSI or MSI-X for it, there is no way
it can generate interrupts and in such a case the port services that
rely on interrupts (PME, PCIe HP, AER) should not be enabled for it.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
  • Loading branch information
Rafael J. Wysocki authored and Jesse Barnes committed Mar 20, 2009
1 parent 1bf83e5 commit 90e9cd5
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 13 deletions.
41 changes: 28 additions & 13 deletions drivers/pci/pcie/portdrv_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,17 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
{
struct pcie_port_data *port_data = pci_get_drvdata(dev);
int i, pos, nvec, status = -EINVAL;
int interrupt_mode = PCIE_PORT_INTx_MODE;
int interrupt_mode = PCIE_PORT_NO_IRQ;

/* Set INTx as default */
for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
if (mask & (1 << i))
nvec++;
vectors[i] = dev->irq;
}

if (dev->pin)
interrupt_mode = PCIE_PORT_INTx_MODE;

/* Check MSI quirk */
if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk)
return interrupt_mode;
Expand Down Expand Up @@ -141,7 +143,7 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev,
dev->id.vendor = parent->vendor;
dev->id.device = parent->device;
dev->id.port_type = port_type;
dev->id.service_type = (1 << service_type);
dev->id.service_type = service_type;

/* Initialize generic device interface */
device = &dev->device;
Expand Down Expand Up @@ -232,19 +234,32 @@ int pcie_port_device_register(struct pci_dev *dev)
/* Allocate child services if any */
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
struct pcie_device *child;
int service = 1 << i;

if (capabilities & (1 << i)) {
child = alloc_pcie_device(dev, i, vectors[i]);
if (child) {
status = device_register(&child->device);
if (status) {
kfree(child);
continue;
}
get_device(&child->device);
}
if (!(capabilities & service))
continue;

/*
* Don't use service devices that require interrupts if there is
* no way to generate them.
*/
if (irq_mode == PCIE_PORT_NO_IRQ
&& service != PCIE_PORT_SERVICE_VC)
continue;

child = alloc_pcie_device(dev, service, vectors[i]);
if (!child)
continue;

status = device_register(&child->device);
if (status) {
kfree(child);
continue;
}

get_device(&child->device);
}

return 0;
}

Expand Down
1 change: 1 addition & 0 deletions include/linux/pcieport_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define PCIE_PORT_SERVICE_VC 8 /* Virtual Channel */

/* Root/Upstream/Downstream Port's Interrupt Mode */
#define PCIE_PORT_NO_IRQ (-1)
#define PCIE_PORT_INTx_MODE 0
#define PCIE_PORT_MSI_MODE 1
#define PCIE_PORT_MSIX_MODE 2
Expand Down

0 comments on commit 90e9cd5

Please sign in to comment.