drm/amd/pm: fix torn gpu metrics reads
authorYang Wang <kevinyang.wang@amd.com>
Thu, 23 Jul 2026 15:18:33 +0000 (23:18 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 29 Jul 2026 00:00:06 +0000 (20:00 -0400)
amdgpu_dpm_get_gpu_metrics() returns a pointer to the shared metrics cache
after dropping adev->pm.mutex. The sysfs path then copies from that pointer.
Another reader can refresh the cache in place during the copy and return a
snapshot containing data from two generations.

Pass caller-provided storage through the DPM interface and copy the metrics
while the mutex is held. This keeps the cache pointer private and makes each
sysfs read observe one complete sample.

Fixes: 25c933b1c4fc ("drm/amd/powerplay: add new sysfs interface for retrieving gpu metrics(V2)")
Signed-off-by: Yang Wang <kevinyang.wang@amd.com>
Reviewed-by: Kenneth Feng <kenneth.feng@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
(cherry picked from commit 862333bb48693ecafcae25af0c9d9ec31015ac77)
Cc: stable@vger.kernel.org
drivers/gpu/drm/amd/pm/amdgpu_dpm.c
drivers/gpu/drm/amd/pm/amdgpu_pm.c
drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h

index c787e77..6d1ad4d 100644 (file)
@@ -1434,17 +1434,23 @@ int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev,
        return ret;
 }
 
-int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table)
+ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf,
+                                  size_t size)
 {
        const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
-       int ret = 0;
+       void *table;
+       ssize_t ret;
 
        if (!pp_funcs->get_gpu_metrics)
                return 0;
 
        mutex_lock(&adev->pm.mutex);
        ret = pp_funcs->get_gpu_metrics(adev->powerplay.pp_handle,
-                                       table);
+                                       &table);
+       if (ret > 0) {
+               ret = min_t(ssize_t, ret, size);
+               memcpy(buf, table, ret);
+       }
        mutex_unlock(&adev->pm.mutex);
 
        return ret;
index d94ea54..763947f 100644 (file)
@@ -1774,7 +1774,6 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev,
 {
        struct drm_device *ddev = dev_get_drvdata(dev);
        struct amdgpu_device *adev = drm_to_adev(ddev);
-       void *gpu_metrics;
        ssize_t size = 0;
        int ret;
 
@@ -1782,15 +1781,10 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev,
        if (ret)
                return ret;
 
-       size = amdgpu_dpm_get_gpu_metrics(adev, &gpu_metrics);
+       size = amdgpu_dpm_get_gpu_metrics(adev, buf, PAGE_SIZE - 1);
        if (size <= 0)
                goto out;
 
-       if (size >= PAGE_SIZE)
-               size = PAGE_SIZE - 1;
-
-       memcpy(buf, gpu_metrics, size);
-
 out:
        amdgpu_pm_put_access(adev);
 
index e95cd22..8d1b097 100644 (file)
@@ -518,7 +518,8 @@ int amdgpu_dpm_get_power_profile_mode(struct amdgpu_device *adev,
                                      char *buf);
 int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev,
                                      long *input, uint32_t size);
-int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table);
+ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf,
+                                  size_t size);
 ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id,
                                   void *table);
 ssize_t amdgpu_dpm_get_temp_metrics(struct amdgpu_device *adev,