[Intel-gfx] [PATCH 11/20] drm/i915: Protect i915_request_await_start from early waits
Tvrtko Ursulin
tvrtko.ursulin at linux.intel.com
Fri Feb 28 12:41:31 UTC 2020
On 27/02/2020 08:57, Chris Wilson wrote:
> We need to be extremely careful inside i915_request_await_start() as it
> needs to walk the list of requests in the foreign timeline with very
> little protection. As we hold our own timeline mutex, we can not nest
> inside the signaler's timeline mutex, so all that remains is our RCU
> protection. However, to be safe we need to tell the compiler that we may
> be traversing the list only under RCU protection, and furthermore we
> need to start declaring requests as elements of the timeline from their
> construction.
>
> Fixes: 9ddc8ec027a3 ("drm/i915: Eliminate the trylock for awaiting an earlier request")
> Fixes: 6a79d848403d ("drm/i915: Lock signaler timeline while navigating")
> Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
> ---
> drivers/gpu/drm/i915/i915_request.c | 41 ++++++++++++++++++++---------
> 1 file changed, 28 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
> index d53af93b919b..e5a55801f753 100644
> --- a/drivers/gpu/drm/i915/i915_request.c
> +++ b/drivers/gpu/drm/i915/i915_request.c
> @@ -290,7 +290,7 @@ bool i915_request_retire(struct i915_request *rq)
> spin_unlock_irq(&rq->lock);
>
> remove_from_client(rq);
> - list_del(&rq->link);
> + list_del_rcu(&rq->link);
>
> intel_context_exit(rq->context);
> intel_context_unpin(rq->context);
> @@ -736,6 +736,8 @@ __i915_request_create(struct intel_context *ce, gfp_t gfp)
> rq->infix = rq->ring->emit; /* end of header; start of user payload */
>
> intel_context_mark_active(ce);
> + list_add_tail_rcu(&rq->link, &tl->requests);
> +
> return rq;
>
> err_unwind:
> @@ -792,13 +794,23 @@ i915_request_await_start(struct i915_request *rq, struct i915_request *signal)
> GEM_BUG_ON(i915_request_timeline(rq) ==
> rcu_access_pointer(signal->timeline));
>
> + if (i915_request_started(signal))
> + return 0;
> +
> fence = NULL;
> rcu_read_lock();
> spin_lock_irq(&signal->lock);
> - if (!i915_request_started(signal) &&
> - !list_is_first(&signal->link,
> - &rcu_dereference(signal->timeline)->requests)) {
> - struct i915_request *prev = list_prev_entry(signal, link);
> + do {
> + struct list_head *pos = READ_ONCE(signal->link.prev);
> + struct i915_request *prev;
> +
> + /* Confirm signal has not been retired, the link is valid */
> + if (unlikely(i915_request_started(signal)))
> + break;
> +
> + /* Is signal the earliest request on its timeline? */
> + if (pos == &rcu_dereference(signal->timeline)->requests)
> + break;
>
> /*
> * Peek at the request before us in the timeline. That
> @@ -806,13 +818,18 @@ i915_request_await_start(struct i915_request *rq, struct i915_request *signal)
> * after acquiring a reference to it, confirm that it is
> * still part of the signaler's timeline.
> */
> - if (i915_request_get_rcu(prev)) {
> - if (list_next_entry(prev, link) == signal)
> - fence = &prev->fence;
> - else
> - i915_request_put(prev);
> + prev = list_entry(pos, typeof(*prev), link);
> + if (!i915_request_get_rcu(prev))
> + break;
> +
> + /* After the strong barrier, confirm prev is still attached */
> + if (unlikely(READ_ONCE(prev->link.next) != &signal->link)) {
> + i915_request_put(prev);
> + break;
> }
> - }
> +
> + fence = &prev->fence;
> + } while (0);
> spin_unlock_irq(&signal->lock);
> rcu_read_unlock();
> if (!fence)
> @@ -1253,8 +1270,6 @@ __i915_request_add_to_timeline(struct i915_request *rq)
> 0);
> }
>
> - list_add_tail(&rq->link, &timeline->requests);
> -
> /*
> * Make sure that no request gazumped us - if it was allocated after
> * our i915_request_alloc() and called __i915_request_add() before
>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
Regards,
Tvrtko
More information about the Intel-gfx
mailing list