[PATCH 1/2] drm/i915/lrc: Record the default hw state after reset upon load

Chris Wilson chris at chris-wilson.co.uk
Wed Nov 1 11:23:08 UTC 2017


Take a copy of the HW state after a reset upon module loading by
executing a context switch from a blank context to the kernel context,
thus saving the default hw state over the blank context image.
We can then use the default hw state to initialise any future context,
ensuring that each starts with the default view of hw state.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/intel_lrc.c        | 166 +++++++++++++++++++++++++++++---
 drivers/gpu/drm/i915/intel_ringbuffer.h |   1 +
 2 files changed, 156 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 6840ec8db037..55bacca97de7 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -1437,6 +1437,18 @@ static u8 gtiir[] = {
 	[VECS] = 3,
 };
 
+static void enable_execlists(struct intel_engine_cs *engine)
+{
+	struct drm_i915_private *dev_priv = engine->i915;
+
+	I915_WRITE(RING_HWSTAM(engine->mmio_base), 0xffffffff);
+	I915_WRITE(RING_MODE_GEN7(engine),
+		   _MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE));
+	I915_WRITE(RING_HWS_PGA(engine->mmio_base),
+		   engine->status_page.ggtt_offset);
+	POSTING_READ(RING_HWS_PGA(engine->mmio_base));
+}
+
 static int gen8_init_common_ring(struct intel_engine_cs *engine)
 {
 	struct drm_i915_private *dev_priv = engine->i915;
@@ -1450,13 +1462,7 @@ static int gen8_init_common_ring(struct intel_engine_cs *engine)
 	intel_engine_reset_breadcrumbs(engine);
 	intel_engine_init_hangcheck(engine);
 
-	I915_WRITE(RING_HWSTAM(engine->mmio_base), 0xffffffff);
-	I915_WRITE(RING_MODE_GEN7(engine),
-		   _MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE));
-	I915_WRITE(RING_HWS_PGA(engine->mmio_base),
-		   engine->status_page.ggtt_offset);
-	POSTING_READ(RING_HWS_PGA(engine->mmio_base));
-
+	enable_execlists(engine);
 	DRM_DEBUG_DRIVER("Execlists enabled for %s\n", engine->name);
 
 	GEM_BUG_ON(engine->id >= ARRAY_SIZE(gtiir));
@@ -1871,6 +1877,10 @@ void intel_logical_ring_cleanup(struct intel_engine_cs *engine)
 	intel_engine_cleanup_common(engine);
 
 	lrc_destroy_wa_ctx(engine);
+
+	if (engine->default_state)
+		i915_gem_object_put(engine->default_state);
+
 	engine->i915 = NULL;
 	dev_priv->engine[engine->id] = NULL;
 	kfree(engine);
@@ -1918,6 +1928,120 @@ logical_ring_default_irqs(struct intel_engine_cs *engine)
 	engine->irq_keep_mask = GT_CONTEXT_SWITCH_INTERRUPT << shift;
 }
 
