git: fa24602ca628 - main - LinuxKPI: pci: fix pcie_get_speed_cap()

From: Bjoern A. Zeeb <bz_at_FreeBSD.org>
Date: Wed, 14 Jan 2026 18:37:16 UTC
The branch main has been updated by bz:

URL: https://cgit.FreeBSD.org/src/commit/?id=fa24602ca6282d71c26079136a74b85824c0e63b

commit fa24602ca6282d71c26079136a74b85824c0e63b
Author:     Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2025-09-17 23:37:02 +0000
Commit:     Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2026-01-14 18:36:09 +0000

    LinuxKPI: pci: fix pcie_get_speed_cap()
    
    pcie_get_speed_cap() has a hard coded skip of 3 devices at the
    beginning.  It is either called on a pdev or on a result from
    pci_upstream_bridge().  In the latter case skipping another three
    devices might get us to acpi0 or nexus, neither of which is a
    PCI device still and pci_get_vendor() will panic() on that.
    
    Sponsored by:   The FreeBSD Foundation (commit)
    GHI:            https://github.com/freebsd/drm-kmod/issues/393
    MFC after:      2 weeks
    Differential Revision: https://reviews.freebsd.org/D53862
---
 sys/compat/linuxkpi/common/include/linux/pci.h | 27 +++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h
index 8fe09554aed2..c337be67f5a4 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci.h
@@ -1136,19 +1136,28 @@ pci_num_vf(struct pci_dev *dev)
 static inline enum pci_bus_speed
 pcie_get_speed_cap(struct pci_dev *dev)
 {
+	struct pci_dev *pbus;
 	device_t root;
 	uint32_t lnkcap, lnkcap2;
 	int error, pos;
 
-	root = device_get_parent(dev->dev.bsddev);
-	if (root == NULL)
-		return (PCI_SPEED_UNKNOWN);
-	root = device_get_parent(root);
-	if (root == NULL)
-		return (PCI_SPEED_UNKNOWN);
-	root = device_get_parent(root);
-	if (root == NULL)
-		return (PCI_SPEED_UNKNOWN);
+	/*
+	 * We should always be called on a PCI device.
+	 * The only current consumer I could find was amdgpu which either
+	 * calls us directly on a pdev(drmn?) or with the result of
+	 * pci_upstream_bridge().
+	 *
+	 * Treat "drmn" as special again as it is not a PCI device.
+	 */
+	if (dev->pdrv != NULL && dev->pdrv->isdrm) {
+		pbus = pci_upstream_bridge(dev);
+		if (pbus == NULL)
+			return (PCI_SPEED_UNKNOWN);
+	} else
+		pbus = dev;
+
+	/* "root" may be misleading as it may not be that. */
+	root = pbus->dev.bsddev;
 
 	if (pci_get_vendor(root) == PCI_VENDOR_ID_VIA ||
 	    pci_get_vendor(root) == PCI_VENDOR_ID_SERVERWORKS)