Skip to content

Commit d12acee

Browse files
committed
drm/i915/execlists: Cancel banned contexts on schedule-out
On schedule-out (CS completion) of a banned context, scrub the context image so that we do not replay the active payload. The intent is that we skip banned payloads on request submission so that the timeline advancement continues on in the background. However, if we are returning to a preempted request, i915_request_skip() is ineffective and instead we need to patch up the context image so that it continues from the start of the next request. v2: Fixup cancellation so that we only scrub the payload of the active request and do not short-circuit the breadcrumbs (which might cause other contexts to execute out of order). v3: Grammar pass 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/20191023133108.21401-3-chris@chris-wilson.co.uk
1 parent 3a7a92a commit d12acee

File tree

2 files changed

+411
-41
lines changed

2 files changed

+411
-41
lines changed

drivers/gpu/drm/i915/gt/intel_lrc.c

Lines changed: 90 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ static void execlists_init_reg_state(u32 *reg_state,
234234
const struct intel_engine_cs *engine,
235235
const struct intel_ring *ring,
236236
bool close);
237+
static void
238+
__execlists_update_reg_state(const struct intel_context *ce,
239+
const struct intel_engine_cs *engine);
237240

238241
static void mark_eio(struct i915_request *rq)
239242
{
@@ -246,6 +249,31 @@ static void mark_eio(struct i915_request *rq)
246249
i915_request_mark_complete(rq);
247250
}
248251

252+
static struct i915_request *active_request(struct i915_request *rq)
253+
{
254+
const struct intel_context * const ce = rq->hw_context;
255+
struct i915_request *active = NULL;
256+
struct list_head *list;
257+
258+
if (!i915_request_is_active(rq)) /* unwound, but incomplete! */
259+
return rq;
260+
261+
rcu_read_lock();
262+
list = &rcu_dereference(rq->timeline)->requests;
263+
list_for_each_entry_from_reverse(rq, list, link) {
264+
if (i915_request_completed(rq))
265+
break;
266+
267+
if (rq->hw_context != ce)
268+
break;
269+
270+
active = rq;
271+
}
272+
rcu_read_unlock();
273+
274+
return active;
275+
}
276+
249277
static inline u32 intel_hws_preempt_address(struct intel_engine_cs *engine)
250278
{
251279
return (i915_ggtt_offset(engine->status_page.vma) +
@@ -972,6 +1000,58 @@ static void kick_siblings(struct i915_request *rq, struct intel_context *ce)
9721000
tasklet_schedule(&ve->base.execlists.tasklet);
9731001
}
9741002

1003+
static void restore_default_state(struct intel_context *ce,
1004+
struct intel_engine_cs *engine)
1005+
{
1006+
u32 *regs = ce->lrc_reg_state;
1007+
1008+
if (engine->pinned_default_state)
1009+
memcpy(regs, /* skip restoring the vanilla PPHWSP */
1010+
engine->pinned_default_state + LRC_STATE_PN * PAGE_SIZE,
1011+
engine->context_size - PAGE_SIZE);
1012+
1013+
execlists_init_reg_state(regs, ce, engine, ce->ring, false);
1014+
}
1015+
1016+
static void reset_active(struct i915_request *rq,
1017+
struct intel_engine_cs *engine)
1018+
{
1019+
struct intel_context * const ce = rq->hw_context;
1020+
1021+
/*
1022+
* The executing context has been cancelled. We want to prevent
1023+
* further execution along this context and propagate the error on
1024+
* to anything depending on its results.
1025+
*
1026+
* In __i915_request_submit(), we apply the -EIO and remove the
1027+
* requests' payloads for any banned requests. But first, we must
1028+
* rewind the context back to the start of the incomplete request so
1029+
* that we do not jump back into the middle of the batch.
1030+
*
1031+
* We preserve the breadcrumbs and semaphores of the incomplete
1032+
* requests so that inter-timeline dependencies (i.e other timelines)
1033+
* remain correctly ordered. And we defer to __i915_request_submit()
1034+
* so that all asynchronous waits are correctly handled.
1035+
*/
1036+
GEM_TRACE("%s(%s): { rq=%llx:%lld }\n",
1037+
__func__, engine->name, rq->fence.context, rq->fence.seqno);
1038+
1039+
/* On resubmission of the active request, payload will be scrubbed */
1040+
rq = active_request(rq);
1041+
if (rq)
1042+
ce->ring->head = intel_ring_wrap(ce->ring, rq->head);
1043+
else
1044+
ce->ring->head = ce->ring->tail;
1045+
intel_ring_update_space(ce->ring);
1046+
1047+
/* Scrub the context image to prevent replaying the previous batch */
1048+
restore_default_state(ce, engine);
1049+
__execlists_update_reg_state(ce, engine);
1050+
1051+
/* We've switched away, so this should be a no-op, but intent matters */
1052+
ce->lrc_desc |= CTX_DESC_FORCE_RESTORE;
1053+
}
1054+
9751055
static inline void
9761056
__execlists_schedule_out(struct i915_request *rq,
9771057
struct intel_engine_cs * const engine)
@@ -982,6 +1062,9 @@ __execlists_schedule_out(struct i915_request *rq,
9821062
execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_OUT);
9831063
intel_gt_pm_put(engine->gt);
9841064

1065+
if (unlikely(i915_gem_context_is_banned(ce->gem_context)))
1066+
reset_active(rq, engine);
1067+
9851068
/*
9861069
* If this is part of a virtual engine, its next request may
9871070
* have been blocked waiting for access to the active context.
@@ -1380,6 +1463,10 @@ static unsigned long active_preempt_timeout(struct intel_engine_cs *engine)
13801463
if (!rq)
13811464
return 0;
13821465

1466+
/* Force a fast reset for terminated contexts (ignoring sysfs!) */
1467+
if (unlikely(i915_gem_context_is_banned(rq->gem_context)))
1468+
return 1;
1469+
13831470
return READ_ONCE(engine->props.preempt_timeout_ms);
13841471
}
13851472

