The branch main has been updated by bz:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=b3b836251f9fefa817d158784189f6d336917f7a

commit b3b836251f9fefa817d158784189f6d336917f7a
Author:     Bjoern A. Zeeb <b...@freebsd.org>
AuthorDate: 2022-02-16 18:04:08 +0000
Commit:     Bjoern A. Zeeb <b...@freebsd.org>
CommitDate: 2022-03-21 14:35:23 +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.
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      3 days
    Reviewed by:    hselasky, (jhb some earlier)
    Thanks to:      wulf for handling drm-kmod
    Differential Revision: https://reviews.freebsd.org/D34305
---
 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 7bf785fb209a..0b4cee0f8dc2 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci.h
@@ -478,6 +478,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)
 {
@@ -1399,13 +1441,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 5f0ec07d3841..b4f4a2219bb4 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);
 }

Reply via email to