drm/amdgpu: fix deadlock while reading mqd from debugfs
authorjsg <jsg@openbsd.org>
Thu, 4 Apr 2024 07:56:50 +0000 (07:56 +0000)
committerjsg <jsg@openbsd.org>
Thu, 4 Apr 2024 07:56:50 +0000 (07:56 +0000)
From Johannes Weiner
197f6d6987c55860f6eea1c93e4f800c59078874 in linux-6.6.y/6.6.24
8678b1060ae2b75feb60b87e5b75e17374e3c1c5 in mainline linux

sys/dev/pci/drm/amd/amdgpu/amdgpu_ring.c

index 80d6e13..dbde3b4 100644 (file)
@@ -520,46 +520,58 @@ static ssize_t amdgpu_debugfs_mqd_read(struct file *f, char __user *buf,
 {
        struct amdgpu_ring *ring = file_inode(f)->i_private;
        volatile u32 *mqd;
-       int r;
+       u32 *kbuf;
+       int r, i;
        uint32_t value, result;
 
        if (*pos & 3 || size & 3)
                return -EINVAL;
 
-       result = 0;
+       kbuf = kmalloc(ring->mqd_size, GFP_KERNEL);
+       if (!kbuf)
+               return -ENOMEM;
 
        r = amdgpu_bo_reserve(ring->mqd_obj, false);
        if (unlikely(r != 0))
-               return r;
+               goto err_free;
 
        r = amdgpu_bo_kmap(ring->mqd_obj, (void **)&mqd);
-       if (r) {
-               amdgpu_bo_unreserve(ring->mqd_obj);
-               return r;
-       }
+       if (r)
+               goto err_unreserve;
 
+       /*
+        * Copy to local buffer to avoid put_user(), which might fault
+        * and acquire mmap_sem, under reservation_ww_class_mutex.
+        */
+       for (i = 0; i < ring->mqd_size/sizeof(u32); i++)
+               kbuf[i] = mqd[i];
+
+       amdgpu_bo_kunmap(ring->mqd_obj);
+       amdgpu_bo_unreserve(ring->mqd_obj);
+
+       result = 0;
        while (size) {
                if (*pos >= ring->mqd_size)
-                       goto done;
+                       break;
 
-               value = mqd[*pos/4];
+               value = kbuf[*pos/4];
                r = put_user(value, (uint32_t *)buf);
                if (r)
-                       goto done;
+                       goto err_free;
                buf += 4;
                result += 4;
                size -= 4;
                *pos += 4;
        }
 
-done:
-       amdgpu_bo_kunmap(ring->mqd_obj);
-       mqd = NULL;
-       amdgpu_bo_unreserve(ring->mqd_obj);
-       if (r)
-               return r;
-
+       kfree(kbuf);
        return result;
+
+err_unreserve:
+       amdgpu_bo_unreserve(ring->mqd_obj);
+err_free:
+       kfree(kbuf);
+       return r;
 }
 
 static const struct file_operations amdgpu_debugfs_mqd_fops = {