summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/i915_gem_context.c
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2016-12-18 15:37:20 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2016-12-18 16:18:50 +0000
commite8a9c58fcd9a5081f71f57f370af1347ed6a310b (patch)
tree61b8fe4eb3219a120f8e941ac4b0aa4a8f10fa69 /drivers/gpu/drm/i915/i915_gem_context.c
parentef11c01db405b49d96a0ab2689807ea33ae85dd6 (diff)
drm/i915: Unify active context tracking between legacy/execlists/guc
The requests conversion introduced a nasty bug where we could generate a new request in the middle of constructing a request if we needed to idle the system in order to evict space for a context. The request to idle would be executed (and waited upon) before the current one, creating a minor havoc in the seqno accounting, as we will consider the current request to already be completed (prior to deferred seqno assignment) but ring->last_retired_head would have been updated and still could allow us to overwrite the current request before execution. We also employed two different mechanisms to track the active context until it was switched out. The legacy method allowed for waiting upon an active context (it could forcibly evict any vma, including context's), but the execlists method took a step backwards by pinning the vma for the entire active lifespan of the context (the only way to evict was to idle the entire GPU, not individual contexts). However, to circumvent the tricky issue of locking (i.e. we cannot take struct_mutex at the time of i915_gem_request_submit(), where we would want to move the previous context onto the active tracker and unpin it), we take the execlists approach and keep the contexts pinned until retirement. The benefit of the execlists approach, more important for execlists than legacy, was the reduction in work in pinning the context for each request - as the context was kept pinned until idle, it could short circuit the pinning for all active contexts. We introduce new engine vfuncs to pin and unpin the context respectively. The context is pinned at the start of the request, and only unpinned when the following request is retired (this ensures that the context is idle and coherent in main memory before we unpin it). We move the engine->last_context tracking into the retirement itself (rather than during request submission) in order to allow the submission to be reordered or unwound without undue difficultly. And finally an ulterior motive for unifying context handling was to prepare for mock requests. v2: Rename to last_retired_context, split out legacy_context tracking for MI_SET_CONTEXT. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> Link: http://patchwork.freedesktop.org/patch/msgid/20161218153724.8439-3-chris@chris-wilson.co.uk
Diffstat (limited to 'drivers/gpu/drm/i915/i915_gem_context.c')
-rw-r--r--drivers/gpu/drm/i915/i915_gem_context.c110
1 files changed, 16 insertions, 94 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index a57c22659a3c..598a70d2b695 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -416,21 +416,6 @@ out:
return ctx;
}
-static void i915_gem_context_unpin(struct i915_gem_context *ctx,
- struct intel_engine_cs *engine)
-{
- if (i915.enable_execlists) {
- intel_lr_context_unpin(ctx, engine);
- } else {
- struct intel_context *ce = &ctx->engine[engine->id];
-
- if (ce->state)
- i915_vma_unpin(ce->state);
-
- i915_gem_context_put(ctx);
- }
-}
-
int i915_gem_context_init(struct drm_i915_private *dev_priv)
{
struct i915_gem_context *ctx;
@@ -490,10 +475,13 @@ void i915_gem_context_lost(struct drm_i915_private *dev_priv)
lockdep_assert_held(&dev_priv->drm.struct_mutex);
for_each_engine(engine, dev_priv, id) {
- if (engine->last_context) {
- i915_gem_context_unpin(engine->last_context, engine);
- engine->last_context = NULL;
- }
+ engine->legacy_active_context = NULL;
+
+ if (!engine->last_retired_context)
+ continue;
+
+ engine->context_unpin(engine, engine->last_retired_context);
+ engine->last_retired_context = NULL;
}
/* Force the GPU state to be restored on enabling */
@@ -715,7 +703,7 @@ static inline bool skip_rcs_switch(struct i915_hw_ppgtt *ppgtt,
if (ppgtt && (intel_engine_flag(engine) & ppgtt->pd_dirty_rings))
return false;
- return to == engine->last_context;
+ return to == engine->legacy_active_context;
}
static bool
@@ -727,11 +715,11 @@ needs_pd_load_pre(struct i915_hw_ppgtt *ppgtt,
return false;
/* Always load the ppgtt on first use */
- if (!engine->last_context)
+ if (!engine->legacy_active_context)
return true;
/* Same context without new entries, skip */
- if (engine->last_context == to &&
+ if (engine->legacy_active_context == to &&
!(intel_engine_flag(engine) & ppgtt->pd_dirty_rings))
return false;
@@ -761,57 +749,20 @@ needs_pd_load_post(struct i915_hw_ppgtt *ppgtt,
return false;
}
-struct i915_vma *
-i915_gem_context_pin_legacy(struct i915_gem_context *ctx,
- unsigned int flags)
-{
- struct i915_vma *vma = ctx->engine[RCS].state;
- int ret;
-
- /* Clear this page out of any CPU caches for coherent swap-in/out.
- * We only want to do this on the first bind so that we do not stall
- * on an active context (which by nature is already on the GPU).
- */
- if (!(vma->flags & I915_VMA_GLOBAL_BIND)) {
- ret = i915_gem_object_set_to_gtt_domain(vma->obj, false);
- if (ret)
- return ERR_PTR(ret);
- }
-
- ret = i915_vma_pin(vma, 0, ctx->ggtt_alignment, PIN_GLOBAL | flags);
- if (ret)
- return ERR_PTR(ret);
-
- return vma;
-}
-
static int do_rcs_switch(struct drm_i915_gem_request *req)
{
struct i915_gem_context *to = req->ctx;
struct intel_engine_cs *engine = req->engine;
struct i915_hw_ppgtt *ppgtt = to->ppgtt ?: req->i915->mm.aliasing_ppgtt;
- struct i915_vma *vma;
- struct i915_gem_context *from;
+ struct i915_gem_context *from = engine->legacy_active_context;
u32 hw_flags;
int ret, i;
+ GEM_BUG_ON(engine->id != RCS);
+
if (skip_rcs_switch(ppgtt, engine, to))
return 0;
- /* Trying to pin first makes error handling easier. */
- vma = i915_gem_context_pin_legacy(to, 0);
- if (IS_ERR(vma))
- return PTR_ERR(vma);
-
- /*
- * Pin can switch back to the default context if we end up calling into
- * evict_everything - as a last ditch gtt defrag effort that also
- * switches to the default context. Hence we need to reload from here.
- *
- * XXX: Doing so is painfully broken!
- */
- from = engine->last_context;
-
if (needs_pd_load_pre(ppgtt, engine, to)) {
/* Older GENs and non render rings still want the load first,
* "PP_DCLV followed by PP_DIR_BASE register through Load
@@ -820,7 +771,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
trace_switch_mm(engine, to);
ret = ppgtt->switch_mm(ppgtt, req);
if (ret)
- goto err;
+ return ret;
}
if (!to->engine[RCS].initialised || i915_gem_context_is_default(to))
@@ -837,29 +788,10 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
if (to != from || (hw_flags & MI_FORCE_RESTORE)) {
ret = mi_set_context(req, hw_flags);
if (ret)
- goto err;
- }
+ return ret;
- /* The backing object for the context is done after switching to the
- * *next* context. Therefore we cannot retire the previous context until
- * the next context has already started running. In fact, the below code
- * is a bit suboptimal because the retiring can occur simply after the
- * MI_SET_CONTEXT instead of when the next seqno has completed.
- */
- if (from != NULL) {
- /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
- * whole damn pipeline, we don't need to explicitly mark the
- * object dirty. The only exception is that the context must be
- * correct in case the object gets swapped out. Ideally we'd be
- * able to defer doing this until we know the object would be
- * swapped, but there is no way to do that yet.
- */
- i915_vma_move_to_active(from->engine[RCS].state, req, 0);
- /* state is kept alive until the next request */
- i915_vma_unpin(from->engine[RCS].state);
- i915_gem_context_put(from);
+ engine->legacy_active_context = to;
}
- engine->last_context = i915_gem_context_get(to);
/* GEN8 does *not* require an explicit reload if the PDPs have been
* setup, and we do not wish to move them.
@@ -900,10 +832,6 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
}
return 0;
-
-err:
- i915_vma_unpin(vma);
- return ret;
}
/**
@@ -943,12 +871,6 @@ int i915_switch_context(struct drm_i915_gem_request *req)
ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine);
}
- if (to != engine->last_context) {
- if (engine->last_context)
- i915_gem_context_put(engine->last_context);
- engine->last_context = i915_gem_context_get(to);
- }
-
return 0;
}