Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d2b4b979 authored by Chris Wilson's avatar Chris Wilson
Browse files

drm/i915: Record the default hw state after reset upon load



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.

v2: Unmap our default state from the GTT after stealing it from the
context. This should stop us from accidentally overwriting it via the
GTT (and frees up some precious GTT space).

Testcase: igt/gem_ctx_isolation
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Reviewed-by: default avatarJoonas Lahtinen <joonas.lahtinen@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20171110142634.10551-7-chris@chris-wilson.co.uk
parent f4e15af7
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -723,8 +723,6 @@ int intel_vgpu_init_gvt_context(struct intel_vgpu *vgpu)
	if (IS_ERR(vgpu->shadow_ctx))
		return PTR_ERR(vgpu->shadow_ctx);

	vgpu->shadow_ctx->engine[RCS].initialised = true;

	bitmap_zero(vgpu->shadow_ctx_desc_updated, I915_NUM_ENGINES);

	return 0;
+0 −1
Original line number Diff line number Diff line
@@ -1974,7 +1974,6 @@ static int i915_context_status(struct seq_file *m, void *unused)
			struct intel_context *ce = &ctx->engine[engine->id];

			seq_printf(m, "%s: ", engine->name);
			seq_putc(m, ce->initialised ? 'I' : 'i');
			if (ce->state)
				describe_obj(m, ce->state->obj);
			if (ce->ring)
+3 −0
Original line number Diff line number Diff line
@@ -406,6 +406,9 @@ static int i915_getparam(struct drm_device *dev, void *data,
		 */
		value = 1;
		break;
	case I915_PARAM_HAS_CONTEXT_ISOLATION:
		value = intel_engines_has_context_isolation(dev_priv);
		break;
	case I915_PARAM_SLICE_MASK:
		value = INTEL_INFO(dev_priv)->sseu.slice_mask;
		if (!value)
+115 −0
Original line number Diff line number Diff line
@@ -4972,6 +4972,120 @@ bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value)
	return true;
}

static int __intel_engines_record_defaults(struct drm_i915_private *i915)
{
	struct i915_gem_context *ctx;
	struct intel_engine_cs *engine;
	enum intel_engine_id id;
	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
	 * can then prime every new context with that state so they all start
	 * from the same default HW values.
	 */

	ctx = i915_gem_context_create_kernel(i915, 0);
	if (IS_ERR(ctx))
		return PTR_ERR(ctx);

	for_each_engine(engine, i915, id) {
		struct drm_i915_gem_request *rq;

		rq = i915_gem_request_alloc(engine, ctx);
		if (IS_ERR(rq)) {
			err = PTR_ERR(rq);
			goto out_ctx;
		}

		err = i915_switch_context(rq);
		if (engine->init_context)
			err = engine->init_context(rq);

		__i915_add_request(rq, true);
		if (err)
			goto err_active;
	}

	err = i915_gem_switch_to_kernel_context(i915);
	if (err)
		goto err_active;

	err = i915_gem_wait_for_idle(i915, I915_WAIT_LOCKED);
	if (err)
		goto err_active;

	assert_kernel_context_is_current(i915);

	for_each_engine(engine, i915, id) {
		struct i915_vma *state;

		state = ctx->engine[id].state;
		if (!state)
			continue;

		/*
		 * As we will hold a reference to the logical state, it will
		 * not be torn down with the context, and importantly the
		 * object will hold onto its vma (making it possible for a
		 * stray GTT write to corrupt our defaults). Unmap the vma
		 * from the GTT to prevent such accidents and reclaim the
		 * space.
		 */
		err = i915_vma_unbind(state);
		if (err)
			goto err_active;

		err = i915_gem_object_set_to_cpu_domain(state->obj, false);
		if (err)
			goto err_active;

		engine->default_state = i915_gem_object_get(state->obj);
	}

	if (IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) {
		unsigned int found = intel_engines_has_context_isolation(i915);

		/*
		 * Make sure that classes with multiple engine instances all
		 * share the same basic configuration.
		 */
		for_each_engine(engine, i915, id) {
			unsigned int bit = BIT(engine->uabi_class);
			unsigned int expected = engine->default_state ? bit : 0;

			if ((found & bit) != expected) {
				DRM_ERROR("mismatching default context state for class %d on engine %s\n",
					  engine->uabi_class, engine->name);
			}
		}
	}

out_ctx:
	i915_gem_context_set_closed(ctx);
	i915_gem_context_put(ctx);
	return err;

err_active:
	/*
	 * If we have to abandon now, we expect the engines to be idle
	 * and ready to be torn-down. First try to flush any remaining
	 * request, ensure we are pointing at the kernel context and
	 * then remove it.
	 */
	if (WARN_ON(i915_gem_switch_to_kernel_context(i915)))
		goto out_ctx;

	if (WARN_ON(i915_gem_wait_for_idle(i915, I915_WAIT_LOCKED)))
		goto out_ctx;

	i915_gem_contexts_lost(i915);
	goto out_ctx;
}

