diff options
| author | Bjoern A. Zeeb <bz@FreeBSD.org> | 2025-09-17 23:37:02 +0000 |
|---|---|---|
| committer | Bjoern A. Zeeb <bz@FreeBSD.org> | 2026-01-14 18:36:09 +0000 |
| commit | fa24602ca6282d71c26079136a74b85824c0e63b (patch) | |
| tree | 19826ceb36d562c56266751da4d69324e8ff210a | |
| parent | f5a77dc8f8df09a907c2a2bdf86802513b1ebb15 (diff) | |
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
| -rw-r--r-- | sys/compat/linuxkpi/common/include/linux/pci.h | 27 |
1 files 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) |
