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 <[email protected]>
---
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c     | 12 +++++++++---
 drivers/gpu/drm/amd/pm/amdgpu_pm.c      |  8 +-------
 drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h |  3 ++-
 3 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c 
b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index df863a451f26..8f59ea12fe0e 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -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;
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c 
b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
index d473976ddae5..e4fc33e3c951 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
@@ -1781,7 +1781,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;
 
@@ -1789,15 +1788,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);
 
diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h 
b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
index a1d1c9f5b1d4..8e8a052e1d53 100644
--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
@@ -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,
-- 
2.47.3

Reply via email to