Index: sys/compat/linuxkpi/common/include/linux/pci.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/pci.h +++ sys/compat/linuxkpi/common/include/linux/pci.h @@ -458,6 +458,47 @@ 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 or to nothing, try to handle this case on the fly + * like we do for pcie_find_root_port(). + */ + if (pdev == pdev->bus->self || pdev->bus->self == NULL) { + device_t bridge; + + bridge = device_get_parent(pdev->dev.bsddev); + if (bridge == NULL) + return (pdev->bus->self); + bridge = device_get_parent(bridge); + if (bridge == NULL) + return (pdev->bus->self); + if (device_get_devclass(device_get_parent(bridge)) != + devclass_find("pci")) + return (pdev->bus->self); + + /* + * "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); + } + return (pdev->bus->self); +} + static inline struct pci_devres * lkpi_pci_devres_get_alloc(struct pci_dev *pdev) { @@ -1379,13 +1420,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) \ Index: sys/compat/linuxkpi/common/src/linux_pci.c =================================================================== --- sys/compat/linuxkpi/common/src/linux_pci.c +++ 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); }