Commit f6e98a18 authored by Chris Wilson's avatar Chris Wilson Committed by Jani Nikula
Browse files

drm/i915: Always flush the active worker before returning from the wait

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: 274cbf20 ("drm/i915: Push the i915_active.retire into a worker")
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: <stable@vger.kernel.org> # v5.5+
Reviewed-by: default avatarMatthew Auld <matthew.auld@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210121232807.16618-1-chris@chris-wilson.co.uk


(cherry picked from commit 977a372e972cb42799746c284035a33c64ebace9)
Signed-off-by: default avatarJani Nikula <jani.nikula@intel.com>
parent 3d480fe1
Loading
Loading
Loading
Loading
+15 −13
Original line number Diff line number Diff line
@@ -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 */
	if (i915_active_acquire_if_busy(ref)) {
		int err;

		err = flush_lazy_signals(ref);
		i915_active_release(ref);
		if (err)
			return err;

	if (!i915_active_is_idle(ref) &&
	    ___wait_var_event(ref, i915_active_is_idle(ref),
		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;
}