diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c')
| -rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c | 46 | 
1 files changed, 29 insertions, 17 deletions
| diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index 5505d646f43a..06f0a6534a94 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -524,46 +524,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 = { |