The userq suspend and resume handlers walks the doorbell XArray but process a user queuewithout holding its kref.
Therefore, a concurrent queue destruction process can free a queue before these handlers finish their work, causing an use-after-free problem. This commit fixes this problem by using amdgpu_userq_xa_find helper which properly holds the kref of a queue in a loop of searching for queues. Signed-off-by: Zhu Lingshan <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 35 +++++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 1427ff175dab..0dab395ef0f4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -1493,23 +1493,35 @@ int amdgpu_userq_suspend(struct amdgpu_device *adev) u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev); struct amdgpu_usermode_queue *queue; struct amdgpu_userq_mgr *uqm; - unsigned long queue_id; + unsigned long queue_id = 0; int r; if (!ip_mask) return 0; - xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) { + queue = amdgpu_userq_xa_find(&adev->userq_doorbell_xa, &queue_id); + while (queue) { uqm = queue->userq_mgr; cancel_delayed_work_sync(&uqm->resume_work); - guard(mutex)(&uqm->userq_mutex); + mutex_lock(&uqm->userq_mutex); if (adev->in_s0ix) r = amdgpu_userq_preempt_helper(queue); else r = amdgpu_userq_unmap_helper(queue); + mutex_unlock(&uqm->userq_mutex); + amdgpu_userq_put(queue); + if (r) return r; + + if (queue_id == ULONG_MAX) + break; + + queue_id++; + queue = amdgpu_userq_xa_find(&adev->userq_doorbell_xa, + &queue_id); } + return 0; } @@ -1518,21 +1530,32 @@ int amdgpu_userq_resume(struct amdgpu_device *adev) u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev); struct amdgpu_usermode_queue *queue; struct amdgpu_userq_mgr *uqm; - unsigned long queue_id; + unsigned long queue_id = 0; int r; if (!ip_mask) return 0; - xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) { + queue = amdgpu_userq_xa_find(&adev->userq_doorbell_xa, &queue_id); + while (queue) { uqm = queue->userq_mgr; - guard(mutex)(&uqm->userq_mutex); + mutex_lock(&uqm->userq_mutex); if (adev->in_s0ix) r = amdgpu_userq_restore_helper(queue); else r = amdgpu_userq_map_helper(queue); + mutex_unlock(&uqm->userq_mutex); + amdgpu_userq_put(queue); + if (r) return r; + + if (queue_id == ULONG_MAX) + break; + + queue_id++; + queue = amdgpu_userq_xa_find(&adev->userq_doorbell_xa, + &queue_id); } return 0; -- 2.53.0
