[PATCH] drm/amdgpu: fix hang and race in userq destroy
Zhu Lingshan <[email protected]>
| Newsgroups | org.freedesktop.lists.amd-gfx |
|---|---|
| Message-ID | <[email protected]> |
When a queue is hung, the hang_detect_work is the only way to recover it. However in amdgpu_userq_destroy(), the hang_detect_work is cancelled too early, resulting in amdgpu_userq_wait_for_last_fence() may never return, leaving an uninterruptible dma_fence_wait() hang there. To fix this problem, this commit moves the cancelling of hang_detect_work after amdgpu_userq_wait_for_last_fence(), and it has to be before the unmap helper, because hang_detect_work resets the queue, so it races with amdgpu_userq_unmap_helper() for MES operations and queue state. This commit splits amdgpu_userq_cleanup() into two parts: 1) amdgpu_userq_detach_doorbell(), which detaches the queue from userq_doorbell_xa. This has to be called before the cancel, otherwise the IRQ handlers (for example amdgpu_userq_process_fence_irq) can re-schedule the hang_detect_work and the cancel is not final. 2) amdgpu_userq_fence_driver_free(), this has to be called after the unmap helper, because it can release the seq64 slot that the GPU writes fence values to. Only one cancel_delayed_work_sync(&queue->hang_detect_work) is needed, so other redundancies are removed. Signed-off-by: Zhu Lingshan <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 17cc48d87c4d..24adad7be251 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -418,19 +418,12 @@ static void amdgpu_userq_wait_for_last_fence(struct amdgpu_usermode_queue *queue dma_fence_wait(f, false); } -static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue) +static void amdgpu_userq_detach_doorbell(struct amdgpu_usermode_queue *queue) { - struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; - struct amdgpu_device *adev = uq_mgr->adev; + struct amdgpu_device *adev = queue->userq_mgr->adev; - /* Wait for mode-1 reset to complete */ down_read(&adev->reset_domain->sem); - - /* Use interrupt-safe locking since IRQ handlers may access these XArrays */ xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index); - amdgpu_userq_fence_driver_free(queue); - queue->fence_drv = NULL; - up_read(&adev->reset_domain->sem); } @@ -551,18 +544,19 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que cancel_delayed_work_sync(&uq_mgr->resume_work); - /* Cancel any pending hang detection work and cleanup */ - cancel_delayed_work_sync(&queue->hang_detect_work); - mutex_lock(&uq_mgr->userq_mutex); amdgpu_userq_wait_for_last_fence(queue); + amdgpu_userq_detach_doorbell(queue); + cancel_delayed_work_sync(&queue->hang_detect_work); + #if defined(CONFIG_DEBUG_FS) debugfs_remove_recursive(queue->debugfs_queue); #endif r = amdgpu_userq_unmap_helper(queue); atomic_dec(&uq_mgr->userq_count[queue->queue_type]); - amdgpu_userq_cleanup(queue); + amdgpu_userq_fence_driver_free(queue); + queue->fence_drv = NULL; mutex_unlock(&uq_mgr->userq_mutex); /* @@ -574,7 +568,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que if (r) queue_work(adev->reset_domain->wq, &uq_mgr->reset_work); - cancel_delayed_work_sync(&queue->hang_detect_work); uq_funcs->mqd_destroy(queue); queue->userq_mgr = NULL; @@ -748,7 +741,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) ((queue->queue_type != AMDGPU_HW_IP_GFX) && (queue->queue_type != AMDGPU_HW_IP_COMPUTE))) { /* Serialize the map against an in-progress GPU reset (MES is - * unresponsive during recovery), matching amdgpu_userq_cleanup(). + * unresponsive during recovery), matching amdgpu_userq_detach_doorbell(). */ down_read(&adev->reset_domain->sem); r = amdgpu_userq_map_helper(queue); -- 2.53.0