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

Reply via email to