diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h --- a/sys/compat/linuxkpi/common/include/linux/pci.h +++ b/sys/compat/linuxkpi/common/include/linux/pci.h @@ -478,6 +478,48 @@ return (0); } +static inline bool +pci_is_root_bus(struct pci_bus *pbus) +{ + + return (pbus->self == NULL); +} + +static inline struct pci_dev * +pci_upstream_bridge(struct pci_dev *pdev) +{ + + if (pci_is_root_bus(pdev->bus)) + return (NULL); + + /* + * If we do not have a (proper) "upstream bridge" set, e.g., we point + * to ourselves, try to handle this case on the fly like we do + * for pcie_find_root_port(). + */ + if (pdev == pdev->bus->self) { + device_t bridge; + + bridge = device_get_parent(pdev->dev.bsddev); + if (bridge == NULL) + goto done; + bridge = device_get_parent(bridge); + if (bridge == NULL) + goto done; + if (device_get_devclass(device_get_parent(bridge)) != + devclass_find("pci")) + goto done; + + /* + * "bridge" is a PCI-to-PCI bridge. Create a Linux pci_dev + * for it so it can be returned. + */ + pdev->bus->self = lkpinew_pci_dev(bridge); + } +done: + return (pdev->bus->self); +} + static inline struct pci_devres * lkpi_pci_devres_get_alloc(struct pci_dev *pdev) { @@ -1399,13 +1441,6 @@ return (0); } -static inline bool -pci_is_root_bus(struct pci_bus *pbus) -{ - - return (pbus->self == NULL); -} - struct pci_dev *lkpi_pci_get_domain_bus_and_slot(int domain, unsigned int bus, unsigned int devfn); #define pci_get_domain_bus_and_slot(domain, bus, devfn) \ diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c --- a/sys/compat/linuxkpi/common/src/linux_pci.c +++ b/sys/compat/linuxkpi/common/src/linux_pci.c @@ -278,6 +278,11 @@ pdev->class = pci_get_class(dev); pdev->revision = pci_get_revid(dev); pdev->bus = malloc(sizeof(*pdev->bus), M_DEVBUF, M_WAITOK | M_ZERO); + /* + * This should be the upstream bridge; pci_upstream_bridge() + * handles that case on demand as otherwise we'll shadow the + * entire PCI hierarchy. + */ pdev->bus->self = pdev; pdev->bus->number = pci_get_bus(dev); pdev->bus->domain = pci_get_domain(dev); @@ -301,6 +306,8 @@ pdev = to_pci_dev(dev); if (pdev->root != NULL) pci_dev_put(pdev->root); + if (pdev->bus->self != pdev) + pci_dev_put(pdev->bus->self); free(pdev->bus, M_DEVBUF); free(pdev, M_DEVBUF); }