git: fa24602ca628 - main - LinuxKPI: pci: fix pcie_get_speed_cap()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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)