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);
}