From: Chris Wilson <ch...@chris-wilson.co.uk>

commit f6e98a1809faa02f40e0d089d6cfc1aa372a34c0 upstream.

The first thing the active retirement worker does is decrement the
i915_active count.

The first thing we do during i915_active_wait is try to increment the
i915_active count, but only if already active [non-zero].

The wait may see that the retirement is already started and so marked the
i915_active as idle, and skip waiting for the retirement handler.
However, the caller of i915_active_wait may immediately free the
i915_active upon returning (e.g. i915_vma_destroy) so we must not return
before the concurrent access from the worker is completed. We must
always flush the worker.

Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/2473
Fixes: 274cbf20fd10 ("drm/i915: Push the i915_active.retire into a worker")
Signed-off-by: Chris Wilson <ch...@chris-wilson.co.uk>
Cc: Matthew Auld <matthew.a...@intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursu...@intel.com>
Cc: <sta...@vger.kernel.org> # v5.5+
Reviewed-by: Matthew Auld <matthew.a...@intel.com>
Link: 
https://patchwork.freedesktop.org/patch/msgid/20210121232807.16618-1-ch...@chris-wilson.co.uk
(cherry picked from commit 977a372e972cb42799746c284035a33c64ebace9)
Signed-off-by: Jani Nikula <jani.nik...@intel.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

diff --git a/drivers/gpu/drm/i915/i915_active.c 
b/drivers/gpu/drm/i915/i915_active.c
index 10a865f3dc09..9ed19b8bca60 100644
--- a/drivers/gpu/drm/i915/i915_active.c
+++ b/drivers/gpu/drm/i915/i915_active.c
@@ -631,24 +631,26 @@ static int flush_lazy_signals(struct i915_active *ref)
 
 int __i915_active_wait(struct i915_active *ref, int state)
 {
-       int err;
-
        might_sleep();
 
-       if (!i915_active_acquire_if_busy(ref))
-               return 0;
-
        /* Any fence added after the wait begins will not be auto-signaled */
-       err = flush_lazy_signals(ref);
-       i915_active_release(ref);
-       if (err)
-               return err;
+       if (i915_active_acquire_if_busy(ref)) {
+               int err;
 
-       if (!i915_active_is_idle(ref) &&
-           ___wait_var_event(ref, i915_active_is_idle(ref),
-                             state, 0, 0, schedule()))
-               return -EINTR;
+               err = flush_lazy_signals(ref);
+               i915_active_release(ref);
+               if (err)
+                       return err;
 
+               if (___wait_var_event(ref, i915_active_is_idle(ref),
+                                     state, 0, 0, schedule()))
+                       return -EINTR;
+       }
+
+       /*
+        * After the wait is complete, the caller may free the active.
+        * We have to flush any concurrent retirement before returning.
+        */
        flush_work(&ref->work);
        return 0;
 }


Reply via email to