amdgpu_userq_wait_ioctl() takes extra references on waited-on fence
drivers and stores them in waitq->fence_drv_xa. When a new userq fence is
created, those references are transferred into userq_fence->fence_drv_array
so they can be released when the fence completes.

However, those inherited references are currently only dropped from
amdgpu_userq_fence_driver_process(). If a fence never reaches that path,
such as it is already signaled when created, so we need to explicitly release
those fences in that case.

v2: use a list(list_cut_before) for managing the signal userq driver 
fences.(Christian)

Signed-off-by: Prike Liang <[email protected]>
---
 .../gpu/drm/amd/amdgpu/amdgpu_userq_fence.c   | 56 ++++++++++++++-----
 1 file changed, 41 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
index 3be80a82788a..ce3446a77c88 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
@@ -145,35 +145,56 @@ amdgpu_userq_fence_driver_free(struct 
amdgpu_usermode_queue *userq)
        amdgpu_userq_fence_driver_put(userq->fence_drv);
 }
 
+static void
+amdgpu_userq_fence_put_fence_drv_array(struct amdgpu_userq_fence *userq_fence)
+{
+       unsigned long i;
+       for (i = 0; i < userq_fence->fence_drv_array_count; i++)
+               amdgpu_userq_fence_driver_put(userq_fence->fence_drv_array[i]);
+       userq_fence->fence_drv_array_count = 0;
+}
+
 void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver 
*fence_drv)
 {
-       struct amdgpu_userq_fence *userq_fence, *tmp;
+       struct amdgpu_userq_fence *userq_fence, *tmp, *first_unsignaled = NULL;
        struct dma_fence *fence;
        unsigned long flags;
        u64 rptr;
-       int i;
+       LIST_HEAD(to_be_signaled);
 
        if (!fence_drv)
                return;
 
        spin_lock_irqsave(&fence_drv->fence_list_lock, flags);
        rptr = amdgpu_userq_fence_read(fence_drv);
+       userq_fence = NULL;
+       fence = NULL;
 
-       list_for_each_entry_safe(userq_fence, tmp, &fence_drv->fences, link) {
-               fence = &userq_fence->base;
-
-               if (rptr < fence->seqno)
+       list_for_each_entry(userq_fence, &fence_drv->fences, link) {
+               if (rptr < userq_fence->base.seqno) {
+                       first_unsignaled = userq_fence;
                        break;
+               }
+       }
 
-               dma_fence_signal(fence);
-
-               for (i = 0; i < userq_fence->fence_drv_array_count; i++)
-                       
amdgpu_userq_fence_driver_put(userq_fence->fence_drv_array[i]);
+       if (first_unsignaled)
+               list_cut_before(&to_be_signaled, &fence_drv->fences,
+                               &first_unsignaled->link);
+       else
+               list_splice_init(&fence_drv->fences, &to_be_signaled);
+       spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags);
 
-               list_del(&userq_fence->link);
+       list_for_each_entry_safe(userq_fence, tmp, &to_be_signaled, link) {
+               fence = &userq_fence->base;
+               list_del_init(&userq_fence->link);
+               dma_fence_signal(fence);
+               /* Drop fence_drv_array outside fence_list_lock
+                * to avoid the recursion lock.
+                */
+               amdgpu_userq_fence_put_fence_drv_array(userq_fence);
                dma_fence_put(fence);
        }
-       spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags);
+
 }
 
 void amdgpu_userq_fence_driver_destroy(struct kref *ref)
@@ -228,6 +249,7 @@ static int amdgpu_userq_fence_create(struct 
amdgpu_usermode_queue *userq,
        struct amdgpu_userq_fence_driver *fence_drv;
        struct dma_fence *fence;
        unsigned long flags;
+       bool signaled = false;
 
        fence_drv = userq->fence_drv;
        if (!fence_drv)
@@ -275,13 +297,17 @@ static int amdgpu_userq_fence_create(struct 
amdgpu_usermode_queue *userq,
 
        /* Check if hardware has already processed the job */
        spin_lock_irqsave(&fence_drv->fence_list_lock, flags);
-       if (!dma_fence_is_signaled(fence))
+       if (!dma_fence_is_signaled(fence)) {
                list_add_tail(&userq_fence->link, &fence_drv->fences);
-       else
+       } else {
+               signaled = true;
                dma_fence_put(fence);
-
+       }
        spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags);
 
+       if (signaled)
+               amdgpu_userq_fence_put_fence_drv_array(userq_fence);
+
        *f = fence;
 
        return 0;
-- 
2.34.1

Reply via email to