git: a85cf68895d4 - releng/13.1 - LinuxKPI: pci: implement pci_upstream_bridge()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 30 Mar 2022 15:49:44 UTC
The branch releng/13.1 has been updated by bz: URL: https://cgit.FreeBSD.org/src/commit/?id=a85cf68895d40ddcdb1f2c7313a7b415f27c0ef5 commit a85cf68895d40ddcdb1f2c7313a7b415f27c0ef5 Author: Bjoern A. Zeeb <bz@FreeBSD.org> AuthorDate: 2022-02-16 18:04:08 +0000 Commit: Bjoern A. Zeeb <bz@FreeBSD.org> CommitDate: 2022-03-30 15:46:52 +0000 LinuxKPI: pci: implement pci_upstream_bridge() Allow drivers to query the "upstream PCI bridge". Currently we point back to ourselves on pdev->bus->self rather than to the parent PCI bridge. We keep this as status-quo with an extra comment and only on-demand allocate a pci_dev for the parent bridge if we are asked for in pci_upstream_bridge(). When releasing the pci_dev we check if pdev->bus->self has changed and call pci_dev_put() to release the reference count on the parent bridge as well. This code moves pci_is_root_bus() higher up in pci.h but no functional change there. Approved by: re (gjb) Sponsored by: The FreeBSD Foundation Reviewed by: hselasky, (jhb some earlier) Thanks to: wulf for handling drm-kmod Differential Revision: https://reviews.freebsd.org/D34305 (cherry picked from commit b3b836251f9fefa817d158784189f6d336917f7a) (cherry picked from commit 50136233846e59eeb53c95498fdd4f3e1acf0a16) --- sys/compat/linuxkpi/common/include/linux/pci.h | 49 ++++++++++++++++++++++---- sys/compat/linuxkpi/common/src/linux_pci.c | 7 ++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h index 361600a55397..eb5f46a4879c 100644 --- a/sys/compat/linuxkpi/common/include/linux/pci.h +++ b/sys/compat/linuxkpi/common/include/linux/pci.h @@ -482,6 +482,48 @@ pci_clear_master(struct pci_dev *pdev) 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) { @@ -1403,13 +1445,6 @@ pci_dev_present(const struct pci_device_id *cur) 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 index ccb52732391e..bc169425c6a8 100644 --- a/sys/compat/linuxkpi/common/src/linux_pci.c +++ b/sys/compat/linuxkpi/common/src/linux_pci.c @@ -278,6 +278,11 @@ lkpifill_pci_dev(device_t dev, struct pci_dev *pdev) 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 @@ lkpinew_pci_dev_release(struct device *dev) 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); }