The last kref of a user queue may be released from a context where can not sleep or require an alrady hold lock(reset_domain->sem, reset_mutext, etc) in the same context.
This commit helps resovle these issues by refactoring the user queue destruction function to a deferred work item, so it does not conflict with the caller context. Signed-off-by: Zhu Lingshan <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 15 ++++++++++++--- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 7 +++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index f398986a61a5..9fe20cb9af58 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -596,11 +596,11 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que return r; } -static void amdgpu_userq_kref_destroy(struct kref *kref) +static void amdgpu_userq_destroy_worker(struct work_struct *work) { int r; struct amdgpu_usermode_queue *queue = - container_of(kref, struct amdgpu_usermode_queue, refcount); + container_of(work, struct amdgpu_usermode_queue, destroy_work); struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; r = amdgpu_userq_destroy(uq_mgr, queue); @@ -610,6 +610,14 @@ static void amdgpu_userq_kref_destroy(struct kref *kref) amdgpu_userq_mgr_dec_refs(uq_mgr); } +static void amdgpu_userq_kref_release(struct kref *kref) +{ + struct amdgpu_usermode_queue *queue = + container_of(kref, struct amdgpu_usermode_queue, refcount); + + WARN_ON_ONCE(!queue_work(system_unbound_wq, &queue->destroy_work)); +} + struct amdgpu_usermode_queue *amdgpu_userq_get(struct amdgpu_userq_mgr *uq_mgr, u32 qid) { struct amdgpu_usermode_queue *queue; @@ -656,7 +664,7 @@ amdgpu_lookup_queue_by_doorbell(struct xarray *xa, u32 doorbell) void amdgpu_userq_put(struct amdgpu_usermode_queue *queue) { if (queue) - kref_put(&queue->refcount, amdgpu_userq_kref_destroy); + kref_put(&queue->refcount, amdgpu_userq_kref_release); } static int amdgpu_userq_priority_permit(struct drm_file *filp, @@ -724,6 +732,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) amdgpu_userq_mgr_inc_refs(uq_mgr); INIT_DELAYED_WORK(&queue->hang_detect_work, amdgpu_userq_hang_detect_work); + INIT_WORK(&queue->destroy_work, amdgpu_userq_destroy_worker); r = amdgpu_userq_fence_driver_alloc(adev, &queue->fence_drv); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index a13d8d4dd5c7..ecc450be7eb2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -87,6 +87,13 @@ struct amdgpu_usermode_queue { * Delayed work which runs when userq_fences time out. */ struct delayed_work hang_detect_work; + + /** + * @destroy_work: + * + * Work struct used to destroy the queue when kref == 0 + */ + struct work_struct destroy_work; struct kref refcount; union { -- 2.53.0
