Thomas Zimmermann ebf85e
From 79f0f4724d9c502d65d08917709a60e4a8cb4972 Mon Sep 17 00:00:00 2001
Thomas Zimmermann ebf85e
From: Chris Wilson <chris@chris-wilson.co.uk>
Thomas Zimmermann ebf85e
Date: Fri, 21 Jul 2017 13:32:34 +0100
Thomas Zimmermann ebf85e
Subject: [PATCH] drm/i915/selftests: Exercise independence of per-engine resets
Thomas Zimmermann ebf85e
Git-commit: 79f0f4724d9c502d65d08917709a60e4a8cb4972
Thomas Zimmermann ebf85e
Patch-mainline: v4.14-rc1
Thomas Zimmermann ebf85e
References: FATE#322643 bsc#1055900
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
If all goes well, resetting one engine should not affect the operation of
Thomas Zimmermann ebf85e
any others. So to test this, we setup a continuous stream of requests
Thomas Zimmermann ebf85e
onto to each of the "innocent" engines whilst constantly resetting our
Thomas Zimmermann ebf85e
target engine.
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Thomas Zimmermann ebf85e
Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Thomas Zimmermann ebf85e
Cc: Michel Thierry <michel.thierry@intel.com>
Thomas Zimmermann ebf85e
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Thomas Zimmermann ebf85e
Link: https://patchwork.freedesktop.org/patch/msgid/20170721123238.16428-16-chris@chris-wilson.co.uk
Thomas Zimmermann ebf85e
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Thomas Zimmermann ebf85e
Acked-by: Takashi Iwai <tiwai@suse.de>
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
---
Thomas Zimmermann ebf85e
 drivers/gpu/drm/i915/selftests/intel_hangcheck.c |  164 +++++++++++++++++++++++
Thomas Zimmermann ebf85e
 drivers/gpu/drm/i915/selftests/mock_context.c    |    8 +
Thomas Zimmermann ebf85e
 drivers/gpu/drm/i915/selftests/mock_context.h    |    3 
Thomas Zimmermann ebf85e
 3 files changed, 175 insertions(+)
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
--- a/drivers/gpu/drm/i915/selftests/intel_hangcheck.c
Thomas Zimmermann ebf85e
+++ b/drivers/gpu/drm/i915/selftests/intel_hangcheck.c
Thomas Zimmermann ebf85e
@@ -22,8 +22,13 @@
Thomas Zimmermann ebf85e
  *
