Skip to content

Commit

Permalink
drm/i915/gt: Transfer old virtual breadcrumbs to irq_worker
Browse files Browse the repository at this point in the history
The second try at staging the transfer of the breadcrumb. In part one,
we realised we could not simply move to the second engine as we were
only holding the breadcrumb lock on the first. So in commit 6c81e21
("drm/i915/gt: Stage the transfer of the virtual breadcrumb"), we
removed it from the first engine and marked up this request to reattach
the signaling on the new engine. However, this failed to take into
account that we only attach the breadcrumb if the new request is added
at the start of the queue, which if we are transferring, it is because
we know there to be a request to be signaled (and hence we would not be
attached).

In this attempt, we try to transfer the completed requests to the
irq_worker on its rq->engine->breadcrumbs. This preserves the coupling
between the rq and its breadcrumbs, so that
i915_request_cancel_breadcrumb() does not attempt to manipulate the list
under the wrong lock.

v2: Code sharing is fun.

Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/1862
Fixes: 6c81e21 ("drm/i915/gt: Stage the transfer of the virtual breadcrumb")
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200513074809.18194-1-chris@chris-wilson.co.uk
  • Loading branch information
Chris Wilson committed May 14, 2020
1 parent ed610f4 commit 0f4013f
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 34 deletions.
52 changes: 42 additions & 10 deletions drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ static void add_retire(struct intel_breadcrumbs *b, struct intel_timeline *tl)
intel_engine_add_retire(engine, tl);
}

static void __signal_request(struct i915_request *rq, struct list_head *signals)
{
GEM_BUG_ON(!test_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags));
clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags);

if (!__dma_fence_signal(&rq->fence))
return;

i915_request_get(rq);
list_add_tail(&rq->signal_link, signals);
}

static void signal_irq_work(struct irq_work *work)
{
struct intel_breadcrumbs *b = container_of(work, typeof(*b), irq_work);
Expand All @@ -155,6 +167,8 @@ static void signal_irq_work(struct irq_work *work)
if (b->irq_armed && list_empty(&b->signalers))
__intel_breadcrumbs_disarm_irq(b);

list_splice_init(&b->signaled_requests, &signal);

list_for_each_entry_safe(ce, cn, &b->signalers, signal_link) {
GEM_BUG_ON(list_empty(&ce->signals));

Expand All @@ -163,24 +177,15 @@ static void signal_irq_work(struct irq_work *work)
list_entry(pos, typeof(*rq), signal_link);

GEM_BUG_ON(!check_signal_order(ce, rq));

if (!__request_completed(rq))
break;

GEM_BUG_ON(!test_bit(I915_FENCE_FLAG_SIGNAL,
&rq->fence.flags));
clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags);

if (!__dma_fence_signal(&rq->fence))
continue;

/*
* Queue for execution after dropping the signaling
* spinlock as the callback chain may end up adding
* more signalers to the same context or engine.
*/
i915_request_get(rq);
list_add_tail(&rq->signal_link, &signal);
__signal_request(rq, &signal);
}

/*
Expand Down Expand Up @@ -255,6 +260,7 @@ void intel_engine_init_breadcrumbs(struct intel_engine_cs *engine)

spin_lock_init(&b->irq_lock);
INIT_LIST_HEAD(&b->signalers);
INIT_LIST_HEAD(&b->signaled_requests);

init_irq_work(&b->irq_work, signal_irq_work);
}
Expand All @@ -274,6 +280,32 @@ void intel_engine_reset_breadcrumbs(struct intel_engine_cs *engine)
spin_unlock_irqrestore(&b->irq_lock, flags);
}

void intel_engine_transfer_stale_breadcrumbs(struct intel_engine_cs *engine,
struct intel_context *ce)
{
struct intel_breadcrumbs *b = &engine->breadcrumbs;
unsigned long flags;

spin_lock_irqsave(&b->irq_lock, flags);
if (!list_empty(&ce->signals)) {
struct i915_request *rq, *next;

/* Queue for executing the signal callbacks in the irq_work */
list_for_each_entry_safe(rq, next, &ce->signals, signal_link) {
GEM_BUG_ON(rq->engine != engine);
GEM_BUG_ON(!__request_completed(rq));

__signal_request(rq, &b->signaled_requests);
}

INIT_LIST_HEAD(&ce->signals);
list_del_init(&ce->signal_link);

irq_work_queue(&b->irq_work);
}
spin_unlock_irqrestore(&b->irq_lock, flags);
}

void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine)
{
}
Expand Down
3 changes: 3 additions & 0 deletions drivers/gpu/drm/i915/gt/intel_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ intel_engine_signal_breadcrumbs(struct intel_engine_cs *engine)
void intel_engine_reset_breadcrumbs(struct intel_engine_cs *engine);
void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine);

void intel_engine_transfer_stale_breadcrumbs(struct intel_engine_cs *engine,
struct intel_context *ce);

void intel_engine_print_breadcrumbs(struct intel_engine_cs *engine,
struct drm_printer *p);

Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/i915/gt/intel_engine_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ struct intel_engine_cs {
spinlock_t irq_lock;
struct list_head signalers;

struct list_head signaled_requests;

struct irq_work irq_work; /* for use from inside irq_lock */

unsigned int irq_enabled;
Expand Down
34 changes: 10 additions & 24 deletions drivers/gpu/drm/i915/gt/intel_lrc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1812,30 +1812,16 @@ static bool virtual_matches(const struct virtual_engine *ve,
return true;
}

static void virtual_xfer_breadcrumbs(struct virtual_engine *ve,
struct i915_request *rq)
static void virtual_xfer_breadcrumbs(struct virtual_engine *ve)
{
struct intel_engine_cs *old = ve->siblings[0];

/* All unattached (rq->engine == old) must already be completed */

spin_lock(&old->breadcrumbs.irq_lock);
if (!list_empty(&ve->context.signal_link)) {
list_del_init(&ve->context.signal_link);

/*
* We cannot acquire the new engine->breadcrumbs.irq_lock
* (as we are holding a breadcrumbs.irq_lock already),
* so attach this request to the signaler on submission.
* The queued irq_work will occur when we finally drop
* the engine->active.lock after dequeue.
*/
set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &rq->fence.flags);

/* Also transfer the pending irq_work for the old breadcrumb. */
intel_engine_signal_breadcrumbs(rq->engine);
}
spin_unlock(&old->breadcrumbs.irq_lock);
/*
* All the outstanding signals on ve->siblings[0] must have
* been completed, just pending the interrupt handler. As those
* signals still refer to the old sibling (via rq->engine), we must
* transfer those to the old irq_worker to keep our locking
* consistent.
*/
intel_engine_transfer_stale_breadcrumbs(ve->siblings[0], &ve->context);
}

#define for_each_waiter(p__, rq__) \
Expand Down Expand Up @@ -2270,7 +2256,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
engine);

if (!list_empty(&ve->context.signals))
virtual_xfer_breadcrumbs(ve, rq);
virtual_xfer_breadcrumbs(ve);

/*
* Move the bound engine to the top of the list
Expand Down

0 comments on commit 0f4013f

Please sign in to comment.