amdgpu_debugfs_gpr_read() drops its runtime PM reference right after
reading the wave registers. When copying them to userspace fails, it
jumps to the err label, which drops the reference a second time. A read
into a bad buffer on Navi31 either steals a reference somebody else
holds (power/runtime_usage goes from 1 to 0) or underflows the count:

  amdgpu 0000:03:00.0: Runtime PM usage count underflow!

The registers are already in the kernel buffer at that point, so finish
the hardware access first and copy the buffer with a single
copy_to_user() instead of the put_user() loop with its own error path.

Fixes: 9eee152aab56 ("drm/amdgpu/debugfs: fix ref count leak when 
pm_runtime_get_sync fails")
Signed-off-by: Jesse Zhang <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 21 +++------------------
 1 file changed, 3 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
index 35cbd358c28c..3dc95d9accf2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
@@ -1267,7 +1267,6 @@ static ssize_t amdgpu_debugfs_gpr_read(struct file *f, 
char __user *buf,
 {
        struct amdgpu_device *adev = f->f_inode->i_private;
        int r;
-       ssize_t result = 0;
        uint32_t offset, se, sh, cu, wave, simd, thread, bank, *data;
 
        if (size > 4096 || size & 3 || *pos & 3)
@@ -1311,25 +1310,11 @@ static ssize_t amdgpu_debugfs_gpr_read(struct file *f, 
char __user *buf,
        mutex_unlock(&adev->grbm_idx_mutex);
 
        pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
+       amdgpu_virt_disable_access_debugfs(adev);
 
-       while (size) {
-               uint32_t value;
-
-               value = data[result >> 2];
-               r = put_user(value, (uint32_t *)buf);
-               if (r) {
-                       amdgpu_virt_disable_access_debugfs(adev);
-                       goto err;
-               }
-
-               result += 4;
-               buf += 4;
-               size -= 4;
-       }
-
+       r = copy_to_user(buf, data, size) ? -EFAULT : size;
        kfree(data);
-       amdgpu_virt_disable_access_debugfs(adev);
-       return result;
+       return r;
 
 err:
        pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
-- 
2.50.1

Reply via email to