drm/i915: Initialize all contexts
authorBen Widawsky <benjamin.widawsky@intel.com>
Mon, 16 Mar 2015 16:00:58 +0000 (16:00 +0000)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Fri, 20 Mar 2015 10:48:19 +0000 (11:48 +0100)
The problem is we're going to switch to a new context, which could be
the default context. The plan was to use restore inhibit, which would be
fine, except if we are using dynamic page tables (which we will). If we
use dynamic page tables and we don't load new page tables, the previous
page tables might go away, and future operations will fault.

CTXA runs.
switch to default, restore inhibit
CTXA dies and has its address space taken away.
Run CTXB, tries to save using the context A's address space - this
fails.

The general solution is to make sure every context has it's own state,
and its own address space. For cases when we must restore inhibit, first
thing we do is load a valid address space. I thought this would be
enough, but apparently there are references within the context itself
which will refer to the old address space - therefore, we also must
reinitialize.

v2: to->ppgtt is only valid in full ppgtt.
v3: Rebased.
v4: Make post PDP update clearer.

Signed-off-by: Ben Widawsky <ben@bwidawsk.net>
Signed-off-by: Michel Thierry <michel.thierry@intel.com> (v2+)
Reviewed-by: Mika Kuoppala <mika.kuoppala@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_gem_context.c

index dd9ab36a039b9613959832c7588e15254e138da9..f3e84c44d0091a00195d3b827c36de435d755ed5 100644 (file)
@@ -609,7 +609,8 @@ needs_pd_load_pre(struct intel_engine_cs *ring, struct intel_context *to)
 }
 
 static bool
-needs_pd_load_post(struct intel_engine_cs *ring, struct intel_context *to)
+needs_pd_load_post(struct intel_engine_cs *ring, struct intel_context *to,
+               u32 hw_flags)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
 
@@ -622,7 +623,7 @@ needs_pd_load_post(struct intel_engine_cs *ring, struct intel_context *to)
        if (ring != &dev_priv->ring[RCS])
                return false;
 
-       if (to->ppgtt->pd_dirty_rings)
+       if (hw_flags & MI_RESTORE_INHIBIT)
                return true;
 
        return false;
@@ -661,9 +662,6 @@ static int do_switch(struct intel_engine_cs *ring,
         */
        from = ring->last_context;
 
-       /* We should never emit switch_mm more than once */
-       WARN_ON(needs_pd_load_pre(ring, to) && needs_pd_load_post(ring, to));
-
        if (needs_pd_load_pre(ring, to)) {
                /* Older GENs and non render rings still want the load first,
                 * "PP_DCLV followed by PP_DIR_BASE register through Load
@@ -706,16 +704,28 @@ static int do_switch(struct intel_engine_cs *ring,
                        goto unpin_out;
        }
 
-       if (!to->legacy_hw_ctx.initialized || i915_gem_context_is_default(to))
+       if (!to->legacy_hw_ctx.initialized) {
                hw_flags |= MI_RESTORE_INHIBIT;
-       else if (to->ppgtt && test_and_clear_bit(ring->id, &to->ppgtt->pd_dirty_rings))
+               /* NB: If we inhibit the restore, the context is not allowed to
+                * die because future work may end up depending on valid address
+                * space. This means we must enforce that a page table load
+                * occur when this occurs. */
+       } else if (to->ppgtt &&
+                       test_and_clear_bit(ring->id, &to->ppgtt->pd_dirty_rings))
                hw_flags |= MI_FORCE_RESTORE;
 
+       /* We should never emit switch_mm more than once */
+       WARN_ON(needs_pd_load_pre(ring, to) &&
+                       needs_pd_load_post(ring, to, hw_flags));
+
        ret = mi_set_context(ring, to, hw_flags);
        if (ret)
                goto unpin_out;
 
-       if (needs_pd_load_post(ring, to)) {
+       /* GEN8 does *not* require an explicit reload if the PDPs have been
+        * setup, and we do not wish to move them.
+        */
+       if (needs_pd_load_post(ring, to, hw_flags)) {
                trace_switch_mm(ring, to);
                ret = to->ppgtt->switch_mm(to->ppgtt, ring);
                /* The hardware context switch is emitted, but we haven't
@@ -766,7 +776,7 @@ static int do_switch(struct intel_engine_cs *ring,
                i915_gem_context_unreference(from);
        }
 
-       uninitialized = !to->legacy_hw_ctx.initialized && from == NULL;
+       uninitialized = !to->legacy_hw_ctx.initialized;
        to->legacy_hw_ctx.initialized = true;
 
 done: