Skip to content

Commit

Permalink
drm/i915/execlists: Minimalistic timeslicing
Browse files Browse the repository at this point in the history
If we have multiple contexts of equal priority pending execution,
activate a timer to demote the currently executing context in favour of
the next in the queue when that timeslice expires. This enforces
fairness between contexts (so long as they allow preemption -- forced
preemption, in the future, will kick those who do not obey) and allows
us to avoid userspace blocking forward progress with e.g. unbounded
MI_SEMAPHORE_WAIT.

For the starting point here, we use the jiffie as our timeslice so that
we should be reasonably efficient wrt frequent CPU wakeups.

Testcase: igt/gem_exec_scheduler/semaphore-resolve
Signed-off-by: Chris Wilson <[email protected]>
Reviewed-by: Mika Kuoppala <[email protected]>
Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
  • Loading branch information
ickle committed Jun 20, 2019
1 parent 22b7a42 commit 8ee36e0
Show file tree
Hide file tree
Showing 5 changed files with 347 additions and 0 deletions.
6 changes: 6 additions & 0 deletions drivers/gpu/drm/i915/gt/intel_engine_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/llist.h>
#include <linux/timer.h>
#include <linux/types.h>

#include "i915_gem.h"
Expand Down Expand Up @@ -149,6 +150,11 @@ struct intel_engine_execlists {
*/
struct tasklet_struct tasklet;

/**
* @timer: kick the current context if its timeslice expires
*/
struct timer_list timer;

/**
* @default_priolist: priority list for I915_PRIORITY_NORMAL
*/
Expand Down
116 changes: 116 additions & 0 deletions drivers/gpu/drm/i915/gt/intel_lrc.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ static int effective_prio(const struct i915_request *rq)
prio |= I915_PRIORITY_NOSEMAPHORE;

/* Restrict mere WAIT boosts from triggering preemption */
BUILD_BUG_ON(__NO_PREEMPTION & ~I915_PRIORITY_MASK); /* only internal */
return prio | __NO_PREEMPTION;
}

Expand Down Expand Up @@ -830,6 +831,81 @@ last_active(const struct intel_engine_execlists *execlists)
return *last;
}

static void
defer_request(struct i915_request * const rq, struct list_head * const pl)
{
struct i915_dependency *p;

/*
* We want to move the interrupted request to the back of
* the round-robin list (i.e. its priority level), but
* in doing so, we must then move all requests that were in
* flight and were waiting for the interrupted request to
* be run after it again.
*/
list_move_tail(&rq->sched.link, pl);

list_for_each_entry(p, &rq->sched.waiters_list, wait_link) {
struct i915_request *w =
container_of(p->waiter, typeof(*w), sched);

/* Leave semaphores spinning on the other engines */
if (w->engine != rq->engine)
continue;

/* No waiter should start before the active request completed */
GEM_BUG_ON(i915_request_started(w));

GEM_BUG_ON(rq_prio(w) > rq_prio(rq));
if (rq_prio(w) < rq_prio(rq))
continue;

if (list_empty(&w->sched.link))
continue; /* Not yet submitted; unready */

/*
* This should be very shallow as it is limited by the
* number of requests that can fit in a ring (<64) and
* the number of contexts that can be in flight on this
* engine.
*/
defer_request(w, pl);
}
}

static void defer_active(struct intel_engine_cs *engine)
{
struct i915_request *rq;

rq = __unwind_incomplete_requests(engine);
if (!rq)
return;

defer_request(rq, i915_sched_lookup_priolist(engine, rq_prio(rq)));
}

static bool
need_timeslice(struct intel_engine_cs *engine, const struct i915_request *rq)
{
int hint;

if (list_is_last(&rq->sched.link, &engine->active.requests))
return false;

hint = max(rq_prio(list_next_entry(rq, sched.link)),
engine->execlists.queue_priority_hint);

return hint >= rq_prio(rq);
}

static bool
enable_timeslice(struct intel_engine_cs *engine)
{
struct i915_request *last = last_active(&engine->execlists);

return last && need_timeslice(engine, last);
}

static void execlists_dequeue(struct intel_engine_cs *engine)
{
struct intel_engine_execlists * const execlists = &engine->execlists;
Expand Down Expand Up @@ -923,6 +999,32 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
*/
last->hw_context->lrc_desc |= CTX_DESC_FORCE_RESTORE;
last = NULL;
} else if (need_timeslice(engine, last) &&
!timer_pending(&engine->execlists.timer)) {
GEM_TRACE("%s: expired last=%llx:%lld, prio=%d, hint=%d\n",
engine->name,
last->fence.context,
last->fence.seqno,
last->sched.attr.priority,
execlists->queue_priority_hint);

ring_set_paused(engine, 1);
defer_active(engine);

/*
* Unlike for preemption, if we rewind and continue
* executing the same context as previously active,
* the order of execution will remain the same and
* the tail will only advance. We do not need to
* force a full context restore, as a lite-restore
* is sufficient to resample the monotonic TAIL.
*
* If we switch to any other context, similarly we
* will not rewind TAIL of current context, and
* normal save/restore will preserve state and allow
* us to later continue executing the same request.
*/
last = NULL;
} else {
/*
* Otherwise if we already have a request pending
Expand Down Expand Up @@ -1247,6 +1349,9 @@ static void process_csb(struct intel_engine_cs *engine)
sizeof(*execlists->pending));
execlists->pending[0] = NULL;

if (enable_timeslice(engine))
mod_timer(&execlists->timer, jiffies + 1);

if (!inject_preempt_hang(execlists))
ring_set_paused(engine, 0);
} else if (status & GEN8_CTX_STATUS_PREEMPTED) {
Expand Down Expand Up @@ -1317,6 +1422,15 @@ static void execlists_submission_tasklet(unsigned long data)
spin_unlock_irqrestore(&engine->active.lock, flags);
}

static void execlists_submission_timer(struct timer_list *timer)
{
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);
}

static void queue_request(struct intel_engine_cs *engine,
struct i915_sched_node *node,
int prio)
Expand Down Expand Up @@ -2542,6 +2656,7 @@ static int gen8_init_rcs_context(struct i915_request *rq)

static void execlists_park(struct intel_engine_cs *engine)
{
del_timer_sync(&engine->execlists.timer);
intel_engine_park(engine);
}

Expand Down Expand Up @@ -2639,6 +2754,7 @@ 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);

logical_ring_default_vfuncs(engine);
logical_ring_default_irqs(engine);
Expand Down
Loading

0 comments on commit 8ee36e0

Please sign in to comment.