drm/i915/execlists: Force preemption
If the preempted context takes too long to relinquish control, e.g. it is stuck inside a shader with arbitration disabled, evict that context with an engine reset. This ensures that preemptions are reasonably responsive, providing a tighter QoS for the more important context at the cost of flagging unresponsive contexts more frequently (i.e. instead of using an ~10s hangcheck, we now evict at ~100ms). The challenge of lies in picking a timeout that can be reasonably serviced by HW for typical workloads, balancing the existing clients against the needs for responsiveness. Note that coupled with timeslicing, this will lead to rapid GPU "hang" detection with multiple active contexts vying for GPU time. The forced preemption mechanism can be compiled out with ./scripts/config --set-val DRM_I915_PREEMPT_TIMEOUT 0 Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com> Reviewed-by: Mika Kuoppala <mika.kuoppala@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20191023133108.21401-2-chris@chris-wilson.co.uk
This commit is contained in:
@ -1372,6 +1372,26 @@ static void record_preemption(struct intel_engine_execlists *execlists)
|
||||
(void)I915_SELFTEST_ONLY(execlists->preempt_hang.count++);
|
||||
}
|
||||
|
||||
static unsigned long active_preempt_timeout(struct intel_engine_cs *engine)
|
||||
{
|
||||
struct i915_request *rq;
|
||||
|
||||
rq = last_active(&engine->execlists);
|
||||
if (!rq)
|
||||
return 0;
|
||||
|
||||
return READ_ONCE(engine->props.preempt_timeout_ms);
|
||||
}
|
||||
|
||||
static void set_preempt_timeout(struct intel_engine_cs *engine)
|
||||
{
|
||||
if (!intel_engine_has_preempt_reset(engine))
|
||||
return;
|
||||
|
||||
set_timer_ms(&engine->execlists.preempt,
|
||||
active_preempt_timeout(engine));
|
||||
}
|
||||
|
||||
static void execlists_dequeue(struct intel_engine_cs *engine)
|
||||
{
|
||||
struct intel_engine_execlists * const execlists = &engine->execlists;
|
||||
@ -1747,6 +1767,8 @@ done:
|
||||
|
||||
memset(port + 1, 0, (last_port - port) * sizeof(*port));
|
||||
execlists_submit_ports(engine);
|
||||
|
||||
set_preempt_timeout(engine);
|
||||
} else {
|
||||
skip_submit:
|
||||
ring_set_paused(engine, 0);
|
||||
@ -1987,6 +2009,43 @@ static void __execlists_submission_tasklet(struct intel_engine_cs *const engine)
|
||||
}
|
||||
}
|
||||
|
||||
static noinline void preempt_reset(struct intel_engine_cs *engine)
|
||||
{
|
||||
const unsigned int bit = I915_RESET_ENGINE + engine->id;
|
||||
unsigned long *lock = &engine->gt->reset.flags;
|
||||
|
||||
if (i915_modparams.reset < 3)
|
||||
return;
|
||||
|
||||
if (test_and_set_bit(bit, lock))
|
||||
return;
|
||||
|
||||
/* Mark this tasklet as disabled to avoid waiting for it to complete */
|
||||
tasklet_disable_nosync(&engine->execlists.tasklet);
|
||||
|
||||
GEM_TRACE("%s: preempt timeout %lu+%ums\n",
|
||||
engine->name,
|
||||
READ_ONCE(engine->props.preempt_timeout_ms),
|
||||
jiffies_to_msecs(jiffies - engine->execlists.preempt.expires));
|
||||
intel_engine_reset(engine, "preemption time out");
|
||||
|
||||
tasklet_enable(&engine->execlists.tasklet);
|
||||
clear_and_wake_up_bit(bit, lock);
|
||||
}
|
||||
|
||||
static bool preempt_timeout(const struct intel_engine_cs *const engine)
|
||||
{
|
||||
const struct timer_list *t = &engine->execlists.preempt;
|
||||
|
||||
if (!CONFIG_DRM_I915_PREEMPT_TIMEOUT)
|
||||
return false;
|
||||
|
||||
if (!timer_expired(t))
|
||||
return false;
|
||||
|
||||
return READ_ONCE(engine->execlists.pending[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the unread Context Status Buffers and manage the submission of new
|
||||
* contexts to the ELSP accordingly.
|
||||
@ -1994,23 +2053,39 @@ static void __execlists_submission_tasklet(struct intel_engine_cs *const engine)
|
||||
static void execlists_submission_tasklet(unsigned long data)
|
||||
{
|
||||
struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
|
||||
unsigned long flags;
|
||||
bool timeout = preempt_timeout(engine);
|
||||
|
||||
process_csb(engine);
|
||||
if (!READ_ONCE(engine->execlists.pending[0])) {
|
||||
if (!READ_ONCE(engine->execlists.pending[0]) || timeout) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&engine->active.lock, flags);
|
||||
__execlists_submission_tasklet(engine);
|
||||
spin_unlock_irqrestore(&engine->active.lock, flags);
|
||||
|
||||
/* Recheck after serialising with direct-submission */
|
||||
if (timeout && preempt_timeout(engine))
|
||||
preempt_reset(engine);
|
||||
}
|
||||
}
|
||||
|
||||
static void execlists_submission_timer(struct timer_list *timer)
|
||||
static void __execlists_kick(struct intel_engine_execlists *execlists)
|
||||
{
|
||||
struct intel_engine_cs *engine =
|
||||
from_timer(engine, timer, execlists.timer);
|
||||
|
||||
/* Kick the tasklet for some interrupt coalescing and reset handling */
|
||||
tasklet_hi_schedule(&engine->execlists.tasklet);
|
||||
tasklet_hi_schedule(&execlists->tasklet);
|
||||
}
|
||||
|
||||
#define execlists_kick(t, member) \
|
||||
__execlists_kick(container_of(t, struct intel_engine_execlists, member))
|
||||
|
||||
static void execlists_timeslice(struct timer_list *timer)
|
||||
{
|
||||
execlists_kick(timer, timer);
|
||||
}
|
||||
|
||||
static void execlists_preempt(struct timer_list *timer)
|
||||
{
|
||||
execlists_kick(timer, preempt);
|
||||
}
|
||||
|
||||
static void queue_request(struct intel_engine_cs *engine,
|
||||
@ -3455,6 +3530,7 @@ gen12_emit_fini_breadcrumb_rcs(struct i915_request *request, u32 *cs)
|
||||
static void execlists_park(struct intel_engine_cs *engine)
|
||||
{
|
||||
cancel_timer(&engine->execlists.timer);
|
||||
cancel_timer(&engine->execlists.preempt);
|
||||
}
|
||||
|
||||
void intel_execlists_set_default_submission(struct intel_engine_cs *engine)
|
||||
@ -3572,7 +3648,8 @@ int intel_execlists_submission_setup(struct intel_engine_cs *engine)
|
||||
{
|
||||
tasklet_init(&engine->execlists.tasklet,
|
||||
execlists_submission_tasklet, (unsigned long)engine);
|
||||
timer_setup(&engine->execlists.timer, execlists_submission_timer, 0);
|
||||
timer_setup(&engine->execlists.timer, execlists_timeslice, 0);
|
||||
timer_setup(&engine->execlists.preempt, execlists_preempt, 0);
|
||||
|
||||
logical_ring_default_vfuncs(engine);
|
||||
logical_ring_default_irqs(engine);
|
||||
|
Reference in New Issue
Block a user