2021-07-05 10:47:44

by Maciej W. Rozycki

[permalink] [raw]
Subject: [PATCH] x86/PCI: Handle PIRQ routing tables with no router device given

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 <[email protected]>
---
arch/x86/pci/irq.c | 63 ++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 43 insertions(+), 20 deletions(-)

linux-x86-pirq-router-nodev.diff
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 */
}


2021-07-06 10:19:11

by Nikolai Zhubr

[permalink] [raw]
Subject: Re: [PATCH] x86/PCI: Handle PIRQ routing tables with no router device given

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 */
> }
>

2021-07-06 12:14:22

by Maciej W. Rozycki

[permalink] [raw]
Subject: Re: [PATCH] x86/PCI: Handle PIRQ routing tables with no router device given

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