diff mbox series

x86/PCI: Handle PIRQ routing tables with no router device given

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

Commit Message

Maciej W. Rozycki July 5, 2021, 10:46 a.m. UTC
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

Comments

Nikolai Zhubr July 6, 2021, 10:28 a.m. UTC | #1
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 */
>   }
>
Maciej W. Rozycki July 6, 2021, 11:35 a.m. UTC | #2
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
diff mbox series

Patch

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