[PATCH 49/49] request-suspend

Chris Wilson chris at chris-wilson.co.uk
Mon Jan 25 12:07:54 UTC 2021


---
 drivers/gpu/drm/i915/i915_request.h           |  23 +++
 drivers/gpu/drm/i915/i915_scheduler.c         |  96 ++++++++--
 drivers/gpu/drm/i915/selftests/i915_request.c | 168 ++++++++++++++++++
 3 files changed, 272 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
index a74d8204fca9..293c474da6ce 100644
--- a/drivers/gpu/drm/i915/i915_request.h
+++ b/drivers/gpu/drm/i915/i915_request.h
@@ -137,6 +137,11 @@ enum {
 	 * the GPU. Here we track such boost requests on a per-request basis.
 	 */
 	I915_FENCE_FLAG_BOOST,
+
+	/*
+	 * I915_FENCE_FLAG_SUSPENDED - this request should not be executed
+	 */
+	I915_FENCE_FLAG_SUSPENDED,
 };
 
 /**
@@ -368,6 +373,9 @@ void i915_request_unsubmit(struct i915_request *request);
 
 void i915_request_cancel(struct i915_request *rq, int error);
 
+void i915_request_suspend(struct i915_request *rq);
+void i915_request_resume(struct i915_request *rq);
+
 long i915_request_wait(struct i915_request *rq,
 		       unsigned int flags,
 		       long timeout)
@@ -591,6 +599,21 @@ static inline void i915_request_clear_hold(struct i915_request *rq)
 	clear_bit(I915_FENCE_FLAG_HOLD, &rq->fence.flags);
 }
 
+static inline bool i915_request_is_suspended(const struct i915_request *rq)
+{
+	return unlikely(test_bit(I915_FENCE_FLAG_SUSPENDED, &rq->fence.flags));
+}
+
+static inline bool i915_request_set_suspended(struct i915_request *rq)
+{
+	return !test_and_set_bit(I915_FENCE_FLAG_SUSPENDED, &rq->fence.flags);
+}
+
+static inline bool i915_request_clear_suspended(struct i915_request *rq)
+{
+	return test_and_clear_bit(I915_FENCE_FLAG_SUSPENDED, &rq->fence.flags);
+}
+
 static inline struct intel_timeline *
 i915_request_timeline(const struct i915_request *rq)
 {
diff --git a/drivers/gpu/drm/i915/i915_scheduler.c b/drivers/gpu/drm/i915/i915_scheduler.c
index 076ae795d852..226bb9902dbf 100644
--- a/drivers/gpu/drm/i915/i915_scheduler.c
+++ b/drivers/gpu/drm/i915/i915_scheduler.c
@@ -8,6 +8,7 @@
 #include <linux/mutex.h>
 #include <linux/prandom.h>
 
+#include "gt/intel_engine_heartbeat.h"
 #include "gt/intel_ring.h"
 #include "gt/intel_lrc_reg.h"
 
@@ -945,6 +946,9 @@ static bool hold_request(const struct i915_request *rq)
 static bool ancestor_on_hold(const struct intel_engine_cs *engine,
 			     const struct i915_request *rq)
 {
+	if (i915_request_is_suspended(rq))
+		return true;
+
 	GEM_BUG_ON(i915_request_on_hold(rq));
 	return unlikely(!list_empty(&engine->active.hold)) && hold_request(rq);
 }
@@ -1019,6 +1023,53 @@ void i915_request_enqueue(struct i915_request *rq)
 		i915_sched_kick(se);
 }
 
+void i915_request_suspend(struct i915_request *rq)
+{
+	struct intel_engine_cs *engine;
+	unsigned long flags;
+
+	if (i915_request_is_suspended(rq))
+		return;
+
+	RQ_TRACE(rq, "suspending, ready? %d, active? %d\n",
+		 i915_request_is_ready(rq),
+		 i915_request_is_active(rq));
+
+	engine = lock_engine_irqsave(rq, flags);
+	if (!i915_request_set_suspended(rq))
+		goto unlock;
+
+	if (!i915_request_is_ready(rq))
+		goto unlock;
+
+	if (!i915_request_is_active(rq))
+		__intel_engine_suspend_request(engine, rq);
+
+unlock:
+	spin_unlock_irqrestore(&engine->active.lock, flags);
+
+	if (i915_request_is_active(rq))
+		intel_engine_pulse(engine);
+}
+
+void i915_request_resume(struct i915_request *rq)
+{
+	struct intel_engine_cs *engine;
+	unsigned long flags;
+
+	if (!i915_request_is_suspended(rq))
+		return;
+
+	RQ_TRACE(rq, "resuming\n");
+
+	engine = lock_engine_irqsave(rq, flags);
+
+	if (i915_request_clear_suspended(rq))
+		__intel_engine_resume_request(engine, rq);
+
+	spin_unlock_irqrestore(&engine->active.lock, flags);
+}
+
 struct i915_request *
 __intel_engine_rewind_requests(struct intel_engine_cs *engine)
 {
@@ -1038,22 +1089,26 @@ __intel_engine_rewind_requests(struct intel_engine_cs *engine)
 
 		__i915_request_unsubmit(rq);
 
-		if (__i915_request_has_started(rq)) {
-			rq->sched.deadline =
-				min(rq_deadline(rq),
-				    next_virtual_deadline(rq_prio(rq)));
-		}
-		GEM_BUG_ON(rq_deadline(rq) == I915_DEADLINE_NEVER);
+		if (i915_request_is_suspended(rq)) {
+			__intel_engine_suspend_request(engine, rq);
+		} else {
+			if (__i915_request_has_started(rq)) {
+				rq->sched.deadline =
+					min(rq_deadline(rq),
+					    next_virtual_deadline(rq_prio(rq)));
+			}
+			GEM_BUG_ON(rq_deadline(rq) == I915_DEADLINE_NEVER);
 
-		if (rq_deadline(rq) != deadline) {
-			deadline = rq_deadline(rq);
-			pl = lookup_priolist(engine, deadline);
-		}
-		GEM_BUG_ON(i915_sched_is_idle(&engine->active));
+			if (rq_deadline(rq) != deadline) {
+				deadline = rq_deadline(rq);
+				pl = lookup_priolist(engine, deadline);
+			}
+			GEM_BUG_ON(i915_sched_is_idle(&engine->active));
 
-		GEM_BUG_ON(i915_request_in_priority_queue(rq));
-		list_move(&rq->sched.link, pl);
-		set_bit(I915_FENCE_FLAG_PQUEUE, &rq->fence.flags);
+			GEM_BUG_ON(i915_request_in_priority_queue(rq));
+			list_move(&rq->sched.link, pl);
+			set_bit(I915_FENCE_FLAG_PQUEUE, &rq->fence.flags);
+		}
 
 		/* Check in case we rollback so far we wrap [size/2] */
 		if (intel_ring_direction(rq->ring,
@@ -1073,12 +1128,17 @@ bool __intel_engine_suspend_request(struct intel_engine_cs *engine,
 	LIST_HEAD(list);
 
 	lockdep_assert_held(&engine->active.lock);
-	GEM_BUG_ON(i915_request_on_hold(rq));
 	GEM_BUG_ON(rq->engine != engine);
 
 	if (__i915_request_is_complete(rq)) /* too late! */
 		return false;
 
+	if (i915_request_on_hold(rq))
+		return false;
+
+	ENGINE_TRACE(engine, "suspending request %llx:%lld\n",
+		     rq->fence.context, rq->fence.seqno);
+
 	/*
 	 * Transfer this request onto the hold queue to prevent it
 	 * being resumbitted to HW (and potentially completed) before we have
@@ -1150,6 +1210,12 @@ void __intel_engine_resume_request(struct intel_engine_cs *engine,
 
 	lockdep_assert_held(&engine->active.lock);
 
+	if (!i915_request_on_hold(rq))
+		return;
+
+	ENGINE_TRACE(engine, "resuming request %llx:%lld\n",
+		     rq->fence.context, rq->fence.seqno);
+
 	/*
 	 * Move this request back to the priority queue, and all of its
 	 * children and grandchildren that were suspended along with it.
diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c
index 66cdda2b9abf..e843d1ba5e04 100644
--- a/drivers/gpu/drm/i915/selftests/i915_request.c
+++ b/drivers/gpu/drm/i915/selftests/i915_request.c
@@ -854,6 +854,173 @@ static int live_cancel_request(void *arg)
 	return 0;
 }
 
+static int __suspend_inactive(struct intel_engine_cs *engine)
+{
+	struct intel_context *ce;
+	struct igt_spinner spin;
+	struct i915_request *rq;
+	int err = 0;
+
+	if (igt_spinner_init(&spin, engine->gt))
+		return -ENOMEM;
+
+	ce = intel_context_create(engine);
+	if (IS_ERR(ce)) {
+		err = PTR_ERR(ce);
+		goto out_spin;
+	}
+
+	rq = intel_context_create_request(ce);
+	if (IS_ERR(rq)) {
+		err = PTR_ERR(rq);
+		goto out_ce;
+	}
+
+	pr_debug("%s: Suspending inactive request\n", engine->name);
+	i915_request_suspend(rq);
+	i915_request_get(rq);
+	i915_request_add(rq);
+
+	intel_engine_flush_submission(engine);
+	GEM_BUG_ON(!i915_request_is_ready(rq));
+
+	if (i915_request_is_active(rq)) {
+		GEM_TRACE_ERR("Suspended request executed!\n");
+		GEM_TRACE_DUMP();
+		err = -EINVAL;
+		goto out_rq;
+	}
+
+	i915_request_resume(rq);
+	if (i915_request_wait(rq, 0, HZ / 5) < 0) {
+		GEM_TRACE_ERR("Resumed request did not complete\n");
+		GEM_TRACE_DUMP();
+		err = -ETIME;
+	}
+
+out_rq:
+	i915_request_put(rq);
+out_ce:
+	intel_context_put(ce);
+out_spin:
+	igt_spinner_fini(&spin);
+	return err;
+}
+
+static int __suspend_active(struct intel_engine_cs *engine)
+{
+	struct intel_context *ce;
+	struct igt_spinner spin;
+	struct i915_request *rq;
+	int err = 0;
+
+	if (igt_spinner_init(&spin, engine->gt))
+		return -ENOMEM;
+
+	ce = intel_context_create(engine);
+	if (IS_ERR(ce)) {
+		err = PTR_ERR(ce);
+		goto out_spin;
+	}
+
+	rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK);
+	if (IS_ERR(rq)) {
+		err = PTR_ERR(rq);
+		goto out_ce;
+	}
+
+	pr_debug("%s: Suspending active request\n", engine->name);
+	i915_request_get(rq);
+	i915_request_add(rq);
+	if (!igt_wait_for_spinner(&spin, rq)) {
+		struct drm_printer p = drm_info_printer(engine->i915->drm.dev);
+
+		pr_err("Failed to start spinner on %s\n", engine->name);
+		intel_engine_dump(engine, &p, "%s\n", engine->name);
+		err = -ETIME;
+		goto out_rq;
+	}
+	i915_request_suspend(rq);
+
+	intel_engine_flush_submission(engine);
+	igt_spinner_end(&spin);
+
+	if (i915_request_completed(rq)) {
+		GEM_TRACE_ERR("%s: Request completed before suspension\n",
+			      engine->name);
+		GEM_TRACE_DUMP();
+		err = -EINVAL;
+		goto out_rq;
+	}
+
+	if (i915_request_is_active(rq)) {
+		GEM_TRACE_ERR("%s: Request is still active after suspension\n",
+			      engine->name);
+		GEM_TRACE_DUMP();
+		err = -EINVAL;
+		goto out_rq;
+	}
+
+	i915_request_resume(rq);
+	if (i915_request_wait(rq, 0, HZ / 5) < 0) {
+		GEM_TRACE_ERR("%s: Failed to resume request\n", engine->name);
+		GEM_TRACE_DUMP();
+		err = -ETIME;
+		goto out_rq;
+	}
+
+out_rq:
+	i915_request_put(rq);
+out_ce:
+	intel_context_put(ce);
+out_spin:
+	igt_spinner_fini(&spin);
+	return err;
+}
+
+static int live_suspend_request(void *arg)
+{
+	struct drm_i915_private *i915 = arg;
+	struct intel_engine_cs *engine;
+
+	/*
+	 * Check temporary suspend/resume of requests. If a suspend request
+	 * is currently on the GPU, it and all of its dependents should be
+	 * removed from the GPU and held until the oldest antecedent is
+	 * resumed.
+	 */
+
+	for_each_uabi_engine(engine, i915) {
+		struct igt_live_test t;
+		int err, err2;
+
+		if (!intel_engine_has_preemption(engine))
+			continue;
+
+		err = igt_live_test_begin(&t, i915, __func__, engine->name);
+		if (err)
+			return err;
+
+		err = __suspend_inactive(engine);
+		if (err == 0)
+			err = __suspend_active(engine);
+#if 0
+		if (err == 0)
+			err = __suspend_active_chain(engine);
+		if (err == 0)
+			err = __suspend_completed(engine);
+#endif
+
+		err2 = igt_live_test_end(&t);
+		if (err)
+			return err;
+		if (err2)
+			return err2;
+	}
+
+	return 0;
+}
+
 static struct i915_vma *empty_batch(struct drm_i915_private *i915)
 {
 	struct drm_i915_gem_object *obj;
@@ -1732,6 +1899,7 @@ int i915_request_live_selftests(struct drm_i915_private *i915)
 		SUBTEST(live_parallel_engines),
 		SUBTEST(live_empty_request),
 		SUBTEST(live_cancel_request),
+		SUBTEST(live_suspend_request),
 		SUBTEST(live_breadcrumbs_smoketest),
 	};
 
-- 
2.20.1



More information about the Intel-gfx-trybot mailing list