Message ID | alpine.DEB.2.21.2107051133010.33206@angie.orcam.me.uk |
---|---|
State | New |
Headers | show |
Series | x86/PCI: Handle PIRQ routing tables with no router device given | expand |
Hello Maciej, 05.07.2021 13:46, Maciej W. Rozycki: > 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.: [...] > linux-x86-pirq-router-nodev.diff This one throws a panic in bus_find_device() here. I can not yet get a good printout because scrollback does not work. Maybe it is because of 4.14 kernel, and in order to apply it I had to change pci_get_domain_bus_and_slot back to pci_get_bus_and_slot. I'll try to also test with 5.x kernel later today. Thank you, Regards, Nikolai > Index: linux-macro-ide-tty/arch/x86/pci/irq.c > =================================================================== > --- linux-macro-ide-tty.orig/arch/x86/pci/irq.c > +++ linux-macro-ide-tty/arch/x86/pci/irq.c > @@ -908,10 +908,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) { > @@ -930,27 +952,28 @@ static void __init pirq_find_router(stru > 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 (pirq_try_router(r, rt, dev)) > + pirq_router_dev = dev; > + } else { > + 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 */ > } >
Hello Nikolai, > > 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.: > [...] > > linux-x86-pirq-router-nodev.diff > > This one throws a panic in bus_find_device() here. > I can not yet get a good printout because scrollback does not work. > Maybe it is because of 4.14 kernel, and in order to apply it I had to change > pci_get_domain_bus_and_slot back to pci_get_bus_and_slot. > I'll try to also test with 5.x kernel later today. Umm, my bad; I missed the initialisation of `dev' for `for_each_pci_dev'. I have posted v2, with another `dev'-related fix as well. Please try that instead, and sorry for the mess-up. Maciej
Index: linux-macro-ide-tty/arch/x86/pci/irq.c =================================================================== --- linux-macro-ide-tty.orig/arch/x86/pci/irq.c +++ linux-macro-ide-tty/arch/x86/pci/irq.c @@ -908,10 +908,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) { @@ -930,27 +952,28 @@ static void __init pirq_find_router(stru 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 (pirq_try_router(r, rt, dev)) + pirq_router_dev = dev; + } else { + 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 */ }
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. Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk> --- arch/x86/pci/irq.c | 63 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 20 deletions(-) linux-x86-pirq-router-nodev.diff