Thomas Zimmermann ebf85e
  */
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
+#include <linux/kthread.h>
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
 #include "../i915_selftest.h"
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
+#include "mock_context.h"
Thomas Zimmermann ebf85e
+#include "mock_drm.h"
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
 struct hang {
Thomas Zimmermann ebf85e
 	struct drm_i915_private *i915;
Thomas Zimmermann ebf85e
 	struct drm_i915_gem_object *hws;
Thomas Zimmermann ebf85e
@@ -372,6 +377,164 @@ static int igt_reset_engine(void *arg)
Thomas Zimmermann ebf85e
 	return err;
Thomas Zimmermann ebf85e
 }
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
+static int active_engine(void *data)
Thomas Zimmermann ebf85e
+{
Thomas Zimmermann ebf85e
+	struct intel_engine_cs *engine = data;
Thomas Zimmermann ebf85e
+	struct drm_i915_gem_request *rq[2] = {};
Thomas Zimmermann ebf85e
+	struct i915_gem_context *ctx[2];
Thomas Zimmermann ebf85e
+	struct drm_file *file;
Thomas Zimmermann ebf85e
+	unsigned long count = 0;
Thomas Zimmermann ebf85e
+	int err = 0;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	file = mock_file(engine->i915);
Thomas Zimmermann ebf85e
+	if (IS_ERR(file))
Thomas Zimmermann ebf85e
+		return PTR_ERR(file);
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	mutex_lock(&engine->i915->drm.struct_mutex);
Thomas Zimmermann ebf85e
+	ctx[0] = live_context(engine->i915, file);
Thomas Zimmermann ebf85e
+	mutex_unlock(&engine->i915->drm.struct_mutex);
Thomas Zimmermann ebf85e
+	if (IS_ERR(ctx[0])) {
Thomas Zimmermann ebf85e
+		err = PTR_ERR(ctx[0]);
Thomas Zimmermann ebf85e
+		goto err_file;
Thomas Zimmermann ebf85e
+	}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	mutex_lock(&engine->i915->drm.struct_mutex);
Thomas Zimmermann ebf85e
+	ctx[1] = live_context(engine->i915, file);
Thomas Zimmermann ebf85e
+	mutex_unlock(&engine->i915->drm.struct_mutex);
Thomas Zimmermann ebf85e
+	if (IS_ERR(ctx[1])) {
Thomas Zimmermann ebf85e
+		err = PTR_ERR(ctx[1]);
Thomas Zimmermann ebf85e
+		i915_gem_context_put(ctx[0]);
Thomas Zimmermann ebf85e
+		goto err_file;
Thomas Zimmermann ebf85e
+	}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	while (!kthread_should_stop()) {
Thomas Zimmermann ebf85e
+		unsigned int idx = count++ & 1;
Thomas Zimmermann ebf85e
+		struct drm_i915_gem_request *old = rq[idx];
Thomas Zimmermann ebf85e
+		struct drm_i915_gem_request *new;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+		mutex_lock(&engine->i915->drm.struct_mutex);
Thomas Zimmermann ebf85e
+		new = i915_gem_request_alloc(engine, ctx[idx]);
Thomas Zimmermann ebf85e
+		if (IS_ERR(new)) {
Thomas Zimmermann ebf85e
+			mutex_unlock(&engine->i915->drm.struct_mutex);
Thomas Zimmermann ebf85e
+			err = PTR_ERR(new);
Thomas Zimmermann ebf85e
+			break;
Thomas Zimmermann ebf85e
+		}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+		rq[idx] = i915_gem_request_get(new);
Thomas Zimmermann ebf85e
+		i915_add_request(new);
Thomas Zimmermann ebf85e
+		mutex_unlock(&engine->i915->drm.struct_mutex);
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+		if (old) {
Thomas Zimmermann ebf85e
+			i915_wait_request(old, 0, MAX_SCHEDULE_TIMEOUT);
Thomas Zimmermann ebf85e
+			i915_gem_request_put(old);
Thomas Zimmermann ebf85e
+		}
Thomas Zimmermann ebf85e
+	}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	for (count = 0; count < ARRAY_SIZE(rq); count++)
Thomas Zimmermann ebf85e
+		i915_gem_request_put(rq[count]);
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+err_file:
Thomas Zimmermann ebf85e
+	mock_file_free(engine->i915, file);
Thomas Zimmermann ebf85e
+	return err;
Thomas Zimmermann ebf85e
+}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+static int igt_reset_active_engines(void *arg)
Thomas Zimmermann ebf85e
+{
Thomas Zimmermann ebf85e
+	struct drm_i915_private *i915 = arg;
Thomas Zimmermann ebf85e
+	struct intel_engine_cs *engine, *active;
Thomas Zimmermann ebf85e
+	enum intel_engine_id id, tmp;
Thomas Zimmermann ebf85e
+	int err = 0;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	/* Check that issuing a reset on one engine does not interfere
Thomas Zimmermann ebf85e
+	 * with any other engine.
Thomas Zimmermann ebf85e
+	 */
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	if (!intel_has_reset_engine(i915))
Thomas Zimmermann ebf85e
+		return 0;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	for_each_engine(engine, i915, id) {
Thomas Zimmermann ebf85e
+		struct task_struct *threads[I915_NUM_ENGINES];
Thomas Zimmermann ebf85e
+		unsigned long resets[I915_NUM_ENGINES];
Thomas Zimmermann ebf85e
+		unsigned long global = i915_reset_count(&i915->gpu_error);
Thomas Zimmermann ebf85e
+		IGT_TIMEOUT(end_time);
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+		memset(threads, 0, sizeof(threads));
Thomas Zimmermann ebf85e
+		for_each_engine(active, i915, tmp) {
Thomas Zimmermann ebf85e
+			struct task_struct *tsk;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+			if (active == engine)
Thomas Zimmermann ebf85e
+				continue;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+			resets[tmp] = i915_reset_engine_count(&i915->gpu_error,
Thomas Zimmermann ebf85e
+							      active);
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+			tsk = kthread_run(active_engine, active,
Thomas Zimmermann ebf85e
+					  "igt/%s", active->name);
Thomas Zimmermann ebf85e
+			if (IS_ERR(tsk)) {
Thomas Zimmermann ebf85e
+				err = PTR_ERR(tsk);
Thomas Zimmermann ebf85e
+				goto unwind;
Thomas Zimmermann ebf85e
+			}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+			threads[tmp] = tsk;
Thomas Zimmermann ebf85e
+			get_task_struct(tsk);
Thomas Zimmermann ebf85e
+		}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+		set_bit(I915_RESET_ENGINE + engine->id, &i915->gpu_error.flags);
Thomas Zimmermann ebf85e
+		do {
Thomas Zimmermann ebf85e
+			err = i915_reset_engine(engine);
Thomas Zimmermann ebf85e
+			if (err) {
Thomas Zimmermann ebf85e
+				pr_err("i915_reset_engine(%s) failed, err=%d\n",
Thomas Zimmermann ebf85e
+				       engine->name, err);
Thomas Zimmermann ebf85e
+				break;
Thomas Zimmermann ebf85e
+			}
Thomas Zimmermann ebf85e
+		} while (time_before(jiffies, end_time));
Thomas Zimmermann ebf85e
+		clear_bit(I915_RESET_ENGINE + engine->id,
Thomas Zimmermann ebf85e
+			  &i915->gpu_error.flags);
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+unwind:
Thomas Zimmermann ebf85e
+		for_each_engine(active, i915, tmp) {
Thomas Zimmermann ebf85e
+			int ret;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+			if (!threads[tmp])
Thomas Zimmermann ebf85e
+				continue;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+			ret = kthread_stop(threads[tmp]);
Thomas Zimmermann ebf85e
+			if (ret) {
Thomas Zimmermann ebf85e
+				pr_err("kthread for active engine %s failed, err=%d\n",
Thomas Zimmermann ebf85e
+				       active->name, ret);
Thomas Zimmermann ebf85e
+				if (!err)
Thomas Zimmermann ebf85e
+					err = ret;
Thomas Zimmermann ebf85e
+			}
Thomas Zimmermann ebf85e
+			put_task_struct(threads[tmp]);
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+			if (resets[tmp] != i915_reset_engine_count(&i915->gpu_error,
Thomas Zimmermann ebf85e
+								   active)) {
Thomas Zimmermann ebf85e
+				pr_err("Innocent engine %s was reset (count=%ld)\n",
Thomas Zimmermann ebf85e
+				       active->name,
Thomas Zimmermann ebf85e
+				       i915_reset_engine_count(&i915->gpu_error,
Thomas Zimmermann ebf85e
+							       active) - resets[tmp]);
Thomas Zimmermann ebf85e
+				err = -EIO;
Thomas Zimmermann ebf85e
+			}
Thomas Zimmermann ebf85e
+		}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+		if (global != i915_reset_count(&i915->gpu_error)) {
Thomas Zimmermann ebf85e
+			pr_err("Global reset (count=%ld)!\n",
Thomas Zimmermann ebf85e
+			       i915_reset_count(&i915->gpu_error) - global);
Thomas Zimmermann ebf85e
+			err = -EIO;
Thomas Zimmermann ebf85e
+		}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+		if (err)
Thomas Zimmermann ebf85e
+			break;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+		cond_resched();
Thomas Zimmermann ebf85e
+	}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	if (i915_terminally_wedged(&i915->gpu_error))
Thomas Zimmermann ebf85e
+		err = -EIO;
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	return err;
Thomas Zimmermann ebf85e
+}
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
 static u32 fake_hangcheck(struct drm_i915_gem_request *rq)
