Signal QUEUE_RESET EVENTFD subscriptions when hung user queues are detected.
MES reports the doorbell indices of hung queues. Use the existing doorbell-to-queue mapping to resolve the affected queue without scanning all user queues. Consolidate the queue state update, reset accounting, EVENTFD signaling, fence completion, and wedged event notification in a common helper. Use the helper for both the original guilty queue and additional affected queues. EVENTFD remains notification-only. v2: (per Christian) - Use the doorbell xarray to look up affected queues instead of scanning all queues. - Move queue reset handling into amdgpu_userq.c. - Consolidate queue state updates, EVENTFD signaling, and fence completion in a single helper. v3: Rebase only. v4: (per Alex) - Rebase on Alex's "drm/amdgpu/userq: properly account for resets". - Preserve the reset counter update in the common hung queue helper. - Use amdgpu_userq_handle_hung_queue() for both successful queue reset paths. v5: (per Alex) - Rebase on Alex's v2 "drm/amdgpu: properly account for resets with user queues". - Keep gpu_reset_counter and drm_dev_wedged_event() in the top-level guilty-queue reset path. - Limit the common helper to queue state, fence completion, and QUEUE_RESET EVENTFD signaling. - Continue to notify both the guilty queue and collateral queues. Suggested-by: Christian König <[email protected]> Suggested-by: Alex Deucher <[email protected]> Signed-off-by: Srinivasan Shanmugam <[email protected]> Acked-by: Christian König <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 41 ++++++++++-- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 1 + drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 75 +++++++++++++++------- 3 files changed, 87 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 20bc15ad55f4..cc30e07a4aa4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -135,6 +135,31 @@ static void amdgpu_userq_mgr_reset_work(struct work_struct *work) amdgpu_device_gpu_recover(adev, NULL, &reset_context); } +/** + * amdgpu_userq_handle_hung_queue - handle a successfully reset hung queue + * @queue: affected user queue + * + * Mark the queue as hung, force-complete its fences, and notify matching + * QUEUE_RESET EVENTFD subscribers. + * + * Device-level reset accounting and wedged-event notification are handled + * separately by the top-level guilty-queue reset path. + */ +void +amdgpu_userq_handle_hung_queue(struct amdgpu_usermode_queue *queue) +{ + struct amdgpu_eventfd_mgr *eventfd_mgr; + + queue->state = AMDGPU_USERQ_STATE_HUNG; + + amdgpu_userq_fence_driver_force_completion(queue); + + eventfd_mgr = amdgpu_userq_eventfd_mgr(queue->userq_mgr); + amdgpu_eventfd_signal(eventfd_mgr, + DRM_AMDGPU_EVENT_TYPE_QUEUE_RESET, + queue); +} + static void amdgpu_userq_hang_detect_work(struct work_struct *work) { struct amdgpu_usermode_queue *queue = @@ -155,7 +180,7 @@ static void amdgpu_userq_hang_detect_work(struct work_struct *work) /* * If GPU recovery feature is disabled system-wide, - * skip all reset detection logic + * skip all reset detection logic. */ if (!amdgpu_gpu_recovery) return; @@ -177,21 +202,25 @@ static void amdgpu_userq_hang_detect_work(struct work_struct *work) queue, NULL, NULL); else r = userq_funcs->reset(queue); + if (r) { gpu_reset = true; } else { atomic_inc(&adev->gpu_reset_counter); - amdgpu_userq_fence_driver_force_completion(queue); - drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, info); + amdgpu_userq_handle_hung_queue(queue); + drm_dev_wedged_event(adev_to_drm(adev), + DRM_WEDGE_RECOVERY_NONE, info); } } else { gpu_reset = true; } + amdgpu_vm_put_task_info(ti); /* - * Don't schedule the work here! Scheduling or queue work from one reset - * handler to another is illegal if you don't take extra precautions! + * Don't schedule the work here! Scheduling or queue work from one + * reset handler to another is illegal if you don't take extra + * precautions. */ if (gpu_reset) amdgpu_userq_mgr_reset_work(&queue->userq_mgr->reset_work); @@ -258,8 +287,6 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell) xa_unlock_irqrestore(xa, flags); } - - int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, struct amdgpu_usermode_queue *queue, u64 addr, u64 expected_size, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index b69621311b80..fa4df8d135eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -180,6 +180,7 @@ void amdgpu_userq_pre_reset(struct amdgpu_device *adev); int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost); void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue); void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell); +void amdgpu_userq_handle_hung_queue(struct amdgpu_usermode_queue *queue); /* * CP packs the per-process doorbell_id of the queue in diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 3d27d2aa2cd0..4eeac5a664bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -26,6 +26,7 @@ #include "amdgpu_gfx.h" #include "mes_userqueue.h" #include "amdgpu_userq_fence.h" +#include "amdgpu_userq_internal.h" #include "amdgpu_cwsr.h" #define AMDGPU_USERQ_PROC_CTX_SZ PAGE_SIZE @@ -238,32 +239,60 @@ int mes_userq_reset_queue(struct amdgpu_device *adev, unsigned int queue, unsigned int db) { + struct xarray *xa = &adev->userq_doorbell_xa; struct amdgpu_usermode_queue *uq; bool use_mmio = adev->gfx.mec.use_mmio_for_reset; - unsigned long uq_id; - int r; + unsigned long flags; + int r = 0; - xa_for_each(&adev->userq_doorbell_xa, uq_id, uq) { - if (uq->queue_type == queue_type) { - if (uq == guilty_uq) - continue; - if (uq->doorbell_index == db) { - uq->state = AMDGPU_USERQ_STATE_HUNG; - if (use_mmio) - r = amdgpu_mes_reset_queue_mmio(adev, queue_type, 0, 1, pipe, queue, 0); - else - r = amdgpu_mes_reset_user_queue(adev, queue_type, db, 0); - if (r) - return r; - r = mes_userq_unmap(uq); - if (r) - return r; - amdgpu_userq_fence_driver_force_completion(uq); - break; - } - } - } - return 0; + /* + * Resolve the doorbell directly to the affected queue instead of + * scanning all user queues. + */ + xa_lock_irqsave(xa, flags); + + uq = xa_load(xa, db); + if (uq) + kref_get(&uq->refcount); + + xa_unlock_irqrestore(xa, flags); + + if (!uq) + return 0; + + /* + * The guilty queue is reset and notified separately by the caller. + */ + if (uq == guilty_uq) + goto out_put_queue; + + if (uq->queue_type != queue_type) + goto out_put_queue; + + if (use_mmio) + r = amdgpu_mes_reset_queue_mmio(adev, queue_type, + 0, 1, pipe, queue, 0); + else + r = amdgpu_mes_reset_user_queue(adev, queue_type, db, 0); + + if (r) + goto out_put_queue; + + r = mes_userq_unmap(uq); + if (r) + goto out_put_queue; + + /* + * Handle the collateral queue itself. Device-level reset accounting + * and the wedged event are emitted only by the top-level guilty-queue + * recovery path. + */ + amdgpu_userq_handle_hung_queue(uq); + +out_put_queue: + amdgpu_userq_put(uq); + + return r; } static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr, -- 2.34.1
