On Tue, Sep 15, 2026 at 12:09 PM Mario Limonciello
<[email protected]> wrote:
>
> 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]>

Reviewed-by: Alex Deucher <[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