A hung user queue must not be replayed after reset because its context
may be corrupted. Keep the guilty state on the queue and report it
through the sticky HUNG status. Skip guilty queues and queues whose VM
generation has changed when mapping or restoring them.

Signed-off-by: Prike Liang <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 30 ++++++++++++++++++++---
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h |  3 +++
 include/uapi/drm/amdgpu_drm.h             | 11 ++++++---
 3 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 4ddd22af63c5..8ac63beca995 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -148,6 +148,8 @@ static void amdgpu_userq_hang_detect_work(struct 
work_struct *work)
                }
        }
 
+       WRITE_ONCE(queue->guilty, true);
+
        if (amdgpu_userq_is_reset_type_supported(adev, queue->queue_type,
                                                 AMDGPU_RESET_TYPE_PER_QUEUE)) {
                int r;
@@ -174,7 +176,7 @@ static void amdgpu_userq_hang_detect_work(struct 
work_struct *work)
         * 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);
+               amdgpu_userq_mgr_reset_work(&uq_mgr->reset_work);
 }
 
 /*
@@ -338,6 +340,10 @@ static int amdgpu_userq_restore_helper(struct 
amdgpu_usermode_queue *queue)
                adev->userq_funcs[queue->queue_type];
        int r = 0;
 
+       if (READ_ONCE(queue->guilty) ||
+           queue->generation != amdgpu_vm_generation(adev, queue->vm))
+               return 0;
+
        if (queue->state == AMDGPU_USERQ_STATE_PREEMPTED) {
                trace_amdgpu_userq_state_start(queue);
 
@@ -388,6 +394,10 @@ static int amdgpu_userq_map_helper(struct 
amdgpu_usermode_queue *queue)
                adev->userq_funcs[queue->queue_type];
        int r;
 
+       if (READ_ONCE(queue->guilty) ||
+           queue->generation != amdgpu_vm_generation(adev, queue->vm))
+               return 0;
+
        if (queue->state == AMDGPU_USERQ_STATE_UNMAPPED) {
                trace_amdgpu_userq_state_start(queue);
 
@@ -692,6 +702,7 @@ amdgpu_userq_create(struct drm_file *filp, union 
drm_amdgpu_userq *args)
        queue->queue_type = args->in.ip_type;
        queue->vm = &fpriv->vm;
        queue->priority = priority;
+       queue->reset_counter = atomic_read(&adev->gpu_reset_counter);
        queue->generation = amdgpu_vm_generation(adev, &fpriv->vm);
        queue->xcp_id = (fpriv->xcp_id != AMDGPU_XCP_NO_PARTITION) ?
                                fpriv->xcp_id : 0;
@@ -831,14 +842,20 @@ amdgpu_userq_query_status(struct drm_file *filp, union 
drm_amdgpu_userq *args)
        }
 
        args->out_qs.flags = 0;
-       if (queue->state == AMDGPU_USERQ_STATE_HUNG)
+       if (queue->reset_counter != atomic_read(&adev->gpu_reset_counter))
+               args->out_qs.flags |= AMDGPU_USERQ_QUERY_STATUS_FLAGS_RESET;
+       if (queue->state == AMDGPU_USERQ_STATE_HUNG ||
+           READ_ONCE(queue->guilty))
                args->out_qs.flags |= AMDGPU_USERQ_QUERY_STATUS_FLAGS_HUNG;
        if (queue->generation != amdgpu_vm_generation(adev, &fpriv->vm))
                args->out_qs.flags |= AMDGPU_USERQ_QUERY_STATUS_FLAGS_VRAMLOST;
        if (queue->state == AMDGPU_USERQ_STATE_INVALID_VA)
                args->out_qs.flags |= 
AMDGPU_USERQ_QUERY_STATUS_FLAGS_INVALID_VA;
-       amdgpu_userq_put(queue);
+       if (amdgpu_in_reset(adev))
+               args->out_qs.flags |= 
AMDGPU_USERQ_QUERY_STATUS_FLAGS_RESET_IN_PROGRESS;
        mutex_unlock(&uq_mgr->userq_mutex);
+       /* The final put may destroy the queue and take userq_mutex. */
+       amdgpu_userq_put(queue);
 
        return 0;
 }