int i915_gem_init(struct drm_i915_private *dev_priv)
{
	int ret;
@@ -5038,6 +5152,7 @@ int i915_gem_init(struct drm_i915_private *dev_priv)
	 */
	intel_init_clock_gating(dev_priv);

	ret = __intel_engines_record_defaults(dev_priv);
out_unlock:
	if (ret == -EIO) {
		/* Allow engine initialisation to fail by marking the GPU as
+12 −43
Original line number Diff line number Diff line
@@ -418,8 +418,8 @@ i915_gem_context_create_gvt(struct drm_device *dev)
	return ctx;
}

static struct i915_gem_context *
create_kernel_context(struct drm_i915_private *i915, int prio)
struct i915_gem_context *
i915_gem_context_create_kernel(struct drm_i915_private *i915, int prio)
{
	struct i915_gem_context *ctx;

@@ -473,7 +473,7 @@ int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
	ida_init(&dev_priv->contexts.hw_ida);

	/* lowest priority; idle task */
	ctx = create_kernel_context(dev_priv, I915_PRIORITY_MIN);
	ctx = i915_gem_context_create_kernel(dev_priv, I915_PRIORITY_MIN);
	if (IS_ERR(ctx)) {
		DRM_ERROR("Failed to create default global context\n");
		err = PTR_ERR(ctx);
@@ -487,7 +487,7 @@ int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
	dev_priv->kernel_context = ctx;

	/* highest priority; preempting task */
	ctx = create_kernel_context(dev_priv, INT_MAX);
	ctx = i915_gem_context_create_kernel(dev_priv, INT_MAX);
	if (IS_ERR(ctx)) {
		DRM_ERROR("Failed to create default preempt context\n");
		err = PTR_ERR(ctx);
@@ -522,28 +522,6 @@ void i915_gem_contexts_lost(struct drm_i915_private *dev_priv)
		engine->context_unpin(engine, engine->last_retired_context);
		engine->last_retired_context = NULL;
	}

	/* Force the GPU state to be restored on enabling */
	if (!i915_modparams.enable_execlists) {
		struct i915_gem_context *ctx;

		list_for_each_entry(ctx, &dev_priv->contexts.list, link) {
			if (!i915_gem_context_is_default(ctx))
				continue;

			for_each_engine(engine, dev_priv, id)
				ctx->engine[engine->id].initialised = false;

			ctx->remap_slice = ALL_L3_SLICES(dev_priv);
		}

		for_each_engine(engine, dev_priv, id) {
			struct intel_context *kce =
				&dev_priv->kernel_context->engine[engine->id];

			kce->initialised = true;
		}
	}
}

void i915_gem_contexts_fini(struct drm_i915_private *i915)
@@ -718,9 +696,6 @@ static inline bool skip_rcs_switch(struct i915_hw_ppgtt *ppgtt,
	if (to->remap_slice)
		return false;

	if (!to->engine[RCS].initialised)
		return false;

	if (ppgtt && (intel_engine_flag(engine) & ppgtt->pd_dirty_rings))
		return false;

@@ -795,11 +770,14 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
			return ret;
	}

	if (!to->engine[RCS].initialised || i915_gem_context_is_default(to))
		/* 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. */
	if (i915_gem_context_is_kernel(to))
		/*
		 * The kernel context(s) is treated as pure scratch and is not
		 * expected to retain any state (as we sacrifice it during
		 * suspend and on resume it may be corrupted). This is ok,
		 * as nothing actually executes using the kernel context; it
		 * is purely used for flushing user contexts.
		 */
		hw_flags = MI_RESTORE_INHIBIT;
	else if (ppgtt && intel_engine_flag(engine) & ppgtt->pd_dirty_rings)
		hw_flags = MI_FORCE_RESTORE;
@@ -843,15 +821,6 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
		to->remap_slice &= ~(1<<i);
	}

	if (!to->engine[RCS].initialised) {
		if (engine->init_context) {
			ret = engine->init_context(req);
			if (ret)
				return ret;
		}
		to->engine[RCS].initialised = true;
	}

	return 0;
}

Loading