drm/amdgpu: Fix GPU PCIe link capability reporting

Commit eb53125a7a ("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" <maraeo@gmail.com>
Closes: https://lore.kernel.org/amd-gfx/CAAxE2A4VhsAzzO1QjBjUg+NgnbD04ZzMyN6xsUJxjKJHH6hxiw@mail.gmail.com/
Suggested-by: Lijo Lazar <lijo.lazar@amd.com>
Fixes: eb53125a7a ("drm/amd: Add dedicated helper for amdgpu_device_find_parent()")
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
(cherry picked from commit 7ea6a47224e2c6e89a3a682d7fbaace4817a55aa)
Cc: stable@vger.kernel.org
This commit is contained in:
Mario Limonciello 2026-09-15 10:59:45 -05:00 committed by Alex Deucher
parent 723d4dc628
commit 04de4007d3
2 changed files with 20 additions and 31 deletions

View File

@ -621,6 +621,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

View File

@ -1954,18 +1954,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;
}
/**
@ -1981,7 +1980,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;
@ -2077,10 +2075,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)
@ -3776,6 +3773,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;
@ -5872,11 +5870,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 */
@ -5898,21 +5894,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);
}
/**