Skip to content

Commit

Permalink
x86/PCI: Handle PIRQ routing tables with no router device given
Browse files Browse the repository at this point in the history
PIRQ routing tables provided by the PCI BIOS usually specify the PCI 
vendor:device ID as well as the bus address of the device implementing 
the PIRQ router, e.g.:

PCI: Interrupt Routing Table found at 0xc00fde10
[...]
PCI: Attempting to find IRQ router for [8086:7000]
pci 0000:00:07.0: PIIX/ICH IRQ router [8086:7000]

however in some cases they do not, in which case we fail to match the 
router handler, e.g.:

PCI: Interrupt Routing Table found at 0xc00fdae0
[...]
PCI: Attempting to find IRQ router for [0000:0000]
PCI: Interrupt router not found at 00:00

This is because we always match the vendor:device ID and the bus address 
literally, even if they are all zeros.

Handle this case then and iterate over all PCI devices until we find a 
matching router handler if the vendor ID given by the routing table is 
the invalid value of zero:

PCI: Attempting to find IRQ router for [0000:0000]
PCI: Trying IRQ router for [1039:0496]
pci 0000:00:05.0: SiS85C497 IRQ router [1039:0496]

Signed-off-by: Maciej W. Rozycki <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
Tested-by: Nikolai Zhubr <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
  • Loading branch information
maciej-w-rozycki authored and KAGA-KOKO committed Apr 10, 2022
1 parent 5d64089 commit ac7cd5e
Showing 1 changed file with 44 additions and 20 deletions.
64 changes: 44 additions & 20 deletions arch/x86/pci/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -1175,10 +1175,32 @@ static struct pci_dev *pirq_router_dev;
* chipset" ?
*/

static bool __init pirq_try_router(struct irq_router *r,
struct irq_routing_table *rt,
struct pci_dev *dev)
{
struct irq_router_handler *h;

DBG(KERN_DEBUG "PCI: Trying IRQ router for [%04x:%04x]\n",
dev->vendor, dev->device);

for (h = pirq_routers; h->vendor; h++) {
/* First look for a router match */
if (rt->rtr_vendor == h->vendor &&
h->probe(r, dev, rt->rtr_device))
return true;
/* Fall back to a device match */
if (dev->vendor == h->vendor &&
h->probe(r, dev, dev->device))
return true;
}
return false;
}

static void __init pirq_find_router(struct irq_router *r)
{
struct irq_routing_table *rt = pirq_table;
struct irq_router_handler *h;
struct pci_dev *dev;

#ifdef CONFIG_PCI_BIOS
if (!rt->signature) {
Expand All @@ -1197,27 +1219,29 @@ static void __init pirq_find_router(struct irq_router *r)
DBG(KERN_DEBUG "PCI: Attempting to find IRQ router for [%04x:%04x]\n",
rt->rtr_vendor, rt->rtr_device);

pirq_router_dev = pci_get_domain_bus_and_slot(0, rt->rtr_bus,
rt->rtr_devfn);
if (!pirq_router_dev) {
DBG(KERN_DEBUG "PCI: Interrupt router not found at "
"%02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);
return;
/* Use any vendor:device provided by the routing table or try all. */
if (rt->rtr_vendor) {
dev = pci_get_domain_bus_and_slot(0, rt->rtr_bus,
rt->rtr_devfn);
if (dev && pirq_try_router(r, rt, dev))
pirq_router_dev = dev;
} else {
dev = NULL;
for_each_pci_dev(dev) {
if (pirq_try_router(r, rt, dev)) {
pirq_router_dev = dev;
break;
}
}
}

for (h = pirq_routers; h->vendor; h++) {
/* First look for a router match */
if (rt->rtr_vendor == h->vendor &&
h->probe(r, pirq_router_dev, rt->rtr_device))
break;
/* Fall back to a device match */
if (pirq_router_dev->vendor == h->vendor &&
h->probe(r, pirq_router_dev, pirq_router_dev->device))
break;
}
dev_info(&pirq_router_dev->dev, "%s IRQ router [%04x:%04x]\n",
pirq_router.name,
pirq_router_dev->vendor, pirq_router_dev->device);
if (pirq_router_dev)
dev_info(&pirq_router_dev->dev, "%s IRQ router [%04x:%04x]\n",
pirq_router.name,
pirq_router_dev->vendor, pirq_router_dev->device);
else
DBG(KERN_DEBUG "PCI: Interrupt router not found at "
"%02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);

/* The device remains referenced for the kernel lifetime */
}
Expand Down

0 comments on commit ac7cd5e

Please sign in to comment.