Thomas Zimmermann ebf85e
 {
Thomas Zimmermann ebf85e
 	u32 reset_count;
Thomas Zimmermann ebf85e
@@ -689,6 +852,7 @@ int intel_hangcheck_live_selftests(struc
Thomas Zimmermann ebf85e
 		SUBTEST(igt_hang_sanitycheck),
Thomas Zimmermann ebf85e
 		SUBTEST(igt_global_reset),
Thomas Zimmermann ebf85e
 		SUBTEST(igt_reset_engine),
Thomas Zimmermann ebf85e
+		SUBTEST(igt_reset_active_engines),
Thomas Zimmermann ebf85e
 		SUBTEST(igt_wait_reset),
Thomas Zimmermann ebf85e
 		SUBTEST(igt_reset_queue),
Thomas Zimmermann ebf85e
 		SUBTEST(igt_render_engine_reset_fallback),
Thomas Zimmermann ebf85e
--- a/drivers/gpu/drm/i915/selftests/mock_context.c
Thomas Zimmermann ebf85e
+++ b/drivers/gpu/drm/i915/selftests/mock_context.c
Thomas Zimmermann ebf85e
@@ -95,3 +95,11 @@ void mock_init_contexts(struct drm_i915_
Thomas Zimmermann ebf85e
 	INIT_WORK(&i915->contexts.free_work, contexts_free_worker);
Thomas Zimmermann ebf85e
 	init_llist_head(&i915->contexts.free_list);
Thomas Zimmermann ebf85e
 }
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+struct i915_gem_context *
Thomas Zimmermann ebf85e
+live_context(struct drm_i915_private *i915, struct drm_file *file)
Thomas Zimmermann ebf85e
+{
Thomas Zimmermann ebf85e
+	lockdep_assert_held(&i915->drm.struct_mutex);
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
+	return i915_gem_create_context(i915, file->driver_priv);
Thomas Zimmermann ebf85e
+}
Thomas Zimmermann ebf85e
--- a/drivers/gpu/drm/i915/selftests/mock_context.h
Thomas Zimmermann ebf85e
+++ b/drivers/gpu/drm/i915/selftests/mock_context.h
Thomas Zimmermann ebf85e
@@ -33,4 +33,7 @@ mock_context(struct drm_i915_private *i9
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 void mock_context_close(struct i915_gem_context *ctx);
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
+struct i915_gem_context *
Thomas Zimmermann ebf85e
+live_context(struct drm_i915_private *i915, struct drm_file *file);
Thomas Zimmermann ebf85e
+
Thomas Zimmermann ebf85e
 #endif /* !__MOCK_CONTEXT_H */