@@ -2792,29 +2879,6 @@ static void reset_csb_pointers(struct intel_engine_cs *engine)
27922879
&execlists->csb_status[reset_value]);
27932880
}
27942881

2795-
static struct i915_request *active_request(struct i915_request *rq)
2796-
{
2797-
const struct intel_context * const ce = rq->hw_context;
2798-
struct i915_request *active = NULL;
2799-
struct list_head *list;
2800-
2801-
if (!i915_request_is_active(rq)) /* unwound, but incomplete! */
2802-
return rq;
2803-
2804-
list = &i915_request_active_timeline(rq)->requests;
2805-
list_for_each_entry_from_reverse(rq, list, link) {
2806-
if (i915_request_completed(rq))
2807-
break;
2808-
2809-
if (rq->hw_context != ce)
2810-
break;
2811-
2812-
active = rq;
2813-
}
2814-
2815-
return active;
2816-
}
2817-
28182882
static void __execlists_reset_reg_state(const struct intel_context *ce,
28192883
const struct intel_engine_cs *engine)
28202884
{
@@ -2831,7 +2895,6 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
28312895
struct intel_engine_execlists * const execlists = &engine->execlists;
28322896
struct intel_context *ce;
28332897
struct i915_request *rq;
2834-
u32 *regs;
28352898

28362899
mb(); /* paranoia: read the CSB pointers from after the reset */
28372900
clflush(execlists->csb_write);
@@ -2907,13 +2970,7 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
29072970
* to recreate its own state.
29082971
*/
29092972
GEM_BUG_ON(!intel_context_is_pinned(ce));
2910-
regs = ce->lrc_reg_state;
2911-
if (engine->pinned_default_state) {
2912-
memcpy(regs, /* skip restoring the vanilla PPHWSP */
2913-
engine->pinned_default_state + LRC_STATE_PN * PAGE_SIZE,
2914-
engine->context_size - PAGE_SIZE);
2915-
}
2916-
execlists_init_reg_state(regs, ce, engine, ce->ring, false);
2973+
restore_default_state(ce, engine);
29172974

29182975
out_replay:
29192976
GEM_TRACE("%s replay {head:%04x, tail:%04x\n",
@@ -4574,16 +4631,8 @@ void intel_lr_context_reset(struct intel_engine_cs *engine,
45744631
* future request will be after userspace has had the opportunity
45754632
* to recreate its own state.
45764633
*/
4577-
if (scrub) {
4578-
u32 *regs = ce->lrc_reg_state;
4579-
4580-
if (engine->pinned_default_state) {
4581-
memcpy(regs, /* skip restoring the vanilla PPHWSP */
4582-
engine->pinned_default_state + LRC_STATE_PN * PAGE_SIZE,
4583-
engine->context_size - PAGE_SIZE);
4584-
}
4585-
execlists_init_reg_state(regs, ce, engine, ce->ring, false);
4586-
}
4634+
if (scrub)
4635+
restore_default_state(ce, engine);
45874636

45884637
/* Rerun the request; its payload has been neutered (if guilty). */
45894638
ce->ring->head = head;

0 commit comments

Comments
 (0)