+static bool csb_idle(struct intel_engine_cs *engine)
+{
+	struct drm_i915_private *i915 = engine->i915;
+	u32 __iomem *status =
+		i915->regs + i915_mmio_reg_offset(RING_CONTEXT_STATUS_BUF_LO(engine, 0));
+	u32 __iomem *ring =
+		i915->regs + i915_mmio_reg_offset(RING_CONTEXT_STATUS_PTR(engine));
+	u32 head, tail;
+	bool idle = false;
+
+	head = ioread32(ring);
+	tail = GEN8_CSB_WRITE_PTR(head);
+	head = GEN8_CSB_READ_PTR(head);
+
+	if (head >= GEN8_CSB_ENTRIES || tail >= GEN8_CSB_ENTRIES)
+		return false;
+
+	while (head != tail) {
+		if (++head == GEN8_CSB_ENTRIES)
+			head = 0;
+
+		if (ioread32(status + 2 * head) & GEN8_CTX_STATUS_ACTIVE_IDLE)
+			idle = true;
+	}
+
+	iowrite32(_MASKED_FIELD(GEN8_CSB_READ_PTR_MASK, head << 8), ring);
+
+	return idle;
+}
+
+static int lrc_record_defaults(struct intel_engine_cs *engine)
+{
+	struct drm_i915_private *i915 = engine->i915;
+	u32 __iomem *elsp =
+		i915->regs + i915_mmio_reg_offset(RING_ELSP(engine));
+	struct i915_gem_context *kctx = i915->kernel_context;
+	struct intel_context *ce = &kctx->engine[engine->id];
+	struct drm_i915_gem_object *obj;
+	struct i915_vma *vma;
+	u32 context_size;
+	void *vaddr;
+	u32 *regs;
+	u64 desc;
+	int err;
+
+	/*
+	 * As we reset the gpu during very early sanitisation, the current
+	 * register state on the GPU should reflect its defaults values.
+	 * We load a context onto the hw (with restore-inhibit), then switch
+	 * over to a second context to save that default register state. We
+	 * then prime every new context with that state so they all start
+	 * from defaults.
+	 */
+
+	intel_runtime_pm_get(i915);
+	intel_uncore_forcewake_get(i915, FORCEWAKE_ALL);
+
+	context_size = round_up(engine->context_size, I915_GTT_PAGE_SIZE);
+	obj = i915_gem_object_create(i915, context_size);
+	if (IS_ERR(obj)) {
+		err = PTR_ERR(obj);
+		goto out_rpm;
+	}
+
+	vma = i915_vma_instance(obj, &i915->ggtt.base, NULL);
+	if (IS_ERR(vma)) {
+		err = PTR_ERR(vma);
+		goto out_obj;
+	}
+
+	err = i915_vma_pin(vma, 0, GEN8_LR_CONTEXT_ALIGN, PIN_GLOBAL);
+	if (err)
+		goto out_obj;
+
+	vaddr = i915_gem_object_pin_map(obj, I915_MAP_WB);
+	if (IS_ERR(vaddr)) {
+		err = PTR_ERR(vaddr);
+		goto out_vma;
+	}
+
+	regs = vaddr + PAGE_SIZE;
+	execlists_init_reg_state(regs, kctx, engine, ce->ring);
+	regs[CTX_CONTEXT_CONTROL+1] |=
+		_MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT);
+
+	desc = kctx->desc_template;
+	desc |= i915_ggtt_offset(vma);
+	desc |= (u64)(MAX_CONTEXT_HW_ID - 1) << GEN8_CTX_ID_SHIFT;
+
+	/* ELSP is programmed backwards. 2nd context first, 1st context last. */
+	enable_execlists(engine);
+	elsp_write(ce->lrc_desc, elsp);
+	elsp_write(desc, elsp);
+
+	msleep(1);
+	if (!wait_for(csb_idle(engine), 1000)) {
+		obj->mm.dirty = true;
+		engine->default_state = i915_gem_object_get(obj);
+	} else {
+		err = -EIO;
+	}
+
+	i915_gem_object_unpin_map(obj);
+out_vma:
+	i915_vma_unpin(vma);
+out_obj:
+	i915_gem_object_put(obj);
+out_rpm:
+	intel_uncore_forcewake_put(i915, FORCEWAKE_ALL);
+	intel_runtime_pm_put(i915);
+
+	return err;
+}
+
 static void
 logical_ring_setup(struct intel_engine_cs *engine)
 {
@@ -1958,6 +2082,10 @@ static int logical_ring_init(struct intel_engine_cs *engine)
 	if (ret)
 		goto error;
 
+	ret = lrc_record_defaults(engine);
+	if (ret)
+		goto error;
+
 	return 0;
 
 error:
@@ -2100,7 +2228,6 @@ static void execlists_init_reg_state(u32 *regs,
 
 	CTX_REG(regs, CTX_CONTEXT_CONTROL, RING_CONTEXT_CONTROL(engine),
 		_MASKED_BIT_ENABLE(CTX_CTRL_INHIBIT_SYN_CTX_SWITCH |
-				   CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT |
 				   (HAS_RESOURCE_STREAMER(dev_priv) ?
 				   CTX_CTRL_RS_CTX_ENABLE : 0)));
 	CTX_REG(regs, CTX_RING_HEAD, RING_HEAD(base), 0);
@@ -2177,6 +2304,7 @@ populate_lr_context(struct i915_gem_context *ctx,
 		    struct intel_ring *ring)
 {
 	void *vaddr;
+	u32 *regs;
 	int ret;
 
 	ret = i915_gem_object_set_to_cpu_domain(ctx_obj, true);
@@ -2193,11 +2321,27 @@ populate_lr_context(struct i915_gem_context *ctx,
 	}
 	ctx_obj->mm.dirty = true;
 
+	if (engine->default_state) {
+		void *defaults;
+
+		defaults = i915_gem_object_pin_map(engine->default_state,
+						   I915_MAP_WB);
+		if (IS_ERR(defaults))
+			return PTR_ERR(defaults);
+
+		memcpy(vaddr + LRC_HEADER_PAGES * PAGE_SIZE,
+		       defaults, engine->context_size);
+		i915_gem_object_unpin_map(engine->default_state);
+	}
+
 	/* The second page of the context object contains some fields which must
 	 * be set up prior to the first execution. */
-
-	execlists_init_reg_state(vaddr + LRC_STATE_PN * PAGE_SIZE,
-				 ctx, engine, ring);
+	regs = vaddr + LRC_STATE_PN * PAGE_SIZE,
+	execlists_init_reg_state(regs, ctx, engine, ring);
+	if (!engine->default_state) {
+		regs[CTX_CONTEXT_CONTROL+1] |=
+			_MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT);
+	}
 
 	i915_gem_object_unpin_map(ctx_obj);
 
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 69ad875fd011..1d752b9a3065 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -303,6 +303,7 @@ struct intel_engine_cs {
 	struct intel_ring *buffer;
 	struct intel_timeline *timeline;
 
+	struct drm_i915_gem_object *default_state;
 	struct intel_render_state *render_state;
 
 	atomic_t irq_count;
-- 
2.15.0.rc2



More information about the Intel-gfx-trybot mailing list