The branch main has been updated by bz:

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

commit fa24602ca6282d71c26079136a74b85824c0e63b
Author:     Bjoern A. Zeeb <[email protected]>
AuthorDate: 2025-09-17 23:37:02 +0000
Commit:     Bjoern A. Zeeb <[email protected]>
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)

Reply via email to