From: Sultan Alsawaf <sultan@xxxxxxxxxxxxxxx> The following deadlock exists in i915_active_wait() due to a double lock on ref->mutex (call chain listed in order from top to bottom): i915_active_wait(); mutex_lock_interruptible(&ref->mutex); <-- ref->mutex first acquired i915_active_request_retire(); node_retire(); active_retire(); mutex_lock_nested(&ref->mutex, SINGLE_DEPTH_NESTING); <-- DEADLOCK Fix the deadlock by skipping the second ref->mutex lock when active_retire() is called through i915_active_request_retire(). Fixes: 12c255b5dad1 ("drm/i915: Provide an i915_active.acquire callback") Cc: <stable@xxxxxxxxxxxxxxx> # 5.4.x Signed-off-by: Sultan Alsawaf <sultan@xxxxxxxxxxxxxxx> --- drivers/gpu/drm/i915/i915_active.c | 27 +++++++++++++++++++++++---- drivers/gpu/drm/i915/i915_active.h | 4 ++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c index 48e16ad93bbd..cfc77c08a273 100644 --- a/drivers/gpu/drm/i915/i915_active.c +++ b/drivers/gpu/drm/i915/i915_active.c @@ -120,13 +120,17 @@ static inline void debug_active_assert(struct i915_active *ref) { } #endif +#define I915_ACTIVE_RETIRE_NOLOCK BIT(0) + static void __active_retire(struct i915_active *ref) { struct active_node *it, *n; struct rb_root root; bool retire = false; + unsigned long bits; + ref = ptr_unpack_bits(ref, &bits, 2); lockdep_assert_held(&ref->mutex); /* return the unused nodes to our slabcache -- flushing the allocator */ @@ -138,7 +142,8 @@ __active_retire(struct i915_active *ref) retire = true; } - mutex_unlock(&ref->mutex); + if (!(bits & I915_ACTIVE_RETIRE_NOLOCK)) + mutex_unlock(&ref->mutex); if (!retire) return; @@ -155,13 +160,18 @@ __active_retire(struct i915_active *ref) static void active_retire(struct i915_active *ref) { + struct i915_active *ref_packed = ref; + unsigned long bits; + + ref = ptr_unpack_bits(ref, &bits, 2); GEM_BUG_ON(!atomic_read(&ref->count)); if (atomic_add_unless(&ref->count, -1, 1)) return; /* One active may be flushed from inside the acquire of another */ - mutex_lock_nested(&ref->mutex, SINGLE_DEPTH_NESTING); - __active_retire(ref); + if (!(bits & I915_ACTIVE_RETIRE_NOLOCK)) + mutex_lock_nested(&ref->mutex, SINGLE_DEPTH_NESTING); + __active_retire(ref_packed); } static void @@ -170,6 +180,14 @@ node_retire(struct i915_active_request *base, struct i915_request *rq) active_retire(node_from_active(base)->ref); } +static void +node_retire_nolock(struct i915_active_request *base, struct i915_request *rq) +{ + struct i915_active *ref = node_from_active(base)->ref; + + active_retire(ptr_pack_bits(ref, I915_ACTIVE_RETIRE_NOLOCK, 2)); +} + static struct i915_active_request * active_instance(struct i915_active *ref, struct intel_timeline *tl) { @@ -421,7 +439,8 @@ int i915_active_wait(struct i915_active *ref) break; } - err = i915_active_request_retire(&it->base, BKL(ref)); + err = i915_active_request_retire(&it->base, BKL(ref), + node_retire_nolock); if (err) break; } diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h index f95058f99057..0ad7ef60d15f 100644 --- a/drivers/gpu/drm/i915/i915_active.h +++ b/drivers/gpu/drm/i915/i915_active.h @@ -309,7 +309,7 @@ i915_active_request_isset(const struct i915_active_request *active) */ static inline int __must_check i915_active_request_retire(struct i915_active_request *active, - struct mutex *mutex) + struct mutex *mutex, i915_active_retire_fn retire) { struct i915_request *request; long ret; @@ -327,7 +327,7 @@ i915_active_request_retire(struct i915_active_request *active, list_del_init(&active->link); RCU_INIT_POINTER(active->request, NULL); - active->retire(active, request); + retire(active, request); return 0; } -- 2.26.0