@@ -900,6 +917,7 @@ static int amdgpu_userq_input_args_validate(struct 
drm_device *dev,
                }
                break;
        case AMDGPU_USERQ_OP_FREE:
+       case AMDGPU_USERQ_OP_QUERY_STATUS:
                if (args->in.ip_type ||
                    args->in.doorbell_handle ||
                    args->in.doorbell_offset ||
@@ -1596,7 +1614,11 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, 
bool vram_lost)
        int ret = 0, r;
 
        xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
-               if (queue->state == AMDGPU_USERQ_STATE_HUNG && !vram_lost) {
+               if (vram_lost || READ_ONCE(queue->guilty) ||
+                   queue->generation != amdgpu_vm_generation(adev, queue->vm))
+                       continue;
+
+               if (queue->state == AMDGPU_USERQ_STATE_HUNG) {
                        trace_amdgpu_userq_state_start(queue);
 
                        userq_funcs = adev->userq_funcs[queue->queue_type];
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
index d1a2e2a45861..c2349115f404 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
@@ -51,6 +51,8 @@ struct amdgpu_userq_obj {
 struct amdgpu_usermode_queue {
        int                     queue_type;
        enum amdgpu_userq_state state;
+       /* Reset due to a hang; must not be re-mapped, userspace recreates. */
+       bool                    guilty;
        uint64_t                doorbell_handle;
        uint64_t                doorbell_index;
        u32                     doorbell_offset;
@@ -79,6 +81,7 @@ struct amdgpu_usermode_queue {
        struct dma_fence        *last_fence;
        u32                     xcp_id;
        int                     priority;
+       unsigned int            reset_counter;
        uint64_t                generation;
        struct dentry           *debugfs_queue;
 
diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
index 2f81ec3265cd..7e928c4e8868 100644
--- a/include/uapi/drm/amdgpu_drm.h
+++ b/include/uapi/drm/amdgpu_drm.h
@@ -360,18 +360,23 @@ union drm_amdgpu_ctx {
 #define AMDGPU_USERQ_QUERY_STATUS_FLAGS_VRAMLOST (1 << 1)
 /* the invalid userq VA unmapped */
 #define AMDGPU_USERQ_QUERY_STATUS_FLAGS_INVALID_VA (1 << 2)
+/* indicate a GPU reset occurred since the queue was created */
+#define AMDGPU_USERQ_QUERY_STATUS_FLAGS_RESET (1 << 3)
+/* indicate that a GPU reset has not completed yet */
+#define AMDGPU_USERQ_QUERY_STATUS_FLAGS_RESET_IN_PROGRESS (1 << 4)
+
 /*
  * This structure is a container to pass input configuration
  * info for all supported userqueue related operations.
  * For operation AMDGPU_USERQ_OP_CREATE: user is expected
  *  to set all fields, excep the parameter 'queue_id'.
- * For operation AMDGPU_USERQ_OP_FREE: the only input parameter expected
- *  to be set is 'queue_id', eveything else is ignored.
+ * For operations AMDGPU_USERQ_OP_FREE and AMDGPU_USERQ_OP_QUERY_STATUS:
+ *  only 'op' and 'queue_id' should be set; all other fields must be zero.
  */
 struct drm_amdgpu_userq_in {
        /** AMDGPU_USERQ_OP_* */
        __u32   op;
-       /** Queue id passed for operation USERQ_OP_FREE */
+       /** Queue id passed for operations USERQ_OP_FREE and 
USERQ_OP_QUERY_STATUS */
        __u32   queue_id;
        /** the target GPU engine to execute workload (AMDGPU_HW_IP_*) */
        __u32   ip_type;
-- 
2.34.1

Reply via email to