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
This commit is contained in:
Bjoern A. Zeeb
2025-09-17 23:37:02 +00:00
parent f5a77dc8f8
commit fa24602ca6
+18 -9
View File
@@ -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)