Commit eb53125a7ad9 ("drm/amd: Add dedicated helper for
amdgpu_device_find_parent()") made amdgpu_device_gpu_bandwidth() query
the first device outside the dGPU. That is the host side of the
physical link, not the GPU side.

As a result, the ASIC and platform capability masks can both be based
on the host port. drm_amdgpu_info_device then exposes the host
capabilities to userspace, such as Gen5 x16 for a Gen4 x8 GPU.

Cache both ends of the physical link during device initialization.
Use link_dev for the GPU capability and link_partner for the platform
capability and _PR3 detection.

Reported-by: "Marek Olšák" <[email protected]>
Closes: 
https://lore.kernel.org/amd-gfx/caaxe2a4vhsazzo1qjbjug+ngnbd04zzmyn6xsujxjkjhh6h...@mail.gmail.com/
Suggested-by: Lijo Lazar <[email protected]>
Fixes: eb53125a7ad9 ("drm/amd: Add dedicated helper for 
amdgpu_device_find_parent()")
Cc: [email protected]
Signed-off-by: Mario Limonciello <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu.h        |  3 ++
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 48 ++++++++--------------
 2 files changed, 20 insertions(+), 31 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 79b69d74eb2e5..3bad7b8f621cc 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -626,6 +626,9 @@ enum amdgpu_enforce_isolation_mode {
 struct amdgpu_device {
        struct device                   *dev;
        struct pci_dev                  *pdev;
+       /* The two ends of the physical PCIe link outside the device. */
+       struct pci_dev                  *link_dev;
+       struct pci_dev                  *link_partner;
        struct drm_device               ddev;
 
 #ifdef CONFIG_DRM_AMD_ACP
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 44091ac12fa9c..0d277516fd55e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1959,18 +1959,17 @@ static void amdgpu_uid_fini(struct amdgpu_device *adev)
        adev->uid_info = NULL;
 }
 
-static struct pci_dev *amdgpu_device_find_parent(struct amdgpu_device *adev)
+static void amdgpu_device_init_pcie_links(struct amdgpu_device *adev)
 {
-       struct pci_dev *parent = adev->pdev;
+       adev->link_dev = adev->pdev;
+       adev->link_partner = pci_upstream_bridge(adev->link_dev);
 
-       /* skip upstream/downstream switches internal to dGPU */
-       while ((parent = pci_upstream_bridge(parent))) {
-               if (parent->vendor == PCI_VENDOR_ID_ATI)
-                       continue;
-               break;
+       /* Skip upstream/downstream switches internal to the dGPU. */
+       while (adev->link_partner &&
+              adev->link_partner->vendor == PCI_VENDOR_ID_ATI) {
+               adev->link_dev = adev->link_partner;
+               adev->link_partner = pci_upstream_bridge(adev->link_dev);
        }
-
-       return parent;
 }
 
 /**
@@ -1986,7 +1985,6 @@ static struct pci_dev *amdgpu_device_find_parent(struct 
amdgpu_device *adev)
 static int amdgpu_device_ip_early_init(struct amdgpu_device *adev)
 {
        struct amdgpu_ip_block *ip_block;
-       struct pci_dev *parent;
        bool total, skip_bios, early_full_gpu_access = false;
        uint32_t bios_flags;
        int i, r;
@@ -2082,10 +2080,9 @@ static int amdgpu_device_ip_early_init(struct 
amdgpu_device *adev)
            !dev_is_removable(&adev->pdev->dev))
                adev->flags |= AMD_IS_PX;
 
-       if (!(adev->flags & AMD_IS_APU)) {
-               parent = amdgpu_device_find_parent(adev);
-               adev->has_pr3 = parent ? pci_pr3_present(parent) : false;
-       }
+       if (!(adev->flags & AMD_IS_APU))
+               adev->has_pr3 = adev->link_partner &&
+                       pci_pr3_present(adev->link_partner);
 
        adev->pm.pp_feature = amdgpu_pp_feature_mask;
        if (amdgpu_sriov_vf(adev) || sched_policy == KFD_SCHED_POLICY_NO_HWS)
@@ -3783,6 +3780,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
 
        adev->shutdown = false;
        adev->flags = flags;
+       amdgpu_device_init_pcie_links(adev);
 
        if (amdgpu_force_asic_type >= 0 && amdgpu_force_asic_type < CHIP_LAST)
                adev->asic_type = amdgpu_force_asic_type;
@@ -5880,11 +5878,9 @@ static void amdgpu_device_partner_bandwidth(struct 
amdgpu_device *adev,
        *width = PCIE_LNK_WIDTH_UNKNOWN;
 
        if (amdgpu_device_pcie_dynamic_switching_supported(adev)) {
-               struct pci_dev *parent = amdgpu_device_find_parent(adev);
-
-               if (parent) {
-                       *speed = pcie_get_speed_cap(parent);
-                       *width = pcie_get_width_cap(parent);
+               if (adev->link_partner) {
+                       *speed = pcie_get_speed_cap(adev->link_partner);
+                       *width = pcie_get_width_cap(adev->link_partner);
                }
        } else {
                /* use the current speeds rather than max if switching is not 
supported */
@@ -5906,21 +5902,11 @@ static void amdgpu_device_gpu_bandwidth(struct 
amdgpu_device *adev,
                                        enum pci_bus_speed *speed,
                                        enum pcie_link_width *width)
 {
-       struct pci_dev *parent = adev->pdev;
-
        if (!speed || !width)
                return;
 
-       /* use the device itself */
-       *speed = pcie_get_speed_cap(adev->pdev);
-       *width = pcie_get_width_cap(adev->pdev);
-
-       /* use the link outside the device */
-       parent = amdgpu_device_find_parent(adev);
-       if (parent) {
-               *speed = pcie_get_speed_cap(parent);
-               *width = pcie_get_width_cap(parent);
-       }
+       *speed = pcie_get_speed_cap(adev->link_dev);
+       *width = pcie_get_width_cap(adev->link_dev);
 }
 
 /**
-- 
2.43.0

Reply via email to