All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] drm/i915/execlists: Preempt-to-busy
@ 2019-06-20  7:05 Chris Wilson
  2019-06-20  7:05 ` [PATCH 2/3] drm/i915/execlists: Minimalistic timeslicing Chris Wilson
                   ` (10 more replies)
  0 siblings, 11 replies; 23+ messages in thread
From: Chris Wilson @ 2019-06-20  7:05 UTC (permalink / raw)
  To: intel-gfx

When using a global seqno, we required a precise stop-the-workd event to
handle preemption and unwind the global seqno counter. To accomplish
this, we would preempt to a special out-of-band context and wait for the
machine to report that it was idle. Given an idle machine, we could very
precisely see which requests had completed and which we needed to feed
back into the run queue.

However, now that we have scrapped the global seqno, we no longer need
to precisely unwind the global counter and only track requests by their
per-context seqno. This allows us to loosely unwind inflight requests
while scheduling a preemption, with the enormous caveat that the
requests we put back on the run queue are still _inflight_ (until the
preemption request is complete). This makes request tracking much more
messy, as at any point then we can see a completed request that we
believe is not currently scheduled for execution. We also have to be
careful not to rewind RING_TAIL past RING_HEAD on preempting to the
running context, and for this we use a semaphore to prevent completion
of the request before continuing.

To accomplish this feat, we change how we track requests scheduled to
the HW. Instead of appending our requests onto a single list as we
submit, we track each submission to ELSP as its own block. Then upon
receiving the CS preemption event, we promote the pending block to the
inflight block (discarding what was previously being tracked). As normal
CS completion events arrive, we then remove stale entries from the
inflight tracker.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gem/i915_gem_context.c   |   2 +-
 drivers/gpu/drm/i915/gt/intel_context_types.h |   5 +
 drivers/gpu/drm/i915/gt/intel_engine.h        |  61 +-
 drivers/gpu/drm/i915/gt/intel_engine_cs.c     |  63 +-
 drivers/gpu/drm/i915/gt/intel_engine_types.h  |  52 +-
 drivers/gpu/drm/i915/gt/intel_lrc.c           | 675 ++++++++----------
 drivers/gpu/drm/i915/i915_gpu_error.c         |  19 +-
 drivers/gpu/drm/i915/i915_request.c           |   6 +
 drivers/gpu/drm/i915/i915_request.h           |   1 +
 drivers/gpu/drm/i915/i915_scheduler.c         |   3 +-
 drivers/gpu/drm/i915/i915_utils.h             |  12 +
 drivers/gpu/drm/i915/intel_guc_submission.c   | 175 ++---
 drivers/gpu/drm/i915/selftests/i915_request.c |   8 +-
 13 files changed, 472 insertions(+), 610 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index 0f2c22a3bcb6..35871c8a42a6 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -646,7 +646,7 @@ static void init_contexts(struct drm_i915_private *i915)
 
 static bool needs_preempt_context(struct drm_i915_private *i915)
 {
-	return HAS_EXECLISTS(i915);
+	return USES_GUC_SUBMISSION(i915);
 }
 
 int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
diff --git a/drivers/gpu/drm/i915/gt/intel_context_types.h b/drivers/gpu/drm/i915/gt/intel_context_types.h
index 08049ee91cee..4c0e211c715d 100644
--- a/drivers/gpu/drm/i915/gt/intel_context_types.h
+++ b/drivers/gpu/drm/i915/gt/intel_context_types.h
@@ -13,6 +13,7 @@
 #include <linux/types.h>
 
 #include "i915_active_types.h"
+#include "i915_utils.h"
 #include "intel_engine_types.h"
 #include "intel_sseu.h"
 
@@ -38,6 +39,10 @@ struct intel_context {
 	struct i915_gem_context *gem_context;
 	struct intel_engine_cs *engine;
 	struct intel_engine_cs *inflight;
+#define intel_context_inflight(ce) ptr_mask_bits((ce)->inflight, 2)
+#define intel_context_inflight_count(ce)  ptr_unmask_bits((ce)->inflight, 2)
+#define intel_context_inflight_inc(ce) ptr_count_inc(&(ce)->inflight)
+#define intel_context_inflight_dec(ce) ptr_count_dec(&(ce)->inflight)
 
 	struct list_head signal_link;
 	struct list_head signals;
diff --git a/drivers/gpu/drm/i915/gt/intel_engine.h b/drivers/gpu/drm/i915/gt/intel_engine.h
index 2f1c6871ee95..9bb6ff76680e 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine.h
+++ b/drivers/gpu/drm/i915/gt/intel_engine.h
@@ -125,71 +125,26 @@ hangcheck_action_to_str(const enum intel_engine_hangcheck_action a)
 
 void intel_engines_set_scheduler_caps(struct drm_i915_private *i915);
 
-static inline void
-execlists_set_active(struct intel_engine_execlists *execlists,
-		     unsigned int bit)
-{
-	__set_bit(bit, (unsigned long *)&execlists->active);
-}
-
-static inline bool
-execlists_set_active_once(struct intel_engine_execlists *execlists,
-			  unsigned int bit)
-{
-	return !__test_and_set_bit(bit, (unsigned long *)&execlists->active);
-}
-
-static inline void
-execlists_clear_active(struct intel_engine_execlists *execlists,
-		       unsigned int bit)
-{
-	__clear_bit(bit, (unsigned long *)&execlists->active);
-}
-
-static inline void
-execlists_clear_all_active(struct intel_engine_execlists *execlists)
+static inline unsigned int
+execlists_num_ports(const struct intel_engine_execlists * const execlists)
 {
-	execlists->active = 0;
+	return execlists->port_mask + 1;
 }
 
-static inline bool
-execlists_is_active(const struct intel_engine_execlists *execlists,
-		    unsigned int bit)
+static inline struct i915_request *
+execlists_active(const struct intel_engine_execlists *execlists)
 {
-	return test_bit(bit, (unsigned long *)&execlists->active);
+	GEM_BUG_ON(execlists->active - execlists->inflight >
+		   execlists_num_ports(execlists));
+	return READ_ONCE(*execlists->active);
 }
 
-void execlists_user_begin(struct intel_engine_execlists *execlists,
-			  const struct execlist_port *port);
-void execlists_user_end(struct intel_engine_execlists *execlists);
-
 void
 execlists_cancel_port_requests(struct intel_engine_execlists * const execlists);
 
 struct i915_request *
 execlists_unwind_incomplete_requests(struct intel_engine_execlists *execlists);
 
-static inline unsigned int
-execlists_num_ports(const struct intel_engine_execlists * const execlists)
-{
-	return execlists->port_mask + 1;
-}
-
-static inline struct execlist_port *
-execlists_port_complete(struct intel_engine_execlists * const execlists,
-			struct execlist_port * const port)
-{
-	const unsigned int m = execlists->port_mask;
-
-	GEM_BUG_ON(port_index(port, execlists) != 0);
-	GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_USER));
-
-	memmove(port, port + 1, m * sizeof(struct execlist_port));
-	memset(port + m, 0, sizeof(struct execlist_port));
-
-	return port;
-}
-
 static inline u32
 intel_read_status_page(const struct intel_engine_cs *engine, int reg)
 {
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
index 7fd33e81c2d9..d45328e254dc 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
@@ -508,6 +508,10 @@ void intel_engine_init_execlists(struct intel_engine_cs *engine)
 	GEM_BUG_ON(!is_power_of_2(execlists_num_ports(execlists)));
 	GEM_BUG_ON(execlists_num_ports(execlists) > EXECLIST_MAX_PORTS);
 
+	memset(execlists->pending, 0, sizeof(execlists->pending));
+	execlists->active =
+		memset(execlists->inflight, 0, sizeof(execlists->inflight));
+
 	execlists->queue_priority_hint = INT_MIN;
 	execlists->queue = RB_ROOT_CACHED;
 }
@@ -1152,7 +1156,7 @@ bool intel_engine_is_idle(struct intel_engine_cs *engine)
 		return true;
 
 	/* Waiting to drain ELSP? */
-	if (READ_ONCE(engine->execlists.active)) {
+	if (execlists_active(&engine->execlists)) {
 		struct tasklet_struct *t = &engine->execlists.tasklet;
 
 		synchronize_hardirq(engine->i915->drm.irq);
@@ -1169,7 +1173,7 @@ bool intel_engine_is_idle(struct intel_engine_cs *engine)
 		/* Otherwise flush the tasklet if it was on another cpu */
 		tasklet_unlock_wait(t);
 
-		if (READ_ONCE(engine->execlists.active))
+		if (execlists_active(&engine->execlists))
 			return false;
 	}
 
@@ -1367,6 +1371,7 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine,
 	}
 
 	if (HAS_EXECLISTS(dev_priv)) {
+		struct i915_request * const *port, *rq;
 		const u32 *hws =
 			&engine->status_page.addr[I915_HWS_CSB_BUF0_INDEX];
 		const u8 num_entries = execlists->csb_size;
@@ -1399,27 +1404,33 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine,
 		}
 
 		spin_lock_irqsave(&engine->active.lock, flags);
-		for (idx = 0; idx < execlists_num_ports(execlists); idx++) {
-			struct i915_request *rq;
-			unsigned int count;
+		for (port = execlists->active; (rq = *port); port++) {
+			char hdr[80];
+			int len;
+
+			len = snprintf(hdr, sizeof(hdr),
+				       "\t\tActive[%d: ",
+				       (int)(port - execlists->active));
+			if (!i915_request_signaled(rq))
+				len += snprintf(hdr + len, sizeof(hdr) - len,
+						"ring:{start:%08x, hwsp:%08x, seqno:%08x}, ",
+						i915_ggtt_offset(rq->ring->vma),
+						rq->timeline->hwsp_offset,
+						hwsp_seqno(rq));
+			snprintf(hdr + len, sizeof(hdr) - len, "rq: ");
+			print_request(m, rq, hdr);
+		}
+		for (port = execlists->pending; (rq = *port); port++) {
 			char hdr[80];
 
-			rq = port_unpack(&execlists->port[idx], &count);
-			if (!rq) {
-				drm_printf(m, "\t\tELSP[%d] idle\n", idx);
-			} else if (!i915_request_signaled(rq)) {
-				snprintf(hdr, sizeof(hdr),
-					 "\t\tELSP[%d] count=%d, ring:{start:%08x, hwsp:%08x, seqno:%08x}, rq: ",
-					 idx, count,
-					 i915_ggtt_offset(rq->ring->vma),
-					 rq->timeline->hwsp_offset,
-					 hwsp_seqno(rq));
-				print_request(m, rq, hdr);
-			} else {
-				print_request(m, rq, "\t\tELSP[%d] rq: ");
-			}
+			snprintf(hdr, sizeof(hdr),
+				 "\t\tPending[%d] ring:{start:%08x, hwsp:%08x, seqno:%08x}, rq: ",
+				 (int)(port - execlists->pending),
+				 i915_ggtt_offset(rq->ring->vma),
+				 rq->timeline->hwsp_offset,
+				 hwsp_seqno(rq));
+			print_request(m, rq, hdr);
 		}
-		drm_printf(m, "\t\tHW active? 0x%x\n", execlists->active);
 		spin_unlock_irqrestore(&engine->active.lock, flags);
 	} else if (INTEL_GEN(dev_priv) > 6) {
 		drm_printf(m, "\tPP_DIR_BASE: 0x%08x\n",
@@ -1583,15 +1594,19 @@ int intel_enable_engine_stats(struct intel_engine_cs *engine)
 	}
 
 	if (engine->stats.enabled++ == 0) {
-		const struct execlist_port *port = execlists->port;
-		unsigned int num_ports = execlists_num_ports(execlists);
+		struct i915_request * const *port;
+		struct i915_request *rq;
 
 		engine->stats.enabled_at = ktime_get();
 
 		/* XXX submission method oblivious? */
-		while (num_ports-- && port_isset(port)) {
+		for (port = execlists->active; (rq = *port); port++)
 			engine->stats.active++;
-			port++;
+
+		for (port = execlists->pending; (rq = *port); port++) {
+			/* Exclude any contexts already counted in active */
+			if (intel_context_inflight_count(rq->hw_context) == 1)
+				engine->stats.active++;
 		}
 
 		if (engine->stats.active)
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_types.h b/drivers/gpu/drm/i915/gt/intel_engine_types.h
index 43e975a26016..411b7a807b99 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_types.h
+++ b/drivers/gpu/drm/i915/gt/intel_engine_types.h
@@ -172,51 +172,10 @@ struct intel_engine_execlists {
 	 */
 	u32 __iomem *ctrl_reg;
 
-	/**
-	 * @port: execlist port states
-	 *
-	 * For each hardware ELSP (ExecList Submission Port) we keep
-	 * track of the last request and the number of times we submitted
-	 * that port to hw. We then count the number of times the hw reports
-	 * a context completion or preemption. As only one context can
-	 * be active on hw, we limit resubmission of context to port[0]. This
-	 * is called Lite Restore, of the context.
-	 */
-	struct execlist_port {
-		/**
-		 * @request_count: combined request and submission count
-		 */
-		struct i915_request *request_count;
-#define EXECLIST_COUNT_BITS 2
-#define port_request(p) ptr_mask_bits((p)->request_count, EXECLIST_COUNT_BITS)
-#define port_count(p) ptr_unmask_bits((p)->request_count, EXECLIST_COUNT_BITS)
-#define port_pack(rq, count) ptr_pack_bits(rq, count, EXECLIST_COUNT_BITS)
-#define port_unpack(p, count) ptr_unpack_bits((p)->request_count, count, EXECLIST_COUNT_BITS)
-#define port_set(p, packed) ((p)->request_count = (packed))
-#define port_isset(p) ((p)->request_count)
-#define port_index(p, execlists) ((p) - (execlists)->port)
-
-		/**
-		 * @context_id: context ID for port
-		 */
-		GEM_DEBUG_DECL(u32 context_id);
-
 #define EXECLIST_MAX_PORTS 2
-	} port[EXECLIST_MAX_PORTS];
-
-	/**
-	 * @active: is the HW active? We consider the HW as active after
-	 * submitting any context for execution and until we have seen the
-	 * last context completion event. After that, we do not expect any
-	 * more events until we submit, and so can park the HW.
-	 *
-	 * As we have a small number of different sources from which we feed
-	 * the HW, we track the state of each inside a single bitfield.
-	 */
-	unsigned int active;
-#define EXECLISTS_ACTIVE_USER 0
-#define EXECLISTS_ACTIVE_PREEMPT 1
-#define EXECLISTS_ACTIVE_HWACK 2
+	struct i915_request * const *active;
+	struct i915_request *inflight[EXECLIST_MAX_PORTS + 1 /* sentinel */];
+	struct i915_request *pending[EXECLIST_MAX_PORTS + 1];
 
 	/**
 	 * @port_mask: number of execlist ports - 1
@@ -257,11 +216,6 @@ struct intel_engine_execlists {
 	 */
 	u32 *csb_status;
 
-	/**
-	 * @preempt_complete_status: expected CSB upon completing preemption
-	 */
-	u32 preempt_complete_status;
-
 	/**
 	 * @csb_size: context status buffer FIFO size
 	 */
diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
index 82b7ace62d97..dc077b536fa5 100644
--- a/drivers/gpu/drm/i915/gt/intel_lrc.c
+++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
@@ -161,6 +161,8 @@
 #define GEN8_CTX_STATUS_COMPLETED_MASK \
 	 (GEN8_CTX_STATUS_COMPLETE | GEN8_CTX_STATUS_PREEMPTED)
 
+#define CTX_DESC_FORCE_RESTORE BIT_ULL(2)
+
 /* Typical size of the average request (2 pipecontrols and a MI_BB) */
 #define EXECLISTS_REQUEST_SIZE 64 /* bytes */
 #define WA_TAIL_DWORDS 2
@@ -221,6 +223,14 @@ static void execlists_init_reg_state(u32 *reg_state,
 				     struct intel_engine_cs *engine,
 				     struct intel_ring *ring);
 
+static inline u32 intel_hws_preempt_address(struct intel_engine_cs *engine)
+{
+	return (i915_ggtt_offset(engine->status_page.vma) +
+		I915_GEM_HWS_PREEMPT_ADDR);
+}
+
+#define ring_pause(E) ((E)->status_page.addr[I915_GEM_HWS_PREEMPT])
+
 static inline struct i915_priolist *to_priolist(struct rb_node *rb)
 {
 	return rb_entry(rb, struct i915_priolist, node);
@@ -271,12 +281,6 @@ static inline bool need_preempt(const struct intel_engine_cs *engine,
 {
 	int last_prio;
 
-	if (!engine->preempt_context)
-		return false;
-
-	if (i915_request_completed(rq))
-		return false;
-
 	/*
 	 * Check if the current priority hint merits a preemption attempt.
 	 *
@@ -338,9 +342,6 @@ __maybe_unused static inline bool
 assert_priority_queue(const struct i915_request *prev,
 		      const struct i915_request *next)
 {
-	const struct intel_engine_execlists *execlists =
-		&prev->engine->execlists;
-
 	/*
 	 * Without preemption, the prev may refer to the still active element
 	 * which we refuse to let go.
@@ -348,7 +349,7 @@ assert_priority_queue(const struct i915_request *prev,
 	 * Even with preemption, there are times when we think it is better not
 	 * to preempt and leave an ostensibly lower priority request in flight.
 	 */
-	if (port_request(execlists->port) == prev)
+	if (i915_request_is_active(prev))
 		return true;
 
 	return rq_prio(prev) >= rq_prio(next);
@@ -442,13 +443,11 @@ __unwind_incomplete_requests(struct intel_engine_cs *engine)
 		struct intel_engine_cs *owner;
 
 		if (i915_request_completed(rq))
-			break;
+			continue; /* XXX */
 
 		__i915_request_unsubmit(rq);
 		unwind_wa_tail(rq);
 
-		GEM_BUG_ON(rq->hw_context->inflight);
-
 		/*
 		 * Push the request back into the queue for later resubmission.
 		 * If this request is not native to this physical engine (i.e.
@@ -500,32 +499,32 @@ execlists_context_status_change(struct i915_request *rq, unsigned long status)
 				   status, rq);
 }
 
-inline void
-execlists_user_begin(struct intel_engine_execlists *execlists,
-		     const struct execlist_port *port)
+static inline struct i915_request *
+execlists_schedule_in(struct i915_request *rq, int idx)
 {
-	execlists_set_active_once(execlists, EXECLISTS_ACTIVE_USER);
-}
+	struct intel_context *ce = rq->hw_context;
+	int count;
 
-inline void
-execlists_user_end(struct intel_engine_execlists *execlists)
-{
-	execlists_clear_active(execlists, EXECLISTS_ACTIVE_USER);
-}
+	trace_i915_request_in(rq, idx);
 
-static inline void
-execlists_context_schedule_in(struct i915_request *rq)
-{
-	GEM_BUG_ON(rq->hw_context->inflight);
+	count = intel_context_inflight_count(ce);
+	if (!count) {
+		intel_context_get(ce);
+		ce->inflight = rq->engine;
+
+		execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_IN);
+		intel_engine_context_in(ce->inflight);
+	}
+
+	intel_context_inflight_inc(ce);
+	GEM_BUG_ON(intel_context_inflight(ce) != rq->engine);
 
-	execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_IN);
-	intel_engine_context_in(rq->engine);
-	rq->hw_context->inflight = rq->engine;
+	return i915_request_get(rq);
 }
 
-static void kick_siblings(struct i915_request *rq)
+static void kick_siblings(struct i915_request *rq, struct intel_context *ce)
 {
-	struct virtual_engine *ve = to_virtual_engine(rq->hw_context->engine);
+	struct virtual_engine *ve = container_of(ce, typeof(*ve), context);
 	struct i915_request *next = READ_ONCE(ve->request);
 
 	if (next && next->execution_mask & ~rq->execution_mask)
@@ -533,29 +532,42 @@ static void kick_siblings(struct i915_request *rq)
 }
 
 static inline void
-execlists_context_schedule_out(struct i915_request *rq, unsigned long status)
+execlists_schedule_out(struct i915_request *rq)
 {
-	rq->hw_context->inflight = NULL;
-	intel_engine_context_out(rq->engine);
-	execlists_context_status_change(rq, status);
+	struct intel_context *ce = rq->hw_context;
+
+	GEM_BUG_ON(!intel_context_inflight_count(ce));
+
 	trace_i915_request_out(rq);
 
-	/*
-	 * If this is part of a virtual engine, its next request may have
-	 * been blocked waiting for access to the active context. We have
-	 * to kick all the siblings again in case we need to switch (e.g.
-	 * the next request is not runnable on this engine). Hopefully,
-	 * we will already have submitted the next request before the
-	 * tasklet runs and do not need to rebuild each virtual tree
-	 * and kick everyone again.
-	 */
-	if (rq->engine != rq->hw_context->engine)
-		kick_siblings(rq);
+	intel_context_inflight_dec(ce);
+	if (!intel_context_inflight_count(ce)) {
+		intel_engine_context_out(ce->inflight);
+		execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_OUT);
+
+		ce->inflight = NULL;
+		intel_context_put(ce);
+
+		/*
+		 * If this is part of a virtual engine, its next request may
+		 * have been blocked waiting for access to the active context.
+		 * We have to kick all the siblings again in case we need to
+		 * switch (e.g. the next request is not runnable on this
+		 * engine). Hopefully, we will already have submitted the next
+		 * request before the tasklet runs and do not need to rebuild
+		 * each virtual tree and kick everyone again.
+		 */
+		if (rq->engine != ce->engine)
+			kick_siblings(rq, ce);
+	}
+
+	i915_request_put(rq);
 }
 
-static u64 execlists_update_context(struct i915_request *rq)
+static u64 execlists_update_context(const struct i915_request *rq)
 {
 	struct intel_context *ce = rq->hw_context;
+	u64 desc;
 
 	ce->lrc_reg_state[CTX_RING_TAIL + 1] =
 		intel_ring_set_tail(rq->ring, rq->tail);
@@ -576,7 +588,11 @@ static u64 execlists_update_context(struct i915_request *rq)
 	 * wmb).
 	 */
 	mb();
-	return ce->lrc_desc;
+
+	desc = ce->lrc_desc;
+	ce->lrc_desc &= ~CTX_DESC_FORCE_RESTORE;
+
+	return desc;
 }
 
 static inline void write_desc(struct intel_engine_execlists *execlists, u64 desc, u32 port)
@@ -590,12 +606,56 @@ static inline void write_desc(struct intel_engine_execlists *execlists, u64 desc
 	}
 }
 
+static __maybe_unused void
+trace_ports(const struct intel_engine_execlists *execlists,
+	    const char *msg,
+	    struct i915_request * const *ports)
+{
+	const struct intel_engine_cs *engine =
+		container_of(execlists, typeof(*engine), execlists);
+
+	GEM_TRACE("%s: %s { %llx:%lld%s, %llx:%lld }\n",
+		  engine->name, msg,
+		  ports[0]->fence.context,
+		  ports[0]->fence.seqno,
+		  i915_request_completed(ports[0]) ? "!" :
+		  i915_request_started(ports[0]) ? "*" :
+		  "",
+		  ports[1] ? ports[1]->fence.context : 0,
+		  ports[1] ? ports[1]->fence.seqno : 0);
+}
+
+static __maybe_unused bool
+assert_pending_valid(const struct intel_engine_execlists *execlists,
+		     const char *msg)
+{
+	struct i915_request * const *port, *rq;
+	struct intel_context *ce = NULL;
+
+	trace_ports(execlists, msg, execlists->pending);
+
+	if (execlists->pending[execlists_num_ports(execlists)])
+		return false;
+
+	for (port = execlists->pending; (rq = *port); port++) {
+		if (ce == rq->hw_context)
+			return false;
+
+		ce = rq->hw_context;
+		GEM_BUG_ON(i915_active_is_idle(&ce->active));
+		GEM_BUG_ON(!i915_vma_is_pinned(ce->state));
+	}
+
+	return ce;
+}
+
 static void execlists_submit_ports(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists *execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
 	unsigned int n;
 
+	GEM_BUG_ON(!assert_pending_valid(execlists, "submit"));
+
 	/*
 	 * We can skip acquiring intel_runtime_pm_get() here as it was taken
 	 * on our behalf by the request (see i915_gem_mark_busy()) and it will
@@ -613,38 +673,16 @@ static void execlists_submit_ports(struct intel_engine_cs *engine)
 	 * of elsq entries, keep this in mind before changing the loop below.
 	 */
 	for (n = execlists_num_ports(execlists); n--; ) {
-		struct i915_request *rq;
-		unsigned int count;
-		u64 desc;
+		struct i915_request *rq = execlists->pending[n];
 
-		rq = port_unpack(&port[n], &count);
-		if (rq) {
-			GEM_BUG_ON(count > !n);
-			if (!count++)
-				execlists_context_schedule_in(rq);
-			port_set(&port[n], port_pack(rq, count));
-			desc = execlists_update_context(rq);
-			GEM_DEBUG_EXEC(port[n].context_id = upper_32_bits(desc));
-
-			GEM_TRACE("%s in[%d]:  ctx=%d.%d, fence %llx:%lld (current %d), prio=%d\n",
-				  engine->name, n,
-				  port[n].context_id, count,
-				  rq->fence.context, rq->fence.seqno,
-				  hwsp_seqno(rq),
-				  rq_prio(rq));
-		} else {
-			GEM_BUG_ON(!n);
-			desc = 0;
-		}
-
-		write_desc(execlists, desc, n);
+		write_desc(execlists,
+			   rq ? execlists_update_context(rq) : 0,
+			   n);
 	}
 
 	/* we need to manually load the submit queue */
 	if (execlists->ctrl_reg)
 		writel(EL_CTRL_LOAD, execlists->ctrl_reg);
-
-	execlists_clear_active(execlists, EXECLISTS_ACTIVE_HWACK);
 }
 
 static bool ctx_single_port_submission(const struct intel_context *ce)
@@ -668,6 +706,7 @@ static bool can_merge_ctx(const struct intel_context *prev,
 static bool can_merge_rq(const struct i915_request *prev,
 			 const struct i915_request *next)
 {
+	GEM_BUG_ON(prev == next);
 	GEM_BUG_ON(!assert_priority_queue(prev, next));
 
 	if (!can_merge_ctx(prev->hw_context, next->hw_context))
@@ -676,58 +715,6 @@ static bool can_merge_rq(const struct i915_request *prev,
 	return true;
 }
 
-static void port_assign(struct execlist_port *port, struct i915_request *rq)
-{
-	GEM_BUG_ON(rq == port_request(port));
-
-	if (port_isset(port))
-		i915_request_put(port_request(port));
-
-	port_set(port, port_pack(i915_request_get(rq), port_count(port)));
-}
-
-static void inject_preempt_context(struct intel_engine_cs *engine)
-{
-	struct intel_engine_execlists *execlists = &engine->execlists;
-	struct intel_context *ce = engine->preempt_context;
-	unsigned int n;
-
-	GEM_BUG_ON(execlists->preempt_complete_status !=
-		   upper_32_bits(ce->lrc_desc));
-
-	/*
-	 * Switch to our empty preempt context so
-	 * the state of the GPU is known (idle).
-	 */
-	GEM_TRACE("%s\n", engine->name);
-	for (n = execlists_num_ports(execlists); --n; )
-		write_desc(execlists, 0, n);
-
-	write_desc(execlists, ce->lrc_desc, n);
-
-	/* we need to manually load the submit queue */
-	if (execlists->ctrl_reg)
-		writel(EL_CTRL_LOAD, execlists->ctrl_reg);
-
-	execlists_clear_active(execlists, EXECLISTS_ACTIVE_HWACK);
-	execlists_set_active(execlists, EXECLISTS_ACTIVE_PREEMPT);
-
-	(void)I915_SELFTEST_ONLY(execlists->preempt_hang.count++);
-}
-
-static void complete_preempt_context(struct intel_engine_execlists *execlists)
-{
-	GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT));
-
-	if (inject_preempt_hang(execlists))
-		return;
-
-	execlists_cancel_port_requests(execlists);
-	__unwind_incomplete_requests(container_of(execlists,
-						  struct intel_engine_cs,
-						  execlists));
-}
-
 static void virtual_update_register_offsets(u32 *regs,
 					    struct intel_engine_cs *engine)
 {
@@ -792,7 +779,7 @@ static bool virtual_matches(const struct virtual_engine *ve,
 	 * we reuse the register offsets). This is a very small
 	 * hystersis on the greedy seelction algorithm.
 	 */
-	inflight = READ_ONCE(ve->context.inflight);
+	inflight = intel_context_inflight(&ve->context);
 	if (inflight && inflight != engine)
 		return false;
 
@@ -815,13 +802,23 @@ static void virtual_xfer_breadcrumbs(struct virtual_engine *ve,
 	spin_unlock(&old->breadcrumbs.irq_lock);
 }
 
+static struct i915_request *
+last_active(const struct intel_engine_execlists *execlists)
+{
+	struct i915_request * const *last = execlists->active;
+
+	while (*last && i915_request_completed(*last))
+		last++;
+
+	return *last;
+}
+
 static void execlists_dequeue(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
-	const struct execlist_port * const last_port =
-		&execlists->port[execlists->port_mask];
-	struct i915_request *last = port_request(port);
+	struct i915_request **port = execlists->pending;
+	struct i915_request ** const last_port = port + execlists->port_mask;
+	struct i915_request *last;
 	struct rb_node *rb;
 	bool submit = false;
 
@@ -867,65 +864,72 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 		break;
 	}
 
+	/*
+	 * If the queue is higher priority than the last
+	 * request in the currently active context, submit afresh.
+	 * We will resubmit again afterwards in case we need to split
+	 * the active context to interject the preemption request,
+	 * i.e. we will retrigger preemption following the ack in case
+	 * of trouble.
+	 */
+	last = last_active(execlists);
 	if (last) {
-		/*
-		 * Don't resubmit or switch until all outstanding
-		 * preemptions (lite-restore) are seen. Then we
-		 * know the next preemption status we see corresponds
-		 * to this ELSP update.
-		 */
-		GEM_BUG_ON(!execlists_is_active(execlists,
-						EXECLISTS_ACTIVE_USER));
-		GEM_BUG_ON(!port_count(&port[0]));
-
-		/*
-		 * If we write to ELSP a second time before the HW has had
-		 * a chance to respond to the previous write, we can confuse
-		 * the HW and hit "undefined behaviour". After writing to ELSP,
-		 * we must then wait until we see a context-switch event from
-		 * the HW to indicate that it has had a chance to respond.
-		 */
-		if (!execlists_is_active(execlists, EXECLISTS_ACTIVE_HWACK))
-			return;
-
 		if (need_preempt(engine, last, rb)) {
-			inject_preempt_context(engine);
-			return;
-		}
+			GEM_TRACE("%s: preempting last=%llx:%lld, prio=%d, hint=%d\n",
+				  engine->name,
+				  last->fence.context,
+				  last->fence.seqno,
+				  last->sched.attr.priority,
+				  execlists->queue_priority_hint);
+			/*
+			 * Don't let the RING_HEAD advance past the breadcrumb
+			 * as we unwind (and until we resubmit) so that we do
+			 * not accidentally tell it to go backwards.
+			 */
+			ring_pause(engine) = 1;
 
-		/*
-		 * In theory, we could coalesce more requests onto
-		 * the second port (the first port is active, with
-		 * no preemptions pending). However, that means we
-		 * then have to deal with the possible lite-restore
-		 * of the second port (as we submit the ELSP, there
-		 * may be a context-switch) but also we may complete
-		 * the resubmission before the context-switch. Ergo,
-		 * coalescing onto the second port will cause a
-		 * preemption event, but we cannot predict whether
-		 * that will affect port[0] or port[1].
-		 *
-		 * If the second port is already active, we can wait
-		 * until the next context-switch before contemplating
-		 * new requests. The GPU will be busy and we should be
-		 * able to resubmit the new ELSP before it idles,
-		 * avoiding pipeline bubbles (momentary pauses where
-		 * the driver is unable to keep up the supply of new
-		 * work). However, we have to double check that the
-		 * priorities of the ports haven't been switch.
-		 */
-		if (port_count(&port[1]))
-			return;
+			/*
+			 * Note that we have not stopped the GPU at this point,
+			 * so we are unwinding the incomplete requests as they
+			 * remain inflight and so by the time we do complete
+			 * the preemption, some of the unwound requests may
+			 * complete!
+			 */
+			__unwind_incomplete_requests(engine);
 
-		/*
-		 * WaIdleLiteRestore:bdw,skl
-		 * Apply the wa NOOPs to prevent
-		 * ring:HEAD == rq:TAIL as we resubmit the
-		 * request. See gen8_emit_fini_breadcrumb() for
-		 * where we prepare the padding after the
-		 * end of the request.
-		 */
-		last->tail = last->wa_tail;
+			/*
+			 * If we need to return to the preempted context, we
+			 * need to skip the lite-restore and force it to
+			 * reload the RING_TAIL. Otherwise, the HW has a
+			 * tendency to ignore us rewinding the TAIL to the
+			 * end of an earlier request.
+			 */
+			last->hw_context->lrc_desc |= CTX_DESC_FORCE_RESTORE;
+			last = NULL;
+		} else {
+			/*
+			 * Otherwise if we already have a request pending
+			 * for execution after the current one, we can
+			 * just wait until the next CS event before
+			 * queuing more. In either case we will force a
+			 * lite-restore preemption event, but if we wait
+			 * we hopefully coalesce several updates into a single
+			 * submission.
+			 */
+			if (!list_is_last(&last->sched.link,
+					  &engine->active.requests))
+				return;
+
+			/*
+			 * WaIdleLiteRestore:bdw,skl
+			 * Apply the wa NOOPs to prevent
+			 * ring:HEAD == rq:TAIL as we resubmit the
+			 * request. See gen8_emit_fini_breadcrumb() for
+			 * where we prepare the padding after the
+			 * end of the request.
+			 */
+			last->tail = last->wa_tail;
+		}
 	}
 
 	while (rb) { /* XXX virtual is always taking precedence */
@@ -955,9 +959,24 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 				continue;
 			}
 
+			if (i915_request_completed(rq)) {
+				ve->request = NULL;
+				ve->base.execlists.queue_priority_hint = INT_MIN;
+				rb_erase_cached(rb, &execlists->virtual);
+				RB_CLEAR_NODE(rb);
+
+				rq->engine = engine;
+				__i915_request_submit(rq);
+
+				spin_unlock(&ve->base.active.lock);
+
+				rb = rb_first_cached(&execlists->virtual);
+				continue;
+			}
+
 			if (last && !can_merge_rq(last, rq)) {
 				spin_unlock(&ve->base.active.lock);
-				return; /* leave this rq for another engine */
+				return; /* leave this for another */
 			}
 
 			GEM_TRACE("%s: virtual rq=%llx:%lld%s, new engine? %s\n",
@@ -1006,9 +1025,10 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 			}
 
 			__i915_request_submit(rq);
-			trace_i915_request_in(rq, port_index(port, execlists));
-			submit = true;
-			last = rq;
+			if (!i915_request_completed(rq)) {
+				submit = true;
+				last = rq;
+			}
 		}
 
 		spin_unlock(&ve->base.active.lock);
@@ -1021,6 +1041,9 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 		int i;
 
 		priolist_for_each_request_consume(rq, rn, p, i) {
+			if (i915_request_completed(rq))
+				goto skip;
+
 			/*
 			 * Can we combine this request with the current port?
 			 * It has to be the same context/ringbuffer and not
@@ -1060,19 +1083,14 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 				    ctx_single_port_submission(rq->hw_context))
 					goto done;
 
-
-				if (submit)
-					port_assign(port, last);
+				*port = execlists_schedule_in(last, port - execlists->pending);
 				port++;
-
-				GEM_BUG_ON(port_isset(port));
 			}
 
-			__i915_request_submit(rq);
-			trace_i915_request_in(rq, port_index(port, execlists));
-
 			last = rq;
 			submit = true;
+skip:
+			__i915_request_submit(rq);
 		}
 
 		rb_erase_cached(&p->node, &execlists->queue);
@@ -1097,54 +1115,30 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 	 * interrupt for secondary ports).
 	 */
 	execlists->queue_priority_hint = queue_prio(execlists);
+	GEM_TRACE("%s: queue_priority_hint:%d, submit:%s\n",
+		  engine->name, execlists->queue_priority_hint,
+		  yesno(submit));
 
 	if (submit) {
-		port_assign(port, last);
+		*port = execlists_schedule_in(last, port - execlists->pending);
+		memset(port + 1, 0, (last_port - port) * sizeof(*port));
 		execlists_submit_ports(engine);
 	}
-
-	/* We must always keep the beast fed if we have work piled up */
-	GEM_BUG_ON(rb_first_cached(&execlists->queue) &&
-		   !port_isset(execlists->port));
-
-	/* Re-evaluate the executing context setup after each preemptive kick */
-	if (last)
-		execlists_user_begin(execlists, execlists->port);
-
-	/* If the engine is now idle, so should be the flag; and vice versa. */
-	GEM_BUG_ON(execlists_is_active(&engine->execlists,
-				       EXECLISTS_ACTIVE_USER) ==
-		   !port_isset(engine->execlists.port));
 }
 
 void
 execlists_cancel_port_requests(struct intel_engine_execlists * const execlists)
 {
-	struct execlist_port *port = execlists->port;
-	unsigned int num_ports = execlists_num_ports(execlists);
-
-	while (num_ports-- && port_isset(port)) {
-		struct i915_request *rq = port_request(port);
-
-		GEM_TRACE("%s:port%u fence %llx:%lld, (current %d)\n",
-			  rq->engine->name,
-			  (unsigned int)(port - execlists->port),
-			  rq->fence.context, rq->fence.seqno,
-			  hwsp_seqno(rq));
+	struct i915_request * const *port, *rq;
 
-		GEM_BUG_ON(!execlists->active);
-		execlists_context_schedule_out(rq,
-					       i915_request_completed(rq) ?
-					       INTEL_CONTEXT_SCHEDULE_OUT :
-					       INTEL_CONTEXT_SCHEDULE_PREEMPTED);
+	for (port = execlists->pending; (rq = *port); port++)
+		execlists_schedule_out(rq);
+	memset(execlists->pending, 0, sizeof(execlists->pending));
 
-		i915_request_put(rq);
-
-		memset(port, 0, sizeof(*port));
-		port++;
-	}
-
-	execlists_clear_all_active(execlists);
+	for (port = execlists->active; (rq = *port); port++)
+		execlists_schedule_out(rq);
+	execlists->active =
+		memset(execlists->inflight, 0, sizeof(execlists->inflight));
 }
 
 static inline void
@@ -1163,7 +1157,6 @@ reset_in_progress(const struct intel_engine_execlists *execlists)
 static void process_csb(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
 	const u32 * const buf = execlists->csb_status;
 	const u8 num_entries = execlists->csb_size;
 	u8 head, tail;
@@ -1198,9 +1191,7 @@ static void process_csb(struct intel_engine_cs *engine)
 	rmb();
 
 	do {
-		struct i915_request *rq;
 		unsigned int status;
-		unsigned int count;
 
 		if (++head == num_entries)
 			head = 0;
@@ -1223,68 +1214,37 @@ static void process_csb(struct intel_engine_cs *engine)
 		 * status notifier.
 		 */
 
-		GEM_TRACE("%s csb[%d]: status=0x%08x:0x%08x, active=0x%x\n",
+		GEM_TRACE("%s csb[%d]: status=0x%08x:0x%08x\n",
 			  engine->name, head,
-			  buf[2 * head + 0], buf[2 * head + 1],
-			  execlists->active);
+			  buf[2 * head + 0], buf[2 * head + 1]);
 
 		status = buf[2 * head];
-		if (status & (GEN8_CTX_STATUS_IDLE_ACTIVE |
-			      GEN8_CTX_STATUS_PREEMPTED))
-			execlists_set_active(execlists,
-					     EXECLISTS_ACTIVE_HWACK);
-		if (status & GEN8_CTX_STATUS_ACTIVE_IDLE)
-			execlists_clear_active(execlists,
-					       EXECLISTS_ACTIVE_HWACK);
-
-		if (!(status & GEN8_CTX_STATUS_COMPLETED_MASK))
-			continue;
-
-		/* We should never get a COMPLETED | IDLE_ACTIVE! */
-		GEM_BUG_ON(status & GEN8_CTX_STATUS_IDLE_ACTIVE);
+		if (status & GEN8_CTX_STATUS_IDLE_ACTIVE) {
+promote:
+			GEM_BUG_ON(!assert_pending_valid(execlists, "promote"));
+			execlists->active =
+				memcpy(execlists->inflight,
+				       execlists->pending,
+				       execlists_num_ports(execlists) *
+				       sizeof(*execlists->pending));
+			execlists->pending[0] = NULL;
 
-		if (status & GEN8_CTX_STATUS_COMPLETE &&
-		    buf[2*head + 1] == execlists->preempt_complete_status) {
-			GEM_TRACE("%s preempt-idle\n", engine->name);
-			complete_preempt_context(execlists);
-			continue;
-		}
+			if (!inject_preempt_hang(execlists))
+				ring_pause(engine) = 0;
+		} else if (status & GEN8_CTX_STATUS_PREEMPTED) {
+			struct i915_request * const *port = execlists->active;
 
-		if (status & GEN8_CTX_STATUS_PREEMPTED &&
-		    execlists_is_active(execlists,
-					EXECLISTS_ACTIVE_PREEMPT))
-			continue;
+			trace_ports(execlists, "preempted", execlists->active);
 
-		GEM_BUG_ON(!execlists_is_active(execlists,
-						EXECLISTS_ACTIVE_USER));
+			while (*port)
+				execlists_schedule_out(*port++);
 
-		rq = port_unpack(port, &count);
-		GEM_TRACE("%s out[0]: ctx=%d.%d, fence %llx:%lld (current %d), prio=%d\n",
-			  engine->name,
-			  port->context_id, count,
-			  rq ? rq->fence.context : 0,
-			  rq ? rq->fence.seqno : 0,
-			  rq ? hwsp_seqno(rq) : 0,
-			  rq ? rq_prio(rq) : 0);
+			goto promote;
+		} else if (*execlists->active) {
+			struct i915_request *rq = *execlists->active++;
 
-		/* Check the context/desc id for this event matches */
-		GEM_DEBUG_BUG_ON(buf[2 * head + 1] != port->context_id);
-
-		GEM_BUG_ON(count == 0);
-		if (--count == 0) {
-			/*
-			 * On the final event corresponding to the
-			 * submission of this context, we expect either
-			 * an element-switch event or a completion
-			 * event (and on completion, the active-idle
-			 * marker). No more preemptions, lite-restore
-			 * or otherwise.
-			 */
-			GEM_BUG_ON(status & GEN8_CTX_STATUS_PREEMPTED);
-			GEM_BUG_ON(port_isset(&port[1]) &&
-				   !(status & GEN8_CTX_STATUS_ELEMENT_SWITCH));
-			GEM_BUG_ON(!port_isset(&port[1]) &&
-				   !(status & GEN8_CTX_STATUS_ACTIVE_IDLE));
+			trace_ports(execlists, "completed",
+				    execlists->active - 1);
 
 			/*
 			 * We rely on the hardware being strongly
@@ -1293,21 +1253,10 @@ static void process_csb(struct intel_engine_cs *engine)
 			 * user interrupt and CSB is processed.
 			 */
 			GEM_BUG_ON(!i915_request_completed(rq));
+			execlists_schedule_out(rq);
 
-			execlists_context_schedule_out(rq,
-						       INTEL_CONTEXT_SCHEDULE_OUT);
-			i915_request_put(rq);
-
-			GEM_TRACE("%s completed ctx=%d\n",
-				  engine->name, port->context_id);
-
-			port = execlists_port_complete(execlists, port);
-			if (port_isset(port))
-				execlists_user_begin(execlists, port);
-			else
-				execlists_user_end(execlists);
-		} else {
-			port_set(port, port_pack(rq, count));
+			GEM_BUG_ON(execlists->active - execlists->inflight >
+				   execlists_num_ports(execlists));
 		}
 	} while (head != tail);
 
@@ -1332,7 +1281,7 @@ static void __execlists_submission_tasklet(struct intel_engine_cs *const engine)
 	lockdep_assert_held(&engine->active.lock);
 
 	process_csb(engine);
-	if (!execlists_is_active(&engine->execlists, EXECLISTS_ACTIVE_PREEMPT))
+	if (!engine->execlists.pending[0])
 		execlists_dequeue(engine);
 }
 
@@ -1345,11 +1294,6 @@ static void execlists_submission_tasklet(unsigned long data)
 	struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
 	unsigned long flags;
 
-	GEM_TRACE("%s awake?=%d, active=%x\n",
-		  engine->name,
-		  !!intel_wakeref_active(&engine->wakeref),
-		  engine->execlists.active);
-
 	spin_lock_irqsave(&engine->active.lock, flags);
 	__execlists_submission_tasklet(engine);
 	spin_unlock_irqrestore(&engine->active.lock, flags);
@@ -1376,12 +1320,16 @@ static void __submit_queue_imm(struct intel_engine_cs *engine)
 		tasklet_hi_schedule(&execlists->tasklet);
 }
 
-static void submit_queue(struct intel_engine_cs *engine, int prio)
+static void submit_queue(struct intel_engine_cs *engine,
+			 const struct i915_request *rq)
 {
-	if (prio > engine->execlists.queue_priority_hint) {
-		engine->execlists.queue_priority_hint = prio;
-		__submit_queue_imm(engine);
-	}
+	struct intel_engine_execlists *execlists = &engine->execlists;
+
+	if (rq_prio(rq) <= execlists->queue_priority_hint)
+		return;
+
+	execlists->queue_priority_hint = rq_prio(rq);
+	__submit_queue_imm(engine);
 }
 
 static void execlists_submit_request(struct i915_request *request)
@@ -1397,7 +1345,7 @@ static void execlists_submit_request(struct i915_request *request)
 	GEM_BUG_ON(RB_EMPTY_ROOT(&engine->execlists.queue.rb_root));
 	GEM_BUG_ON(list_empty(&request->sched.link));
 
-	submit_queue(engine, rq_prio(request));
+	submit_queue(engine, request);
 
 	spin_unlock_irqrestore(&engine->active.lock, flags);
 }
@@ -2048,27 +1996,13 @@ static void execlists_reset_prepare(struct intel_engine_cs *engine)
 	spin_unlock_irqrestore(&engine->active.lock, flags);
 }
 
-static bool lrc_regs_ok(const struct i915_request *rq)
-{
-	const struct intel_ring *ring = rq->ring;
-	const u32 *regs = rq->hw_context->lrc_reg_state;
-
-	/* Quick spot check for the common signs of context corruption */
-
-	if (regs[CTX_RING_BUFFER_CONTROL + 1] !=
-	    (RING_CTL_SIZE(ring->size) | RING_VALID))
-		return false;
-
-	if (regs[CTX_RING_BUFFER_START + 1] != i915_ggtt_offset(ring->vma))
-		return false;
-
-	return true;
-}
-
-static void reset_csb_pointers(struct intel_engine_execlists *execlists)
+static void reset_csb_pointers(struct intel_engine_cs *engine)
 {
+	struct intel_engine_execlists * const execlists = &engine->execlists;
 	const unsigned int reset_value = execlists->csb_size - 1;
 
+	ring_pause(engine) = 0;
+
 	/*
 	 * After a reset, the HW starts writing into CSB entry [0]. We
 	 * therefore have to set our HEAD pointer back one entry so that
@@ -2115,18 +2049,21 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
 	process_csb(engine); /* drain preemption events */
 
 	/* Following the reset, we need to reload the CSB read/write pointers */
-	reset_csb_pointers(&engine->execlists);
+	reset_csb_pointers(engine);
 
 	/*
 	 * Save the currently executing context, even if we completed
 	 * its request, it was still running at the time of the
 	 * reset and will have been clobbered.
 	 */
-	if (!port_isset(execlists->port))
-		goto out_clear;
+	rq = execlists_active(execlists);
+	if (!rq)
+		return;
 
-	rq = port_request(execlists->port);
 	ce = rq->hw_context;
+	GEM_BUG_ON(i915_active_is_idle(&ce->active));
+	GEM_BUG_ON(!i915_vma_is_pinned(ce->state));
+	rq = active_request(rq);
 
 	/*
 	 * Catch up with any missed context-switch interrupts.
@@ -2139,9 +2076,12 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
 	 */
 	execlists_cancel_port_requests(execlists);
 
-	rq = active_request(rq);
-	if (!rq)
+	if (!rq) {
+		ce->ring->head = ce->ring->tail;
 		goto out_replay;
+	}
+
+	ce->ring->head = intel_ring_wrap(ce->ring, rq->head);
 
 	/*
 	 * If this request hasn't started yet, e.g. it is waiting on a
@@ -2155,7 +2095,7 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
 	 * Otherwise, if we have not started yet, the request should replay
 	 * perfectly and we do not need to flag the result as being erroneous.
 	 */
-	if (!i915_request_started(rq) && lrc_regs_ok(rq))
+	if (!i915_request_started(rq))
 		goto out_replay;
 
 	/*
@@ -2170,7 +2110,7 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
 	 * image back to the expected values to skip over the guilty request.
 	 */
 	i915_reset_request(rq, stalled);
-	if (!stalled && lrc_regs_ok(rq))
+	if (!stalled)
 		goto out_replay;
 
 	/*
@@ -2190,17 +2130,13 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
 	execlists_init_reg_state(regs, ce, engine, ce->ring);
 
 out_replay:
-	/* Rerun the request; its payload has been neutered (if guilty). */
-	ce->ring->head =
-		rq ? intel_ring_wrap(ce->ring, rq->head) : ce->ring->tail;
+	GEM_TRACE("%s replay {head:%04x, tail:%04x\n",
+		  engine->name, ce->ring->head, ce->ring->tail);
 	intel_ring_update_space(ce->ring);
 	__execlists_update_reg_state(ce, engine);
 
 	/* Push back any incomplete requests for replay after the reset. */
 	__unwind_incomplete_requests(engine);
-
-out_clear:
-	execlists_clear_all_active(execlists);
 }
 
 static void execlists_reset(struct intel_engine_cs *engine, bool stalled)
@@ -2296,7 +2232,6 @@ static void execlists_cancel_requests(struct intel_engine_cs *engine)
 
 	execlists->queue_priority_hint = INT_MIN;
 	execlists->queue = RB_ROOT_CACHED;
-	GEM_BUG_ON(port_isset(execlists->port));
 
 	GEM_BUG_ON(__tasklet_is_enabled(&execlists->tasklet));
 	execlists->tasklet.func = nop_submission_tasklet;
@@ -2514,15 +2449,29 @@ static u32 *gen8_emit_wa_tail(struct i915_request *request, u32 *cs)
 	return cs;
 }
 
+static u32 *emit_preempt_busywait(struct i915_request *request, u32 *cs)
+{
+	*cs++ = MI_SEMAPHORE_WAIT |
+		MI_SEMAPHORE_GLOBAL_GTT |
+		MI_SEMAPHORE_POLL |
+		MI_SEMAPHORE_SAD_EQ_SDD;
+	*cs++ = 0;
+	*cs++ = intel_hws_preempt_address(request->engine);
+	*cs++ = 0;
+
+	return cs;
+}
+
 static u32 *gen8_emit_fini_breadcrumb(struct i915_request *request, u32 *cs)
 {
 	cs = gen8_emit_ggtt_write(cs,
 				  request->fence.seqno,
 				  request->timeline->hwsp_offset,
 				  0);
-
 	*cs++ = MI_USER_INTERRUPT;
+
 	*cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
+	cs = emit_preempt_busywait(request, cs);
 
 	request->tail = intel_ring_offset(request, cs);
 	assert_ring_tail_valid(request->ring, request->tail);
@@ -2543,9 +2492,10 @@ static u32 *gen8_emit_fini_breadcrumb_rcs(struct i915_request *request, u32 *cs)
 				    PIPE_CONTROL_FLUSH_ENABLE |
 				    PIPE_CONTROL_CS_STALL,
 				    0);
-
 	*cs++ = MI_USER_INTERRUPT;
+
 	*cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
+	cs = emit_preempt_busywait(request, cs);
 
 	request->tail = intel_ring_offset(request, cs);
 	assert_ring_tail_valid(request->ring, request->tail);
@@ -2594,8 +2544,7 @@ void intel_execlists_set_default_submission(struct intel_engine_cs *engine)
 	engine->flags |= I915_ENGINE_SUPPORTS_STATS;
 	if (!intel_vgpu_active(engine->i915))
 		engine->flags |= I915_ENGINE_HAS_SEMAPHORES;
-	if (engine->preempt_context &&
-	    HAS_LOGICAL_RING_PREEMPTION(engine->i915))
+	if (HAS_LOGICAL_RING_PREEMPTION(engine->i915))
 		engine->flags |= I915_ENGINE_HAS_PREEMPTION;
 }
 
@@ -2718,11 +2667,6 @@ int intel_execlists_submission_init(struct intel_engine_cs *engine)
 			i915_mmio_reg_offset(RING_ELSP(base));
 	}
 
-	execlists->preempt_complete_status = ~0u;
-	if (engine->preempt_context)
-		execlists->preempt_complete_status =
-			upper_32_bits(engine->preempt_context->lrc_desc);
-
 	execlists->csb_status =
 		&engine->status_page.addr[I915_HWS_CSB_BUF0_INDEX];
 
@@ -2734,7 +2678,7 @@ int intel_execlists_submission_init(struct intel_engine_cs *engine)
 	else
 		execlists->csb_size = GEN11_CSB_ENTRIES;
 
-	reset_csb_pointers(execlists);
+	reset_csb_pointers(engine);
 
 	return 0;
 }
@@ -2917,11 +2861,6 @@ populate_lr_context(struct intel_context *ce,
 	if (!engine->default_state)
 		regs[CTX_CONTEXT_CONTROL + 1] |=
 			_MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT);
-	if (ce->gem_context == engine->i915->preempt_context &&
-	    INTEL_GEN(engine->i915) < 11)
-		regs[CTX_CONTEXT_CONTROL + 1] |=
-			_MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT |
-					   CTX_CTRL_ENGINE_CTX_SAVE_INHIBIT);
 
 	ret = 0;
 err_unpin_ctx:
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index b7e9fddef270..a497cf7acb6a 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -1248,10 +1248,10 @@ static void error_record_engine_registers(struct i915_gpu_state *error,
 	}
 }
 
-static void record_request(struct i915_request *request,
+static void record_request(const struct i915_request *request,
 			   struct drm_i915_error_request *erq)
 {
-	struct i915_gem_context *ctx = request->gem_context;
+	const struct i915_gem_context *ctx = request->gem_context;
 
 	erq->flags = request->fence.flags;
 	erq->context = request->fence.context;
@@ -1315,20 +1315,15 @@ static void engine_record_requests(struct intel_engine_cs *engine,
 	ee->num_requests = count;
 }
 
-static void error_record_engine_execlists(struct intel_engine_cs *engine,
+static void error_record_engine_execlists(const struct intel_engine_cs *engine,
 					  struct drm_i915_error_engine *ee)
 {
 	const struct intel_engine_execlists * const execlists = &engine->execlists;
-	unsigned int n;
+	struct i915_request * const *port = execlists->active;
+	unsigned int n = 0;
 
-	for (n = 0; n < execlists_num_ports(execlists); n++) {
-		struct i915_request *rq = port_request(&execlists->port[n]);
-
-		if (!rq)
-			break;
-
-		record_request(rq, &ee->execlist[n]);
-	}
+	while (*port)
+		record_request(*port++, &ee->execlist[n++]);
 
 	ee->num_ports = n;
 }
diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
index 7083e6ab92c5..0c99694faab7 100644
--- a/drivers/gpu/drm/i915/i915_request.c
+++ b/drivers/gpu/drm/i915/i915_request.c
@@ -276,6 +276,12 @@ static bool i915_request_retire(struct i915_request *rq)
 
 	local_irq_disable();
 
+	/*
+	 * We only loosely track inflight requests across preemption,
+	 * and so we may find ourselves attempting to retire a _completed_
+	 * request that we have removed from the HW and put back on a run
+	 * queue.
+	 */
 	spin_lock(&rq->engine->active.lock);
 	list_del(&rq->sched.link);
 	spin_unlock(&rq->engine->active.lock);
diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
index edbbdfec24ab..bebc1e9b4a5e 100644
--- a/drivers/gpu/drm/i915/i915_request.h
+++ b/drivers/gpu/drm/i915/i915_request.h
@@ -28,6 +28,7 @@
 #include <linux/dma-fence.h>
 #include <linux/lockdep.h>
 
+#include "gt/intel_context_types.h"
 #include "gt/intel_engine_types.h"
 
 #include "i915_gem.h"
diff --git a/drivers/gpu/drm/i915/i915_scheduler.c b/drivers/gpu/drm/i915/i915_scheduler.c
index 2e9b38bdc33c..b1ba3e65cd52 100644
--- a/drivers/gpu/drm/i915/i915_scheduler.c
+++ b/drivers/gpu/drm/i915/i915_scheduler.c
@@ -179,8 +179,7 @@ static inline int rq_prio(const struct i915_request *rq)
 
 static void kick_submission(struct intel_engine_cs *engine, int prio)
 {
-	const struct i915_request *inflight =
-		port_request(engine->execlists.port);
+	const struct i915_request *inflight = *engine->execlists.active;
 
 	/*
 	 * If we are already the currently executing context, don't
diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
index 2987219a6300..4920ff9aba62 100644
--- a/drivers/gpu/drm/i915/i915_utils.h
+++ b/drivers/gpu/drm/i915/i915_utils.h
@@ -131,6 +131,18 @@ __check_struct_size(size_t base, size_t arr, size_t count, size_t *size)
 	((typeof(ptr))((unsigned long)(ptr) | __bits));			\
 })
 
+#define ptr_count_dec(p_ptr) do {					\
+	typeof(p_ptr) __p = (p_ptr);					\
+	unsigned long __v = (unsigned long)(*__p);			\
+	*__p = (typeof(*p_ptr))(--__v);					\
+} while (0)
+
+#define ptr_count_inc(p_ptr) do {					\
+	typeof(p_ptr) __p = (p_ptr);					\
+	unsigned long __v = (unsigned long)(*__p);			\
+	*__p = (typeof(*p_ptr))(++__v);					\
+} while (0)
+
 #define page_mask_bits(ptr) ptr_mask_bits(ptr, PAGE_SHIFT)
 #define page_unmask_bits(ptr) ptr_unmask_bits(ptr, PAGE_SHIFT)
 #define page_pack_bits(ptr, bits) ptr_pack_bits(ptr, bits, PAGE_SHIFT)
diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c
index db531ebc7704..12c22359fdac 100644
--- a/drivers/gpu/drm/i915/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/intel_guc_submission.c
@@ -32,7 +32,11 @@
 #include "intel_guc_submission.h"
 #include "i915_drv.h"
 
-#define GUC_PREEMPT_FINISHED		0x1
+enum {
+	GUC_PREEMPT_NONE = 0,
+	GUC_PREEMPT_INPROGRESS,
+	GUC_PREEMPT_FINISHED,
+};
 #define GUC_PREEMPT_BREADCRUMB_DWORDS	0x8
 #define GUC_PREEMPT_BREADCRUMB_BYTES	\
 	(sizeof(u32) * GUC_PREEMPT_BREADCRUMB_DWORDS)
@@ -537,15 +541,11 @@ static void guc_add_request(struct intel_guc *guc, struct i915_request *rq)
 	u32 ctx_desc = lower_32_bits(rq->hw_context->lrc_desc);
 	u32 ring_tail = intel_ring_set_tail(rq->ring, rq->tail) / sizeof(u64);
 
-	spin_lock(&client->wq_lock);
-
 	guc_wq_item_append(client, engine->guc_id, ctx_desc,
 			   ring_tail, rq->fence.seqno);
 	guc_ring_doorbell(client);
 
 	client->submissions[engine->id] += 1;
-
-	spin_unlock(&client->wq_lock);
 }
 
 /*
@@ -631,8 +631,9 @@ static void inject_preempt_context(struct work_struct *work)
 	data[6] = intel_guc_ggtt_offset(guc, guc->shared_data);
 
 	if (WARN_ON(intel_guc_send(guc, data, ARRAY_SIZE(data)))) {
-		execlists_clear_active(&engine->execlists,
-				       EXECLISTS_ACTIVE_PREEMPT);
+		intel_write_status_page(engine,
+					I915_GEM_HWS_PREEMPT,
+					GUC_PREEMPT_NONE);
 		tasklet_schedule(&engine->execlists.tasklet);
 	}
 
@@ -672,8 +673,6 @@ static void complete_preempt_context(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists *execlists = &engine->execlists;
 
-	GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT));
-
 	if (inject_preempt_hang(execlists))
 		return;
 
@@ -681,89 +680,90 @@ static void complete_preempt_context(struct intel_engine_cs *engine)
 	execlists_unwind_incomplete_requests(execlists);
 
 	wait_for_guc_preempt_report(engine);
-	intel_write_status_page(engine, I915_GEM_HWS_PREEMPT, 0);
+	intel_write_status_page(engine, I915_GEM_HWS_PREEMPT, GUC_PREEMPT_NONE);
 }
 
-/**
- * guc_submit() - Submit commands through GuC
- * @engine: engine associated with the commands
- *
- * The only error here arises if the doorbell hardware isn't functioning
- * as expected, which really shouln't happen.
- */
-static void guc_submit(struct intel_engine_cs *engine)
+static void guc_submit(struct intel_engine_cs *engine,
+		       struct i915_request **out,
+		       struct i915_request **end)
 {
 	struct intel_guc *guc = &engine->i915->guc;
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
-	unsigned int n;
+	struct intel_guc_client *client = guc->execbuf_client;
 
-	for (n = 0; n < execlists_num_ports(execlists); n++) {
-		struct i915_request *rq;
-		unsigned int count;
+	spin_lock(&client->wq_lock);
 
-		rq = port_unpack(&port[n], &count);
-		if (rq && count == 0) {
-			port_set(&port[n], port_pack(rq, ++count));
+	do {
+		struct i915_request *rq = *out++;
 
-			flush_ggtt_writes(rq->ring->vma);
+		flush_ggtt_writes(rq->ring->vma);
+		guc_add_request(guc, rq);
+	} while (out != end);
 
-			guc_add_request(guc, rq);
-		}
-	}
+	spin_unlock(&client->wq_lock);
 }
 
-static void port_assign(struct execlist_port *port, struct i915_request *rq)
+static inline int rq_prio(const struct i915_request *rq)
 {
-	GEM_BUG_ON(port_isset(port));
-
-	port_set(port, i915_request_get(rq));
+	return rq->sched.attr.priority | __NO_PREEMPTION;
 }
 
-static inline int rq_prio(const struct i915_request *rq)
+static struct i915_request *schedule_in(struct i915_request *rq, int idx)
 {
-	return rq->sched.attr.priority;
+	trace_i915_request_in(rq, idx);
+
+	if (!rq->hw_context->inflight)
+		rq->hw_context->inflight = rq->engine;
+	intel_context_inflight_inc(rq->hw_context);
+
+	return i915_request_get(rq);
 }
 
-static inline int port_prio(const struct execlist_port *port)
+static void schedule_out(struct i915_request *rq)
 {
-	return rq_prio(port_request(port)) | __NO_PREEMPTION;
+	trace_i915_request_out(rq);
+
+	intel_context_inflight_dec(rq->hw_context);
+	if (!intel_context_inflight_count(rq->hw_context))
+		rq->hw_context->inflight = NULL;
+
+	i915_request_put(rq);
 }
 
-static bool __guc_dequeue(struct intel_engine_cs *engine)
+static void __guc_dequeue(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
-	struct i915_request *last = NULL;
-	const struct execlist_port * const last_port =
-		&execlists->port[execlists->port_mask];
+	struct i915_request **first = execlists->inflight;
+	struct i915_request ** const last_port = first + execlists->port_mask;
+	struct i915_request *last = first[0];
+	struct i915_request **port;
 	bool submit = false;
 	struct rb_node *rb;
 
 	lockdep_assert_held(&engine->active.lock);
 
-	if (port_isset(port)) {
+	if (last) {
 		if (intel_engine_has_preemption(engine)) {
 			struct guc_preempt_work *preempt_work =
 				&engine->i915->guc.preempt_work[engine->id];
 			int prio = execlists->queue_priority_hint;
 
-			if (i915_scheduler_need_preempt(prio,
-							port_prio(port))) {
-				execlists_set_active(execlists,
-						     EXECLISTS_ACTIVE_PREEMPT);
+			if (i915_scheduler_need_preempt(prio, rq_prio(last))) {
+				intel_write_status_page(engine,
+							I915_GEM_HWS_PREEMPT,
+							GUC_PREEMPT_INPROGRESS);
 				queue_work(engine->i915->guc.preempt_wq,
 					   &preempt_work->work);
-				return false;
+				return;
 			}
 		}
 
-		port++;
-		if (port_isset(port))
-			return false;
+		if (*++first)
+			return;
+
+		last = NULL;
 	}
-	GEM_BUG_ON(port_isset(port));
 
+	port = first;
 	while ((rb = rb_first_cached(&execlists->queue))) {
 		struct i915_priolist *p = to_priolist(rb);
 		struct i915_request *rq, *rn;
@@ -774,18 +774,15 @@ static bool __guc_dequeue(struct intel_engine_cs *engine)
 				if (port == last_port)
 					goto done;
 
-				if (submit)
-					port_assign(port, last);
+				*port = schedule_in(last,
+						    port - execlists->inflight);
 				port++;
 			}
 
 			list_del_init(&rq->sched.link);
-
 			__i915_request_submit(rq);
-			trace_i915_request_in(rq, port_index(port, execlists));
-
-			last = rq;
 			submit = true;
+			last = rq;
 		}
 
 		rb_erase_cached(&p->node, &execlists->queue);
@@ -794,58 +791,41 @@ static bool __guc_dequeue(struct intel_engine_cs *engine)
 done:
 	execlists->queue_priority_hint =
 		rb ? to_priolist(rb)->priority : INT_MIN;
-	if (submit)
-		port_assign(port, last);
-	if (last)
-		execlists_user_begin(execlists, execlists->port);
-
-	/* We must always keep the beast fed if we have work piled up */
-	GEM_BUG_ON(port_isset(execlists->port) &&
-		   !execlists_is_active(execlists, EXECLISTS_ACTIVE_USER));
-	GEM_BUG_ON(rb_first_cached(&execlists->queue) &&
-		   !port_isset(execlists->port));
-
-	return submit;
-}
-
-static void guc_dequeue(struct intel_engine_cs *engine)
-{
-	if (__guc_dequeue(engine))
-		guc_submit(engine);
+	if (submit) {
+		*port = schedule_in(last, port - execlists->inflight);
+		*++port = NULL;
+		guc_submit(engine, first, port);
+	}
+	execlists->active = execlists->inflight;
 }
 
 static void guc_submission_tasklet(unsigned long data)
 {
 	struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
 	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
-	struct i915_request *rq;
+	struct i915_request **port, *rq;
 	unsigned long flags;
 
 	spin_lock_irqsave(&engine->active.lock, flags);
 
-	rq = port_request(port);
-	while (rq && i915_request_completed(rq)) {
-		trace_i915_request_out(rq);
-		i915_request_put(rq);
+	for (port = execlists->inflight; (rq = *port); port++) {
+		if (!i915_request_completed(rq))
+			break;
 
-		port = execlists_port_complete(execlists, port);
-		if (port_isset(port)) {
-			execlists_user_begin(execlists, port);
-			rq = port_request(port);
-		} else {
-			execlists_user_end(execlists);
-			rq = NULL;
-		}
+		schedule_out(rq);
+	}
+	if (port != execlists->inflight) {
+		int idx = port - execlists->inflight;
+		int rem = ARRAY_SIZE(execlists->inflight) - idx;
+		memmove(execlists->inflight, port, rem * sizeof(*port));
 	}
 
-	if (execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT) &&
-	    intel_read_status_page(engine, I915_GEM_HWS_PREEMPT) ==
+	if (intel_read_status_page(engine, I915_GEM_HWS_PREEMPT) ==
 	    GUC_PREEMPT_FINISHED)
 		complete_preempt_context(engine);
 
-	if (!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT))
-		guc_dequeue(engine);
+	if (!intel_read_status_page(engine, I915_GEM_HWS_PREEMPT))
+		__guc_dequeue(engine);
 
 	spin_unlock_irqrestore(&engine->active.lock, flags);
 }
@@ -959,7 +939,6 @@ static void guc_cancel_requests(struct intel_engine_cs *engine)
 
 	execlists->queue_priority_hint = INT_MIN;
 	execlists->queue = RB_ROOT_CACHED;
-	GEM_BUG_ON(port_isset(execlists->port));
 
 	spin_unlock_irqrestore(&engine->active.lock, flags);
 }
@@ -1422,7 +1401,7 @@ int intel_guc_submission_enable(struct intel_guc *guc)
 	 * and it is guaranteed that it will remove the work item from the
 	 * queue before our request is completed.
 	 */
-	BUILD_BUG_ON(ARRAY_SIZE(engine->execlists.port) *
+	BUILD_BUG_ON(ARRAY_SIZE(engine->execlists.inflight) *
 		     sizeof(struct guc_wq_item) *
 		     I915_NUM_ENGINES > GUC_WQ_SIZE);
 
diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c
index 298bb7116c51..1a5b9e284ca9 100644
--- a/drivers/gpu/drm/i915/selftests/i915_request.c
+++ b/drivers/gpu/drm/i915/selftests/i915_request.c
@@ -366,13 +366,15 @@ static int __igt_breadcrumbs_smoketest(void *arg)
 
 		if (!wait_event_timeout(wait->wait,
 					i915_sw_fence_done(wait),
-					HZ / 2)) {
+					5 * HZ)) {
 			struct i915_request *rq = requests[count - 1];
 
-			pr_err("waiting for %d fences (last %llx:%lld) on %s timed out!\n",
-			       count,
+			pr_err("waiting for %d/%d fences (last %llx:%lld) on %s timed out!\n",
+			       atomic_read(&wait->pending), count,
 			       rq->fence.context, rq->fence.seqno,
 			       t->engine->name);
+			GEM_TRACE_DUMP();
+
 			i915_gem_set_wedged(t->engine->i915);
 			GEM_BUG_ON(!i915_request_completed(rq));
 			i915_sw_fence_wait(wait);
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 2/3] drm/i915/execlists: Minimalistic timeslicing
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
@ 2019-06-20  7:05 ` Chris Wilson
  2019-06-20 13:51   ` Mika Kuoppala
  2019-06-20  7:05 ` [PATCH 3/3] drm/i915/execlists: Force preemption Chris Wilson
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Chris Wilson @ 2019-06-20  7:05 UTC (permalink / raw)
  To: intel-gfx

If we have multiple contexts of equal priority pending execution,
activate a timer to demote the currently executing context in favour of
the next in the queue when that timeslice expires. This enforces
fairness between contexts (so long as they allow preemption -- forced
preemption, in the future, will kick those who do not obey) and allows
us to avoid userspace blocking forward progress with e.g. unbounded
MI_SEMAPHORE_WAIT.

For the starting point here, we use the jiffie as our timeslice so that
we should be reasonably efficient wrt frequent CPU wakeups.

Testcase: igt/gem_exec_scheduler/semaphore-resolve
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gt/intel_engine_types.h |   6 +
 drivers/gpu/drm/i915/gt/intel_lrc.c          | 111 +++++++++
 drivers/gpu/drm/i915/gt/selftest_lrc.c       | 223 +++++++++++++++++++
 drivers/gpu/drm/i915/i915_scheduler.c        |   1 +
 drivers/gpu/drm/i915/i915_scheduler_types.h  |   1 +
 5 files changed, 342 insertions(+)

diff --git a/drivers/gpu/drm/i915/gt/intel_engine_types.h b/drivers/gpu/drm/i915/gt/intel_engine_types.h
index 411b7a807b99..6591d6bd2692 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_types.h
+++ b/drivers/gpu/drm/i915/gt/intel_engine_types.h
@@ -12,6 +12,7 @@
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/llist.h>
+#include <linux/timer.h>
 #include <linux/types.h>
 
 #include "i915_gem.h"
@@ -149,6 +150,11 @@ struct intel_engine_execlists {
 	 */
 	struct tasklet_struct tasklet;
 
+	/**
+	 * @timer: kick the current context if its timeslice expires
+	 */
+	struct timer_list timer;
+
 	/**
 	 * @default_priolist: priority list for I915_PRIORITY_NORMAL
 	 */
diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
index dc077b536fa5..fca79adb4aa3 100644
--- a/drivers/gpu/drm/i915/gt/intel_lrc.c
+++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
@@ -255,6 +255,7 @@ static int effective_prio(const struct i915_request *rq)
 		prio |= I915_PRIORITY_NOSEMAPHORE;
 
 	/* Restrict mere WAIT boosts from triggering preemption */
+	BUILD_BUG_ON(__NO_PREEMPTION & ~I915_PRIORITY_MASK); /* only internal */
 	return prio | __NO_PREEMPTION;
 }
 
@@ -813,6 +814,81 @@ last_active(const struct intel_engine_execlists *execlists)
 	return *last;
 }
 
+static void
+defer_request(struct i915_request * const rq, struct list_head * const pl)
+{
+	struct i915_dependency *p;
+
+	/*
+	 * We want to move the interrupted request to the back of
+	 * the round-robin list (i.e. its priority level), but
+	 * in doing so, we must then move all requests that were in
+	 * flight and were waiting for the interrupted request to
+	 * be run after it again.
+	 */
+	list_move_tail(&rq->sched.link, pl);
+
+	list_for_each_entry(p, &rq->sched.waiters_list, wait_link) {
+		struct i915_request *w =
+			container_of(p->waiter, typeof(*w), sched);
+
+		/* Leave semaphores spinning on the other engines */
+		if (w->engine != rq->engine)
+			continue;
+
+		/* No waiter should start before the active request completed */
+		GEM_BUG_ON(i915_request_started(w));
+
+		GEM_BUG_ON(rq_prio(w) > rq_prio(rq));
+		if (rq_prio(w) < rq_prio(rq))
+			continue;
+
+		if (list_empty(&w->sched.link))
+			continue; /* Not yet submitted; unready */
+
+		/*
+		 * This should be very shallow as it is limited by the
+		 * number of requests that can fit in a ring (<64) and
+		 * the number of contexts that can be in flight on this
+		 * engine.
+		 */
+		defer_request(w, pl);
+	}
+}
+
+static void defer_active(struct intel_engine_cs *engine)
+{
+	struct i915_request *rq;
+
+	rq = __unwind_incomplete_requests(engine);
+	if (!rq)
+		return;
+
+	defer_request(rq, i915_sched_lookup_priolist(engine, rq_prio(rq)));
+}
+
+static bool
+need_timeslice(struct intel_engine_cs *engine, const struct i915_request *rq)
+{
+	int hint;
+
+	if (list_is_last(&rq->sched.link, &engine->active.requests))
+		return false;
+
+	hint = max(rq_prio(list_next_entry(rq, sched.link)),
+		   engine->execlists.queue_priority_hint);
+
+	return hint >= rq_prio(rq);
+}
+
+static bool
+enable_timeslice(struct intel_engine_cs *engine)
+{
+	struct i915_request *last = last_active(&engine->execlists);
+
+	return last && need_timeslice(engine, last);
+}
+
 static void execlists_dequeue(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists * const execlists = &engine->execlists;
@@ -906,6 +982,27 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 			 */
 			last->hw_context->lrc_desc |= CTX_DESC_FORCE_RESTORE;
 			last = NULL;
+		} else if (need_timeslice(engine, last) &&
+			   !timer_pending(&engine->execlists.timer)) {
+			GEM_TRACE("%s: expired last=%llx:%lld, prio=%d, hint=%d\n",
+				  engine->name,
+				  last->fence.context,
+				  last->fence.seqno,
+				  last->sched.attr.priority,
+				  execlists->queue_priority_hint);
+
+			ring_pause(engine) = 1;
+			defer_active(engine);
+
+			/*
+			 * Unlike for preemption, if we rewind and continue
+			 * executing the same context as previously active,
+			 * the order of execution will remain the same and
+			 * the tail will only advance. We do not need to
+			 * force a full context restore, as a lite-restore
+			 * is sufficient to resample the monotonic TAIL.
+			 */
+			last = NULL;
 		} else {
 			/*
 			 * Otherwise if we already have a request pending
@@ -1229,6 +1326,9 @@ static void process_csb(struct intel_engine_cs *engine)
 				       sizeof(*execlists->pending));
 			execlists->pending[0] = NULL;
 
+			if (enable_timeslice(engine))
+				mod_timer(&execlists->timer, jiffies + 1);
+
 			if (!inject_preempt_hang(execlists))
 				ring_pause(engine) = 0;
 		} else if (status & GEN8_CTX_STATUS_PREEMPTED) {
@@ -1299,6 +1399,15 @@ static void execlists_submission_tasklet(unsigned long data)
 	spin_unlock_irqrestore(&engine->active.lock, flags);
 }
 
+static void execlists_submission_timer(struct timer_list *timer)
+{
+	struct intel_engine_cs *engine =
+		from_timer(engine, timer, execlists.timer);
+
+	/* Kick the tasklet for some interrupt coalescing and reset handling */
+	tasklet_hi_schedule(&engine->execlists.tasklet);
+}
+
 static void queue_request(struct intel_engine_cs *engine,
 			  struct i915_sched_node *node,
 			  int prio)
@@ -2524,6 +2633,7 @@ static int gen8_init_rcs_context(struct i915_request *rq)
 
 static void execlists_park(struct intel_engine_cs *engine)
 {
+	del_timer_sync(&engine->execlists.timer);
 	intel_engine_park(engine);
 }
 
@@ -2621,6 +2731,7 @@ int intel_execlists_submission_setup(struct intel_engine_cs *engine)
 
 	tasklet_init(&engine->execlists.tasklet,
 		     execlists_submission_tasklet, (unsigned long)engine);
+	timer_setup(&engine->execlists.timer, execlists_submission_timer, 0);
 
 	logical_ring_default_vfuncs(engine);
 	logical_ring_default_irqs(engine);
diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c b/drivers/gpu/drm/i915/gt/selftest_lrc.c
index 401e8b539297..0c97f953e908 100644
--- a/drivers/gpu/drm/i915/gt/selftest_lrc.c
+++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c
@@ -79,6 +79,225 @@ static int live_sanitycheck(void *arg)
 	return err;
 }
 
+static int
+emit_semaphore_chain(struct i915_request *rq, struct i915_vma *vma, int idx)
+{
+	u32 *cs;
+
+	cs = intel_ring_begin(rq, 10);
+	if (IS_ERR(cs))
+		return PTR_ERR(cs);
+
+	*cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
+
+	*cs++ = MI_SEMAPHORE_WAIT |
+		MI_SEMAPHORE_GLOBAL_GTT |
+		MI_SEMAPHORE_POLL |
+		MI_SEMAPHORE_SAD_NEQ_SDD;
+	*cs++ = 0;
+	*cs++ = i915_ggtt_offset(vma) + 4 * idx;
+	*cs++ = 0;
+
+	if (idx > 0) {
+		*cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
+		*cs++ = i915_ggtt_offset(vma) + 4 * (idx - 1);
+		*cs++ = 0;
+		*cs++ = 1;
+	} else {
+		*cs++ = MI_NOOP;
+		*cs++ = MI_NOOP;
+		*cs++ = MI_NOOP;
+		*cs++ = MI_NOOP;
+	}
+
+	*cs++ = MI_ARB_ON_OFF | MI_ARB_DISABLE;
+
+	intel_ring_advance(rq, cs);
+	return 0;
+}
+
+static struct i915_request *
+semaphore_queue(struct intel_engine_cs *engine, struct i915_vma *vma, int idx)
+{
+	struct i915_gem_context *ctx;
+	struct i915_request *rq;
+	int err;
+
+	ctx = kernel_context(engine->i915);
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	rq = igt_request_alloc(ctx, engine);
+	if (IS_ERR(rq))
+		goto out_ctx;
+
+	err = emit_semaphore_chain(rq, vma, idx);
+	i915_request_add(rq);
+	if (err)
+		rq = ERR_PTR(err);
+
+out_ctx:
+	kernel_context_close(ctx);
+	return rq;
+}
+
+static int
+release_queue(struct intel_engine_cs *engine,
+	      struct i915_vma *vma,
+	      int idx)
+{
+	struct i915_sched_attr attr = {
+		.priority = I915_USER_PRIORITY(I915_PRIORITY_MAX),
+	};
+	struct i915_request *rq;
+	u32 *cs;
+
+	rq = i915_request_create(engine->kernel_context);
+	if (IS_ERR(rq))
+		return PTR_ERR(rq);
+
+	cs = intel_ring_begin(rq, 4);
+	if (IS_ERR(cs)) {
+		i915_request_add(rq);
+		return PTR_ERR(cs);
+	}
+
+	*cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
+	*cs++ = i915_ggtt_offset(vma) + 4 * (idx - 1);
+	*cs++ = 0;
+	*cs++ = 1;
+
+	intel_ring_advance(rq, cs);
+	i915_request_add(rq);
+
+	engine->schedule(rq, &attr);
+
+	return 0;
+}
+
+static int
+slice_semaphore_queue(struct intel_engine_cs *outer,
+		      struct i915_vma *vma,
+		      int count)
+{
+	struct intel_engine_cs *engine;
+	struct i915_request *head;
+	enum intel_engine_id id;
+	int err, i, n = 0;
+
+	head = semaphore_queue(outer, vma, n++);
+	if (IS_ERR(head))
+		return PTR_ERR(head);
+
+	i915_request_get(head);
+	for_each_engine(engine, outer->i915, id) {
+		for (i = 0; i < count; i++) {
+			struct i915_request *rq;
+
+			rq = semaphore_queue(engine, vma, n++);
+			if (IS_ERR(rq)) {
+				err = PTR_ERR(rq);
+				goto out;
+			}
+		}
+	}
+
+	err = release_queue(outer, vma, n);
+	if (err)
+		goto out;
+
+	if (i915_request_wait(head,
+			      I915_WAIT_LOCKED,
+			      2 * RUNTIME_INFO(outer->i915)->num_engines * (count + 2) * (count + 3)) < 0) {
+		pr_err("Failed to slice along semaphore chain of length (%d, %d)!\n",
+		       count, n);
+		GEM_TRACE_DUMP();
+		i915_gem_set_wedged(outer->i915);
+		err = -EIO;
+	}
+
+out:
+	i915_request_put(head);
+	return err;
+}
+
+static int live_timeslice_preempt(void *arg)
+{
+	struct drm_i915_private *i915 = arg;
+	struct drm_i915_gem_object *obj;
+	intel_wakeref_t wakeref;
+	struct i915_vma *vma;
+	void *vaddr;
+	int err = 0;
+	int count;
+
+	/*
+	 * If a request takes too long, we would like to give other users
+	 * a fair go on the GPU. In particular, users may create batches
+	 * that wait upon external input, where that input may even be
+	 * supplied by another GPU job. To avoid blocking forever, we
+	 * need to preempt the current task and replace it with another
+	 * ready task.
+	 */
+
+	mutex_lock(&i915->drm.struct_mutex);
+	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
+
+	obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
+	if (IS_ERR(obj)) {
+		err = PTR_ERR(obj);
+		goto err_unlock;
+	}
+
+	vma = i915_vma_instance(obj, &i915->ggtt.vm, NULL);
+	if (IS_ERR(vma)) {
+		err = PTR_ERR(vma);
+		goto err_obj;
+	}
+
+	vaddr = i915_gem_object_pin_map(obj, I915_MAP_WC);
+	if (IS_ERR(vaddr)) {
+		err = PTR_ERR(vaddr);
+		goto err_obj;
+	}
+
+	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
+	if (err)
+		goto err_map;
+
+	for_each_prime_number_from(count, 1, 16) {
+		struct intel_engine_cs *engine;
+		enum intel_engine_id id;
+
+		for_each_engine(engine, i915, id) {
+			memset(vaddr, 0, PAGE_SIZE);
+
+			err = slice_semaphore_queue(engine, vma, count);
+			if (err)
+				goto err_pin;
+
+			if (igt_flush_test(i915, I915_WAIT_LOCKED)) {
+				err = -EIO;
+				goto err_pin;
+			}
+		}
+	}
+
+err_pin:
+	i915_vma_unpin(vma);
+err_map:
+	i915_gem_object_unpin_map(obj);
+err_obj:
+	i915_gem_object_put(obj);
+err_unlock:
+	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+		err = -EIO;
+	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
+	mutex_unlock(&i915->drm.struct_mutex);
+
+	return err;
+}
+
 static int live_busywait_preempt(void *arg)
 {
 	struct drm_i915_private *i915 = arg;
@@ -398,6 +617,9 @@ static int live_late_preempt(void *arg)
 	if (!ctx_lo)
 		goto err_ctx_hi;
 
+	/* Make sure ctx_lo stays before ctx_hi until we trigger preemption. */
+	ctx_lo->sched.priority = I915_USER_PRIORITY(1);
+
 	for_each_engine(engine, i915, id) {
 		struct igt_live_test t;
 		struct i915_request *rq;
@@ -1812,6 +2034,7 @@ int intel_execlists_live_selftests(struct drm_i915_private *i915)
 {
 	static const struct i915_subtest tests[] = {
 		SUBTEST(live_sanitycheck),
+		SUBTEST(live_timeslice_preempt),
 		SUBTEST(live_busywait_preempt),
 		SUBTEST(live_preempt),
 		SUBTEST(live_late_preempt),
diff --git a/drivers/gpu/drm/i915/i915_scheduler.c b/drivers/gpu/drm/i915/i915_scheduler.c
index b1ba3e65cd52..0bd452e851d8 100644
--- a/drivers/gpu/drm/i915/i915_scheduler.c
+++ b/drivers/gpu/drm/i915/i915_scheduler.c
@@ -394,6 +394,7 @@ bool __i915_sched_node_add_dependency(struct i915_sched_node *node,
 		list_add(&dep->wait_link, &signal->waiters_list);
 		list_add(&dep->signal_link, &node->signalers_list);
 		dep->signaler = signal;
+		dep->waiter = node;
 		dep->flags = flags;
 
 		/* Keep track of whether anyone on this chain has a semaphore */
diff --git a/drivers/gpu/drm/i915/i915_scheduler_types.h b/drivers/gpu/drm/i915/i915_scheduler_types.h
index 3e309631bd0b..aad81acba9dc 100644
--- a/drivers/gpu/drm/i915/i915_scheduler_types.h
+++ b/drivers/gpu/drm/i915/i915_scheduler_types.h
@@ -62,6 +62,7 @@ struct i915_sched_node {
 
 struct i915_dependency {
 	struct i915_sched_node *signaler;
+	struct i915_sched_node *waiter;
 	struct list_head signal_link;
 	struct list_head wait_link;
 	struct list_head dfs_link;
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 3/3] drm/i915/execlists: Force preemption
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
  2019-06-20  7:05 ` [PATCH 2/3] drm/i915/execlists: Minimalistic timeslicing Chris Wilson
@ 2019-06-20  7:05 ` Chris Wilson
  2019-06-20 14:00   ` Mika Kuoppala
  2019-06-20  7:46 ` ✗ Fi.CI.CHECKPATCH: warning for series starting with [1/3] drm/i915/execlists: Preempt-to-busy Patchwork
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Chris Wilson @ 2019-06-20  7:05 UTC (permalink / raw)
  To: intel-gfx

If the preempted context takes too long to relinquish control, e.g. it
is stuck inside a shader with arbitration disabled, evict that context
with an engine reset. This ensures that preemptions are reasonably
responsive, providing a tighter QoS for the more important context at
the cost of flagging unresponsive contexts more frequently (i.e. instead
of using an ~10s hangcheck, we now evict at ~10ms).  The challenge of
lies in picking a timeout that can be reasonably serviced by HW for
typical workloads, balancing the existing clients against the needs for
responsiveness.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 drivers/gpu/drm/i915/Kconfig.profile | 12 ++++++
 drivers/gpu/drm/i915/gt/intel_lrc.c  | 56 ++++++++++++++++++++++++++--
 2 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/Kconfig.profile b/drivers/gpu/drm/i915/Kconfig.profile
index 48df8889a88a..8273d3baafe4 100644
--- a/drivers/gpu/drm/i915/Kconfig.profile
+++ b/drivers/gpu/drm/i915/Kconfig.profile
@@ -25,3 +25,15 @@ config DRM_I915_SPIN_REQUEST
 	  May be 0 to disable the initial spin. In practice, we estimate
 	  the cost of enabling the interrupt (if currently disabled) to be
 	  a few microseconds.
+
+config DRM_I915_PREEMPT_TIMEOUT
+	int "Preempt timeout (ms)"
+	default 10 # milliseconds
+	help
+	  How long to wait (in milliseconds) for a preemption event to occur
+	  when submitting a new context via execlists. If the current context
+	  does not hit an arbitration point and yield to HW before the timer
+	  expires, the HW will be reset to allow the more important context
+	  to execute.
+
+	  May be 0 to disable the timeout.
diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
index fca79adb4aa3..e8d7deba3e49 100644
--- a/drivers/gpu/drm/i915/gt/intel_lrc.c
+++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
@@ -889,6 +889,15 @@ enable_timeslice(struct intel_engine_cs *engine)
 	return last && need_timeslice(engine, last);
 }
 
+static unsigned long preempt_expires(void)
+{
+	unsigned long timeout =
+		msecs_to_jiffies_timeout(CONFIG_DRM_I915_PREEMPT_TIMEOUT);
+
+	barrier();
+	return jiffies + timeout;
+}
+
 static void execlists_dequeue(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists * const execlists = &engine->execlists;
@@ -1220,6 +1229,9 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 		*port = execlists_schedule_in(last, port - execlists->pending);
 		memset(port + 1, 0, (last_port - port) * sizeof(*port));
 		execlists_submit_ports(engine);
+
+		if (CONFIG_DRM_I915_PREEMPT_TIMEOUT)
+			mod_timer(&execlists->timer, preempt_expires());
 	}
 }
 
@@ -1376,13 +1388,48 @@ static void process_csb(struct intel_engine_cs *engine)
 	invalidate_csb_entries(&buf[0], &buf[num_entries - 1]);
 }
 
-static void __execlists_submission_tasklet(struct intel_engine_cs *const engine)
+static bool __execlists_submission_tasklet(struct intel_engine_cs *const engine)
 {
 	lockdep_assert_held(&engine->active.lock);
 
 	process_csb(engine);
-	if (!engine->execlists.pending[0])
+	if (!engine->execlists.pending[0]) {
 		execlists_dequeue(engine);
+		return true;
+	}
+
+	return false;
+}
+
+static void preempt_reset(struct intel_engine_cs *engine)
+{
+	const unsigned int bit = I915_RESET_ENGINE + engine->id;
+	unsigned long *lock = &engine->i915->gpu_error.flags;
+
+	if (test_and_set_bit(bit, lock))
+		return;
+
+	tasklet_disable_nosync(&engine->execlists.tasklet);
+	spin_unlock(&engine->active.lock);
+
+	i915_reset_engine(engine, "preemption time out");
+
+	spin_lock(&engine->active.lock);
+	tasklet_enable(&engine->execlists.tasklet);
+
+	clear_bit(bit, lock);
+	wake_up_bit(lock, bit);
+}
+
+static bool preempt_timeout(struct intel_engine_cs *const engine)
+{
+	if (!CONFIG_DRM_I915_PREEMPT_TIMEOUT)
+		return false;
+
+	if (!intel_engine_has_preemption(engine))
+		return false;
+
+	return !timer_pending(&engine->execlists.timer);
 }
 
 /*
@@ -1395,7 +1442,10 @@ static void execlists_submission_tasklet(unsigned long data)
 	unsigned long flags;
 
 	spin_lock_irqsave(&engine->active.lock, flags);
-	__execlists_submission_tasklet(engine);
+
+	if (!__execlists_submission_tasklet(engine) && preempt_timeout(engine))
+		preempt_reset(engine);
+
 	spin_unlock_irqrestore(&engine->active.lock, flags);
 }
 
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* ✗ Fi.CI.CHECKPATCH: warning for series starting with [1/3] drm/i915/execlists: Preempt-to-busy
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
  2019-06-20  7:05 ` [PATCH 2/3] drm/i915/execlists: Minimalistic timeslicing Chris Wilson
  2019-06-20  7:05 ` [PATCH 3/3] drm/i915/execlists: Force preemption Chris Wilson
@ 2019-06-20  7:46 ` Patchwork
  2019-06-20  7:48 ` ✗ Fi.CI.SPARSE: " Patchwork
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Patchwork @ 2019-06-20  7:46 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [1/3] drm/i915/execlists: Preempt-to-busy
URL   : https://patchwork.freedesktop.org/series/62431/
State : warning

== Summary ==

$ dim checkpatch origin/drm-tip
4fedb897cd15 drm/i915/execlists: Preempt-to-busy
-:1502: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'p_ptr' - possible side-effects?
#1502: FILE: drivers/gpu/drm/i915/i915_utils.h:134:
+#define ptr_count_dec(p_ptr) do {					\
+	typeof(p_ptr) __p = (p_ptr);					\
+	unsigned long __v = (unsigned long)(*__p);			\
+	*__p = (typeof(*p_ptr))(--__v);					\
+} while (0)

-:1508: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'p_ptr' - possible side-effects?
#1508: FILE: drivers/gpu/drm/i915/i915_utils.h:140:
+#define ptr_count_inc(p_ptr) do {					\
+	typeof(p_ptr) __p = (p_ptr);					\
+	unsigned long __v = (unsigned long)(*__p);			\
+	*__p = (typeof(*p_ptr))(++__v);					\
+} while (0)

-:1791: WARNING:LINE_SPACING: Missing a blank line after declarations
#1791: FILE: drivers/gpu/drm/i915/intel_guc_submission.c:820:
+		int rem = ARRAY_SIZE(execlists->inflight) - idx;
+		memmove(execlists->inflight, port, rem * sizeof(*port));

total: 0 errors, 1 warnings, 2 checks, 1690 lines checked
44f749f54824 drm/i915/execlists: Minimalistic timeslicing
-:345: WARNING:LONG_LINE: line over 100 characters
#345: FILE: drivers/gpu/drm/i915/gt/selftest_lrc.c:211:
+			      2 * RUNTIME_INFO(outer->i915)->num_engines * (count + 2) * (count + 3)) < 0) {

total: 0 errors, 1 warnings, 0 checks, 426 lines checked
9eafb424a83a drm/i915/execlists: Force preemption

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* ✗ Fi.CI.SPARSE: warning for series starting with [1/3] drm/i915/execlists: Preempt-to-busy
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
                   ` (2 preceding siblings ...)
  2019-06-20  7:46 ` ✗ Fi.CI.CHECKPATCH: warning for series starting with [1/3] drm/i915/execlists: Preempt-to-busy Patchwork
@ 2019-06-20  7:48 ` Patchwork
  2019-06-20  8:09 ` ✗ Fi.CI.BAT: failure " Patchwork
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Patchwork @ 2019-06-20  7:48 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [1/3] drm/i915/execlists: Preempt-to-busy
URL   : https://patchwork.freedesktop.org/series/62431/
State : warning

== Summary ==

$ dim sparse origin/drm-tip
Sparse version: v0.5.2
Commit: drm/i915/execlists: Preempt-to-busy
-drivers/gpu/drm/i915/selftests/../i915_utils.h:220:16: warning: expression using sizeof(void)
+drivers/gpu/drm/i915/selftests/../i915_utils.h:232:16: warning: expression using sizeof(void)

Commit: drm/i915/execlists: Minimalistic timeslicing
+drivers/gpu/drm/i915/gt/intel_lrc.c:878:16: warning: expression using sizeof(void)
+drivers/gpu/drm/i915/gt/intel_lrc.c:878:16: warning: expression using sizeof(void)

Commit: drm/i915/execlists: Force preemption
+
+drivers/gpu/drm/i915/i915_utils.h:232:16: warning: expression using sizeof(void)
+Error in reading or end of file.

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* ✗ Fi.CI.BAT: failure for series starting with [1/3] drm/i915/execlists: Preempt-to-busy
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
                   ` (3 preceding siblings ...)
  2019-06-20  7:48 ` ✗ Fi.CI.SPARSE: " Patchwork
@ 2019-06-20  8:09 ` Patchwork
  2019-06-20  8:13   ` Chris Wilson
  2019-06-20  8:24 ` [PATCH] " Chris Wilson
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Patchwork @ 2019-06-20  8:09 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [1/3] drm/i915/execlists: Preempt-to-busy
URL   : https://patchwork.freedesktop.org/series/62431/
State : failure

== Summary ==

CI Bug Log - changes from CI_DRM_6312 -> Patchwork_13359
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with Patchwork_13359 absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in Patchwork_13359, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in Patchwork_13359:

### IGT changes ###

#### Possible regressions ####

  * igt@i915_module_load@reload:
    - fi-kbl-r:           [PASS][1] -> [INCOMPLETE][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-kbl-r/igt@i915_module_load@reload.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-kbl-r/igt@i915_module_load@reload.html
    - fi-whl-u:           [PASS][3] -> [INCOMPLETE][4]
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-whl-u/igt@i915_module_load@reload.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-whl-u/igt@i915_module_load@reload.html
    - fi-skl-guc:         [PASS][5] -> [INCOMPLETE][6]
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-skl-guc/igt@i915_module_load@reload.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-skl-guc/igt@i915_module_load@reload.html

  * igt@i915_module_load@reload-no-display:
    - fi-kbl-guc:         [PASS][7] -> [INCOMPLETE][8]
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-kbl-guc/igt@i915_module_load@reload-no-display.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-kbl-guc/igt@i915_module_load@reload-no-display.html

  * igt@i915_selftest@live_contexts:
    - fi-bdw-5557u:       [PASS][9] -> [INCOMPLETE][10]
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-bdw-5557u/igt@i915_selftest@live_contexts.html
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-bdw-5557u/igt@i915_selftest@live_contexts.html
    - fi-bsw-kefka:       [PASS][11] -> [INCOMPLETE][12]
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-bsw-kefka/igt@i915_selftest@live_contexts.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-bsw-kefka/igt@i915_selftest@live_contexts.html
    - fi-kbl-x1275:       [PASS][13] -> [INCOMPLETE][14]
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-kbl-x1275/igt@i915_selftest@live_contexts.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-kbl-x1275/igt@i915_selftest@live_contexts.html
    - fi-skl-lmem:        [PASS][15] -> [INCOMPLETE][16]
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-skl-lmem/igt@i915_selftest@live_contexts.html
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-skl-lmem/igt@i915_selftest@live_contexts.html
    - fi-bsw-n3050:       [PASS][17] -> [INCOMPLETE][18]
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-bsw-n3050/igt@i915_selftest@live_contexts.html
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-bsw-n3050/igt@i915_selftest@live_contexts.html
    - fi-cfl-8109u:       [PASS][19] -> [INCOMPLETE][20]
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-cfl-8109u/igt@i915_selftest@live_contexts.html
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-cfl-8109u/igt@i915_selftest@live_contexts.html

  * igt@i915_selftest@live_timelines:
    - fi-skl-6700k2:      [PASS][21] -> [INCOMPLETE][22]
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-skl-6700k2/igt@i915_selftest@live_timelines.html
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-skl-6700k2/igt@i915_selftest@live_timelines.html

  * igt@i915_selftest@live_workarounds:
    - fi-kbl-8809g:       [PASS][23] -> [INCOMPLETE][24]
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-kbl-8809g/igt@i915_selftest@live_workarounds.html
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-kbl-8809g/igt@i915_selftest@live_workarounds.html

  
Known issues
------------

  Here are the changes found in Patchwork_13359 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_ctx_create@basic-files:
    - fi-icl-u2:          [PASS][25] -> [INCOMPLETE][26] ([fdo#107713] / [fdo#109100])
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-icl-u2/igt@gem_ctx_create@basic-files.html
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-icl-u2/igt@gem_ctx_create@basic-files.html
    - fi-icl-guc:         [PASS][27] -> [INCOMPLETE][28] ([fdo#107713] / [fdo#109100])
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-icl-guc/igt@gem_ctx_create@basic-files.html
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-icl-guc/igt@gem_ctx_create@basic-files.html

  * igt@gem_mmap_gtt@basic-write-cpu-read-gtt:
    - fi-icl-u3:          [PASS][29] -> [DMESG-WARN][30] ([fdo#107724])
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-icl-u3/igt@gem_mmap_gtt@basic-write-cpu-read-gtt.html
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-icl-u3/igt@gem_mmap_gtt@basic-write-cpu-read-gtt.html

  * igt@i915_selftest@live_contexts:
    - fi-apl-guc:         [PASS][31] -> [INCOMPLETE][32] ([fdo#103927])
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-apl-guc/igt@i915_selftest@live_contexts.html
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-apl-guc/igt@i915_selftest@live_contexts.html

  * igt@i915_selftest@live_hangcheck:
    - fi-skl-gvtdvm:      [PASS][33] -> [INCOMPLETE][34] ([fdo#108744])
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-skl-gvtdvm/igt@i915_selftest@live_hangcheck.html
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-skl-gvtdvm/igt@i915_selftest@live_hangcheck.html

  
#### Possible fixes ####

  * igt@gem_exec_suspend@basic-s3:
    - fi-blb-e6850:       [INCOMPLETE][35] ([fdo#107718]) -> [PASS][36]
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-blb-e6850/igt@gem_exec_suspend@basic-s3.html
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-blb-e6850/igt@gem_exec_suspend@basic-s3.html

  * igt@i915_selftest@live_contexts:
    - fi-skl-gvtdvm:      [DMESG-FAIL][37] ([fdo#110235]) -> [PASS][38]
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-skl-gvtdvm/igt@i915_selftest@live_contexts.html
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-skl-gvtdvm/igt@i915_selftest@live_contexts.html

  * igt@kms_frontbuffer_tracking@basic:
    - fi-icl-u3:          [FAIL][39] ([fdo#103167]) -> [PASS][40]
   [39]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-icl-u3/igt@kms_frontbuffer_tracking@basic.html
   [40]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-icl-u3/igt@kms_frontbuffer_tracking@basic.html

  
  [fdo#103167]: https://bugs.freedesktop.org/show_bug.cgi?id=103167
  [fdo#103927]: https://bugs.freedesktop.org/show_bug.cgi?id=103927
  [fdo#107713]: https://bugs.freedesktop.org/show_bug.cgi?id=107713
  [fdo#107718]: https://bugs.freedesktop.org/show_bug.cgi?id=107718
  [fdo#107724]: https://bugs.freedesktop.org/show_bug.cgi?id=107724
  [fdo#108744]: https://bugs.freedesktop.org/show_bug.cgi?id=108744
  [fdo#109100]: https://bugs.freedesktop.org/show_bug.cgi?id=109100
  [fdo#110235]: https://bugs.freedesktop.org/show_bug.cgi?id=110235


Participating hosts (49 -> 45)
------------------------------

  Additional (4): fi-cml-u2 fi-bxt-j4205 fi-gdg-551 fi-cml-u 
  Missing    (8): fi-kbl-soraka fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-bsw-cyan fi-icl-y fi-byt-clapper fi-bdw-samus 


Build changes
-------------

  * Linux: CI_DRM_6312 -> Patchwork_13359

  CI_DRM_6312: 034e3ac6a2d180d188da927388b60c7e62c5655b @ git://anongit.freedesktop.org/gfx-ci/linux
  IGT_5061: c88ced79a7b71aec58f1d9c5c599ac2f431bcf7a @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
  Patchwork_13359: 9eafb424a83ab4dab71f60593a9179347ec2827d @ git://anongit.freedesktop.org/gfx-ci/linux


== Linux commits ==

9eafb424a83a drm/i915/execlists: Force preemption
44f749f54824 drm/i915/execlists: Minimalistic timeslicing
4fedb897cd15 drm/i915/execlists: Preempt-to-busy

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: ✗ Fi.CI.BAT: failure for series starting with [1/3] drm/i915/execlists: Preempt-to-busy
  2019-06-20  8:09 ` ✗ Fi.CI.BAT: failure " Patchwork
@ 2019-06-20  8:13   ` Chris Wilson
  2019-06-20  8:15     ` Chris Wilson
  0 siblings, 1 reply; 23+ messages in thread
From: Chris Wilson @ 2019-06-20  8:13 UTC (permalink / raw)
  To: Patchwork; +Cc: intel-gfx

Quoting Patchwork (2019-06-20 09:09:33)
> == Series Details ==
> 
> Series: series starting with [1/3] drm/i915/execlists: Preempt-to-busy
> URL   : https://patchwork.freedesktop.org/series/62431/
> State : failure
> 
> == Summary ==
> 
> CI Bug Log - changes from CI_DRM_6312 -> Patchwork_13359
> ====================================================
> 
> Summary
> -------
> 
>   **FAILURE**
> 
>   Serious unknown changes coming with Patchwork_13359 absolutely need to be
>   verified manually.
>   
>   If you think the reported changes have nothing to do with the changes
>   introduced in Patchwork_13359, please notify your bug team to allow them
>   to document this new failure mode, which will reduce false positives in CI.
> 
>   External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/
> 
> Possible new issues
> -------------------
> 
>   Here are the unknown changes that may have been introduced in Patchwork_13359:
> 
> ### IGT changes ###
> 
> #### Possible regressions ####
> 
>   * igt@i915_module_load@reload:
>     - fi-kbl-r:           [PASS][1] -> [INCOMPLETE][2]
>    [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-kbl-r/igt@i915_module_load@reload.html
>    [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-kbl-r/igt@i915_module_load@reload.html
>     - fi-whl-u:           [PASS][3] -> [INCOMPLETE][4]
>    [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-whl-u/igt@i915_module_load@reload.html
>    [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-whl-u/igt@i915_module_load@reload.html
>     - fi-skl-guc:         [PASS][5] -> [INCOMPLETE][6]
>    [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-skl-guc/igt@i915_module_load@reload.html
>    [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-skl-guc/igt@i915_module_load@reload.html

Hmm, new asserts firing. More than likely a bug in the assert or a
latent issue. Still scary.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: ✗ Fi.CI.BAT: failure for series starting with [1/3] drm/i915/execlists: Preempt-to-busy
  2019-06-20  8:13   ` Chris Wilson
@ 2019-06-20  8:15     ` Chris Wilson
  0 siblings, 0 replies; 23+ messages in thread
From: Chris Wilson @ 2019-06-20  8:15 UTC (permalink / raw)
  To: Patchwork; +Cc: intel-gfx

Quoting Chris Wilson (2019-06-20 09:13:27)
> Quoting Patchwork (2019-06-20 09:09:33)
> > == Series Details ==
> > 
> > Series: series starting with [1/3] drm/i915/execlists: Preempt-to-busy
> > URL   : https://patchwork.freedesktop.org/series/62431/
> > State : failure
> > 
> > == Summary ==
> > 
> > CI Bug Log - changes from CI_DRM_6312 -> Patchwork_13359
> > ====================================================
> > 
> > Summary
> > -------
> > 
> >   **FAILURE**
> > 
> >   Serious unknown changes coming with Patchwork_13359 absolutely need to be
> >   verified manually.
> >   
> >   If you think the reported changes have nothing to do with the changes
> >   introduced in Patchwork_13359, please notify your bug team to allow them
> >   to document this new failure mode, which will reduce false positives in CI.
> > 
> >   External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/
> > 
> > Possible new issues
> > -------------------
> > 
> >   Here are the unknown changes that may have been introduced in Patchwork_13359:
> > 
> > ### IGT changes ###
> > 
> > #### Possible regressions ####
> > 
> >   * igt@i915_module_load@reload:
> >     - fi-kbl-r:           [PASS][1] -> [INCOMPLETE][2]
> >    [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-kbl-r/igt@i915_module_load@reload.html
> >    [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-kbl-r/igt@i915_module_load@reload.html
> >     - fi-whl-u:           [PASS][3] -> [INCOMPLETE][4]
> >    [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-whl-u/igt@i915_module_load@reload.html
> >    [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-whl-u/igt@i915_module_load@reload.html
> >     - fi-skl-guc:         [PASS][5] -> [INCOMPLETE][6]
> >    [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-skl-guc/igt@i915_module_load@reload.html
> >    [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13359/fi-skl-guc/igt@i915_module_load@reload.html
> 
> Hmm, new asserts firing. More than likely a bug in the assert or a
> latent issue. Still scary.

Nah, seems to be a slight misfire on the assert. It is asserting that
the engine is still alive as we process the final request made to put it
to asleep. Will need a new assert.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH] drm/i915/execlists: Preempt-to-busy
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
                   ` (4 preceding siblings ...)
  2019-06-20  8:09 ` ✗ Fi.CI.BAT: failure " Patchwork
@ 2019-06-20  8:24 ` Chris Wilson
  2019-06-20 12:41   ` Mika Kuoppala
  2019-06-20  9:23 ` ✗ Fi.CI.CHECKPATCH: warning for series starting with drm/i915/execlists: Preempt-to-busy (rev2) Patchwork
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Chris Wilson @ 2019-06-20  8:24 UTC (permalink / raw)
  To: intel-gfx

When using a global seqno, we required a precise stop-the-workd event to
handle preemption and unwind the global seqno counter. To accomplish
this, we would preempt to a special out-of-band context and wait for the
machine to report that it was idle. Given an idle machine, we could very
precisely see which requests had completed and which we needed to feed
back into the run queue.

However, now that we have scrapped the global seqno, we no longer need
to precisely unwind the global counter and only track requests by their
per-context seqno. This allows us to loosely unwind inflight requests
while scheduling a preemption, with the enormous caveat that the
requests we put back on the run queue are still _inflight_ (until the
preemption request is complete). This makes request tracking much more
messy, as at any point then we can see a completed request that we
believe is not currently scheduled for execution. We also have to be
careful not to rewind RING_TAIL past RING_HEAD on preempting to the
running context, and for this we use a semaphore to prevent completion
of the request before continuing.

To accomplish this feat, we change how we track requests scheduled to
the HW. Instead of appending our requests onto a single list as we
submit, we track each submission to ELSP as its own block. Then upon
receiving the CS preemption event, we promote the pending block to the
inflight block (discarding what was previously being tracked). As normal
CS completion events arrive, we then remove stale entries from the
inflight tracker.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
Skip the bonus asserts if the context is already completed.
---
 drivers/gpu/drm/i915/gem/i915_gem_context.c   |   2 +-
 drivers/gpu/drm/i915/gt/intel_context_types.h |   5 +
 drivers/gpu/drm/i915/gt/intel_engine.h        |  61 +-
 drivers/gpu/drm/i915/gt/intel_engine_cs.c     |  63 +-
 drivers/gpu/drm/i915/gt/intel_engine_types.h  |  52 +-
 drivers/gpu/drm/i915/gt/intel_lrc.c           | 678 ++++++++----------
 drivers/gpu/drm/i915/i915_gpu_error.c         |  19 +-
 drivers/gpu/drm/i915/i915_request.c           |   6 +
 drivers/gpu/drm/i915/i915_request.h           |   1 +
 drivers/gpu/drm/i915/i915_scheduler.c         |   3 +-
 drivers/gpu/drm/i915/i915_utils.h             |  12 +
 drivers/gpu/drm/i915/intel_guc_submission.c   | 175 ++---
 drivers/gpu/drm/i915/selftests/i915_request.c |   8 +-
 13 files changed, 475 insertions(+), 610 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index 0f2c22a3bcb6..35871c8a42a6 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -646,7 +646,7 @@ static void init_contexts(struct drm_i915_private *i915)
 
 static bool needs_preempt_context(struct drm_i915_private *i915)
 {
-	return HAS_EXECLISTS(i915);
+	return USES_GUC_SUBMISSION(i915);
 }
 
 int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
diff --git a/drivers/gpu/drm/i915/gt/intel_context_types.h b/drivers/gpu/drm/i915/gt/intel_context_types.h
index 08049ee91cee..4c0e211c715d 100644
--- a/drivers/gpu/drm/i915/gt/intel_context_types.h
+++ b/drivers/gpu/drm/i915/gt/intel_context_types.h
@@ -13,6 +13,7 @@
 #include <linux/types.h>
 
 #include "i915_active_types.h"
+#include "i915_utils.h"
 #include "intel_engine_types.h"
 #include "intel_sseu.h"
 
@@ -38,6 +39,10 @@ struct intel_context {
 	struct i915_gem_context *gem_context;
 	struct intel_engine_cs *engine;
 	struct intel_engine_cs *inflight;
+#define intel_context_inflight(ce) ptr_mask_bits((ce)->inflight, 2)
+#define intel_context_inflight_count(ce)  ptr_unmask_bits((ce)->inflight, 2)
+#define intel_context_inflight_inc(ce) ptr_count_inc(&(ce)->inflight)
+#define intel_context_inflight_dec(ce) ptr_count_dec(&(ce)->inflight)
 
 	struct list_head signal_link;
 	struct list_head signals;
diff --git a/drivers/gpu/drm/i915/gt/intel_engine.h b/drivers/gpu/drm/i915/gt/intel_engine.h
index 2f1c6871ee95..9bb6ff76680e 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine.h
+++ b/drivers/gpu/drm/i915/gt/intel_engine.h
@@ -125,71 +125,26 @@ hangcheck_action_to_str(const enum intel_engine_hangcheck_action a)
 
 void intel_engines_set_scheduler_caps(struct drm_i915_private *i915);
 
-static inline void
-execlists_set_active(struct intel_engine_execlists *execlists,
-		     unsigned int bit)
-{
-	__set_bit(bit, (unsigned long *)&execlists->active);
-}
-
-static inline bool
-execlists_set_active_once(struct intel_engine_execlists *execlists,
-			  unsigned int bit)
-{
-	return !__test_and_set_bit(bit, (unsigned long *)&execlists->active);
-}
-
-static inline void
-execlists_clear_active(struct intel_engine_execlists *execlists,
-		       unsigned int bit)
-{
-	__clear_bit(bit, (unsigned long *)&execlists->active);
-}
-
-static inline void
-execlists_clear_all_active(struct intel_engine_execlists *execlists)
+static inline unsigned int
+execlists_num_ports(const struct intel_engine_execlists * const execlists)
 {
-	execlists->active = 0;
+	return execlists->port_mask + 1;
 }
 
-static inline bool
-execlists_is_active(const struct intel_engine_execlists *execlists,
-		    unsigned int bit)
+static inline struct i915_request *
+execlists_active(const struct intel_engine_execlists *execlists)
 {
-	return test_bit(bit, (unsigned long *)&execlists->active);
+	GEM_BUG_ON(execlists->active - execlists->inflight >
+		   execlists_num_ports(execlists));
+	return READ_ONCE(*execlists->active);
 }
 
-void execlists_user_begin(struct intel_engine_execlists *execlists,
-			  const struct execlist_port *port);
-void execlists_user_end(struct intel_engine_execlists *execlists);
-
 void
 execlists_cancel_port_requests(struct intel_engine_execlists * const execlists);
 
 struct i915_request *
 execlists_unwind_incomplete_requests(struct intel_engine_execlists *execlists);
 
-static inline unsigned int
-execlists_num_ports(const struct intel_engine_execlists * const execlists)
-{
-	return execlists->port_mask + 1;
-}
-
-static inline struct execlist_port *
-execlists_port_complete(struct intel_engine_execlists * const execlists,
-			struct execlist_port * const port)
-{
-	const unsigned int m = execlists->port_mask;
-
-	GEM_BUG_ON(port_index(port, execlists) != 0);
-	GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_USER));
-
-	memmove(port, port + 1, m * sizeof(struct execlist_port));
-	memset(port + m, 0, sizeof(struct execlist_port));
-
-	return port;
-}
-
 static inline u32
 intel_read_status_page(const struct intel_engine_cs *engine, int reg)
 {
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
index 7fd33e81c2d9..d45328e254dc 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
@@ -508,6 +508,10 @@ void intel_engine_init_execlists(struct intel_engine_cs *engine)
 	GEM_BUG_ON(!is_power_of_2(execlists_num_ports(execlists)));
 	GEM_BUG_ON(execlists_num_ports(execlists) > EXECLIST_MAX_PORTS);
 
+	memset(execlists->pending, 0, sizeof(execlists->pending));
+	execlists->active =
+		memset(execlists->inflight, 0, sizeof(execlists->inflight));
+
 	execlists->queue_priority_hint = INT_MIN;
 	execlists->queue = RB_ROOT_CACHED;
 }
@@ -1152,7 +1156,7 @@ bool intel_engine_is_idle(struct intel_engine_cs *engine)
 		return true;
 
 	/* Waiting to drain ELSP? */
-	if (READ_ONCE(engine->execlists.active)) {
+	if (execlists_active(&engine->execlists)) {
 		struct tasklet_struct *t = &engine->execlists.tasklet;
 
 		synchronize_hardirq(engine->i915->drm.irq);
@@ -1169,7 +1173,7 @@ bool intel_engine_is_idle(struct intel_engine_cs *engine)
 		/* Otherwise flush the tasklet if it was on another cpu */
 		tasklet_unlock_wait(t);
 
-		if (READ_ONCE(engine->execlists.active))
+		if (execlists_active(&engine->execlists))
 			return false;
 	}
 
@@ -1367,6 +1371,7 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine,
 	}
 
 	if (HAS_EXECLISTS(dev_priv)) {
+		struct i915_request * const *port, *rq;
 		const u32 *hws =
 			&engine->status_page.addr[I915_HWS_CSB_BUF0_INDEX];
 		const u8 num_entries = execlists->csb_size;
@@ -1399,27 +1404,33 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine,
 		}
 
 		spin_lock_irqsave(&engine->active.lock, flags);
-		for (idx = 0; idx < execlists_num_ports(execlists); idx++) {
-			struct i915_request *rq;
-			unsigned int count;
+		for (port = execlists->active; (rq = *port); port++) {
+			char hdr[80];
+			int len;
+
+			len = snprintf(hdr, sizeof(hdr),
+				       "\t\tActive[%d: ",
+				       (int)(port - execlists->active));
+			if (!i915_request_signaled(rq))
+				len += snprintf(hdr + len, sizeof(hdr) - len,
+						"ring:{start:%08x, hwsp:%08x, seqno:%08x}, ",
+						i915_ggtt_offset(rq->ring->vma),
+						rq->timeline->hwsp_offset,
+						hwsp_seqno(rq));
+			snprintf(hdr + len, sizeof(hdr) - len, "rq: ");
+			print_request(m, rq, hdr);
+		}
+		for (port = execlists->pending; (rq = *port); port++) {
 			char hdr[80];
 
-			rq = port_unpack(&execlists->port[idx], &count);
-			if (!rq) {
-				drm_printf(m, "\t\tELSP[%d] idle\n", idx);
-			} else if (!i915_request_signaled(rq)) {
-				snprintf(hdr, sizeof(hdr),
-					 "\t\tELSP[%d] count=%d, ring:{start:%08x, hwsp:%08x, seqno:%08x}, rq: ",
-					 idx, count,
-					 i915_ggtt_offset(rq->ring->vma),
-					 rq->timeline->hwsp_offset,
-					 hwsp_seqno(rq));
-				print_request(m, rq, hdr);
-			} else {
-				print_request(m, rq, "\t\tELSP[%d] rq: ");
-			}
+			snprintf(hdr, sizeof(hdr),
+				 "\t\tPending[%d] ring:{start:%08x, hwsp:%08x, seqno:%08x}, rq: ",
+				 (int)(port - execlists->pending),
+				 i915_ggtt_offset(rq->ring->vma),
+				 rq->timeline->hwsp_offset,
+				 hwsp_seqno(rq));
+			print_request(m, rq, hdr);
 		}
-		drm_printf(m, "\t\tHW active? 0x%x\n", execlists->active);
 		spin_unlock_irqrestore(&engine->active.lock, flags);
 	} else if (INTEL_GEN(dev_priv) > 6) {
 		drm_printf(m, "\tPP_DIR_BASE: 0x%08x\n",
@@ -1583,15 +1594,19 @@ int intel_enable_engine_stats(struct intel_engine_cs *engine)
 	}
 
 	if (engine->stats.enabled++ == 0) {
-		const struct execlist_port *port = execlists->port;
-		unsigned int num_ports = execlists_num_ports(execlists);
+		struct i915_request * const *port;
+		struct i915_request *rq;
 
 		engine->stats.enabled_at = ktime_get();
 
 		/* XXX submission method oblivious? */
-		while (num_ports-- && port_isset(port)) {
+		for (port = execlists->active; (rq = *port); port++)
 			engine->stats.active++;
-			port++;
+
+		for (port = execlists->pending; (rq = *port); port++) {
+			/* Exclude any contexts already counted in active */
+			if (intel_context_inflight_count(rq->hw_context) == 1)
+				engine->stats.active++;
 		}
 
 		if (engine->stats.active)
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_types.h b/drivers/gpu/drm/i915/gt/intel_engine_types.h
index 43e975a26016..411b7a807b99 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_types.h
+++ b/drivers/gpu/drm/i915/gt/intel_engine_types.h
@@ -172,51 +172,10 @@ struct intel_engine_execlists {
 	 */
 	u32 __iomem *ctrl_reg;
 
-	/**
-	 * @port: execlist port states
-	 *
-	 * For each hardware ELSP (ExecList Submission Port) we keep
-	 * track of the last request and the number of times we submitted
-	 * that port to hw. We then count the number of times the hw reports
-	 * a context completion or preemption. As only one context can
-	 * be active on hw, we limit resubmission of context to port[0]. This
-	 * is called Lite Restore, of the context.
-	 */
-	struct execlist_port {
-		/**
-		 * @request_count: combined request and submission count
-		 */
-		struct i915_request *request_count;
-#define EXECLIST_COUNT_BITS 2
-#define port_request(p) ptr_mask_bits((p)->request_count, EXECLIST_COUNT_BITS)
-#define port_count(p) ptr_unmask_bits((p)->request_count, EXECLIST_COUNT_BITS)
-#define port_pack(rq, count) ptr_pack_bits(rq, count, EXECLIST_COUNT_BITS)
-#define port_unpack(p, count) ptr_unpack_bits((p)->request_count, count, EXECLIST_COUNT_BITS)
-#define port_set(p, packed) ((p)->request_count = (packed))
-#define port_isset(p) ((p)->request_count)
-#define port_index(p, execlists) ((p) - (execlists)->port)
-
-		/**
-		 * @context_id: context ID for port
-		 */
-		GEM_DEBUG_DECL(u32 context_id);
-
 #define EXECLIST_MAX_PORTS 2
-	} port[EXECLIST_MAX_PORTS];
-
-	/**
-	 * @active: is the HW active? We consider the HW as active after
-	 * submitting any context for execution and until we have seen the
-	 * last context completion event. After that, we do not expect any
-	 * more events until we submit, and so can park the HW.
-	 *
-	 * As we have a small number of different sources from which we feed
-	 * the HW, we track the state of each inside a single bitfield.
-	 */
-	unsigned int active;
-#define EXECLISTS_ACTIVE_USER 0
-#define EXECLISTS_ACTIVE_PREEMPT 1
-#define EXECLISTS_ACTIVE_HWACK 2
+	struct i915_request * const *active;
+	struct i915_request *inflight[EXECLIST_MAX_PORTS + 1 /* sentinel */];
+	struct i915_request *pending[EXECLIST_MAX_PORTS + 1];
 
 	/**
 	 * @port_mask: number of execlist ports - 1
@@ -257,11 +216,6 @@ struct intel_engine_execlists {
 	 */
 	u32 *csb_status;
 
-	/**
-	 * @preempt_complete_status: expected CSB upon completing preemption
-	 */
-	u32 preempt_complete_status;
-
 	/**
 	 * @csb_size: context status buffer FIFO size
 	 */
diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
index 82b7ace62d97..b1e45c651660 100644
--- a/drivers/gpu/drm/i915/gt/intel_lrc.c
+++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
@@ -161,6 +161,8 @@
 #define GEN8_CTX_STATUS_COMPLETED_MASK \
 	 (GEN8_CTX_STATUS_COMPLETE | GEN8_CTX_STATUS_PREEMPTED)
 
+#define CTX_DESC_FORCE_RESTORE BIT_ULL(2)
+
 /* Typical size of the average request (2 pipecontrols and a MI_BB) */
 #define EXECLISTS_REQUEST_SIZE 64 /* bytes */
 #define WA_TAIL_DWORDS 2
@@ -221,6 +223,14 @@ static void execlists_init_reg_state(u32 *reg_state,
 				     struct intel_engine_cs *engine,
 				     struct intel_ring *ring);
 
+static inline u32 intel_hws_preempt_address(struct intel_engine_cs *engine)
+{
+	return (i915_ggtt_offset(engine->status_page.vma) +
+		I915_GEM_HWS_PREEMPT_ADDR);
+}
+
+#define ring_pause(E) ((E)->status_page.addr[I915_GEM_HWS_PREEMPT])
+
 static inline struct i915_priolist *to_priolist(struct rb_node *rb)
 {
 	return rb_entry(rb, struct i915_priolist, node);
@@ -271,12 +281,6 @@ static inline bool need_preempt(const struct intel_engine_cs *engine,
 {
 	int last_prio;
 
-	if (!engine->preempt_context)
-		return false;
-
-	if (i915_request_completed(rq))
-		return false;
-
 	/*
 	 * Check if the current priority hint merits a preemption attempt.
 	 *
@@ -338,9 +342,6 @@ __maybe_unused static inline bool
 assert_priority_queue(const struct i915_request *prev,
 		      const struct i915_request *next)
 {
-	const struct intel_engine_execlists *execlists =
-		&prev->engine->execlists;
-
 	/*
 	 * Without preemption, the prev may refer to the still active element
 	 * which we refuse to let go.
@@ -348,7 +349,7 @@ assert_priority_queue(const struct i915_request *prev,
 	 * Even with preemption, there are times when we think it is better not
 	 * to preempt and leave an ostensibly lower priority request in flight.
 	 */
-	if (port_request(execlists->port) == prev)
+	if (i915_request_is_active(prev))
 		return true;
 
 	return rq_prio(prev) >= rq_prio(next);
@@ -442,13 +443,11 @@ __unwind_incomplete_requests(struct intel_engine_cs *engine)
 		struct intel_engine_cs *owner;
 
 		if (i915_request_completed(rq))
-			break;
+			continue; /* XXX */
 
 		__i915_request_unsubmit(rq);
 		unwind_wa_tail(rq);
 
-		GEM_BUG_ON(rq->hw_context->inflight);
-
 		/*
 		 * Push the request back into the queue for later resubmission.
 		 * If this request is not native to this physical engine (i.e.
@@ -500,32 +499,32 @@ execlists_context_status_change(struct i915_request *rq, unsigned long status)
 				   status, rq);
 }
 
-inline void
-execlists_user_begin(struct intel_engine_execlists *execlists,
-		     const struct execlist_port *port)
+static inline struct i915_request *
+execlists_schedule_in(struct i915_request *rq, int idx)
 {
-	execlists_set_active_once(execlists, EXECLISTS_ACTIVE_USER);
-}
+	struct intel_context *ce = rq->hw_context;
+	int count;
 
-inline void
-execlists_user_end(struct intel_engine_execlists *execlists)
-{
-	execlists_clear_active(execlists, EXECLISTS_ACTIVE_USER);
-}
+	trace_i915_request_in(rq, idx);
 
-static inline void
-execlists_context_schedule_in(struct i915_request *rq)
-{
-	GEM_BUG_ON(rq->hw_context->inflight);
+	count = intel_context_inflight_count(ce);
+	if (!count) {
+		intel_context_get(ce);
+		ce->inflight = rq->engine;
+
+		execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_IN);
+		intel_engine_context_in(ce->inflight);
+	}
 
-	execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_IN);
-	intel_engine_context_in(rq->engine);
-	rq->hw_context->inflight = rq->engine;
+	intel_context_inflight_inc(ce);
+	GEM_BUG_ON(intel_context_inflight(ce) != rq->engine);
+
+	return i915_request_get(rq);
 }
 
-static void kick_siblings(struct i915_request *rq)
+static void kick_siblings(struct i915_request *rq, struct intel_context *ce)
 {
-	struct virtual_engine *ve = to_virtual_engine(rq->hw_context->engine);
+	struct virtual_engine *ve = container_of(ce, typeof(*ve), context);
 	struct i915_request *next = READ_ONCE(ve->request);
 
 	if (next && next->execution_mask & ~rq->execution_mask)
@@ -533,29 +532,42 @@ static void kick_siblings(struct i915_request *rq)
 }
 
 static inline void
-execlists_context_schedule_out(struct i915_request *rq, unsigned long status)
+execlists_schedule_out(struct i915_request *rq)
 {
-	rq->hw_context->inflight = NULL;
-	intel_engine_context_out(rq->engine);
-	execlists_context_status_change(rq, status);
+	struct intel_context *ce = rq->hw_context;
+
+	GEM_BUG_ON(!intel_context_inflight_count(ce));
+
 	trace_i915_request_out(rq);
 
-	/*
-	 * If this is part of a virtual engine, its next request may have
-	 * been blocked waiting for access to the active context. We have
-	 * to kick all the siblings again in case we need to switch (e.g.
-	 * the next request is not runnable on this engine). Hopefully,
-	 * we will already have submitted the next request before the
-	 * tasklet runs and do not need to rebuild each virtual tree
-	 * and kick everyone again.
-	 */
-	if (rq->engine != rq->hw_context->engine)
-		kick_siblings(rq);
+	intel_context_inflight_dec(ce);
+	if (!intel_context_inflight_count(ce)) {
+		intel_engine_context_out(ce->inflight);
+		execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_OUT);
+
+		ce->inflight = NULL;
+		intel_context_put(ce);
+
+		/*
+		 * If this is part of a virtual engine, its next request may
+		 * have been blocked waiting for access to the active context.
+		 * We have to kick all the siblings again in case we need to
+		 * switch (e.g. the next request is not runnable on this
+		 * engine). Hopefully, we will already have submitted the next
+		 * request before the tasklet runs and do not need to rebuild
+		 * each virtual tree and kick everyone again.
+		 */
+		if (rq->engine != ce->engine)
+			kick_siblings(rq, ce);
+	}
+
+	i915_request_put(rq);
 }
 
-static u64 execlists_update_context(struct i915_request *rq)
+static u64 execlists_update_context(const struct i915_request *rq)
 {
 	struct intel_context *ce = rq->hw_context;
+	u64 desc;
 
 	ce->lrc_reg_state[CTX_RING_TAIL + 1] =
 		intel_ring_set_tail(rq->ring, rq->tail);
@@ -576,7 +588,11 @@ static u64 execlists_update_context(struct i915_request *rq)
 	 * wmb).
 	 */
 	mb();
-	return ce->lrc_desc;
+
+	desc = ce->lrc_desc;
+	ce->lrc_desc &= ~CTX_DESC_FORCE_RESTORE;
+
+	return desc;
 }
 
 static inline void write_desc(struct intel_engine_execlists *execlists, u64 desc, u32 port)
@@ -590,12 +606,59 @@ static inline void write_desc(struct intel_engine_execlists *execlists, u64 desc
 	}
 }
 
+static __maybe_unused void
+trace_ports(const struct intel_engine_execlists *execlists,
+	    const char *msg,
+	    struct i915_request * const *ports)
+{
+	const struct intel_engine_cs *engine =
+		container_of(execlists, typeof(*engine), execlists);
+
+	GEM_TRACE("%s: %s { %llx:%lld%s, %llx:%lld }\n",
+		  engine->name, msg,
+		  ports[0]->fence.context,
+		  ports[0]->fence.seqno,
+		  i915_request_completed(ports[0]) ? "!" :
+		  i915_request_started(ports[0]) ? "*" :
+		  "",
+		  ports[1] ? ports[1]->fence.context : 0,
+		  ports[1] ? ports[1]->fence.seqno : 0);
+}
+
+static __maybe_unused bool
+assert_pending_valid(const struct intel_engine_execlists *execlists,
+		     const char *msg)
+{
+	struct i915_request * const *port, *rq;
+	struct intel_context *ce = NULL;
+
+	trace_ports(execlists, msg, execlists->pending);
+
+	if (execlists->pending[execlists_num_ports(execlists)])
+		return false;
+
+	for (port = execlists->pending; (rq = *port); port++) {
+		if (ce == rq->hw_context)
+			return false;
+
+		ce = rq->hw_context;
+		if (i915_request_completed(rq))
+			continue;
+
+		GEM_BUG_ON(i915_active_is_idle(&ce->active));
+		GEM_BUG_ON(!i915_vma_is_pinned(ce->state));
+	}
+
+	return ce;
+}
+
 static void execlists_submit_ports(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists *execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
 	unsigned int n;
 
+	GEM_BUG_ON(!assert_pending_valid(execlists, "submit"));
+
 	/*
 	 * We can skip acquiring intel_runtime_pm_get() here as it was taken
 	 * on our behalf by the request (see i915_gem_mark_busy()) and it will
@@ -613,38 +676,16 @@ static void execlists_submit_ports(struct intel_engine_cs *engine)
 	 * of elsq entries, keep this in mind before changing the loop below.
 	 */
 	for (n = execlists_num_ports(execlists); n--; ) {
-		struct i915_request *rq;
-		unsigned int count;
-		u64 desc;
+		struct i915_request *rq = execlists->pending[n];
 
-		rq = port_unpack(&port[n], &count);
-		if (rq) {
-			GEM_BUG_ON(count > !n);
-			if (!count++)
-				execlists_context_schedule_in(rq);
-			port_set(&port[n], port_pack(rq, count));
-			desc = execlists_update_context(rq);
-			GEM_DEBUG_EXEC(port[n].context_id = upper_32_bits(desc));
-
-			GEM_TRACE("%s in[%d]:  ctx=%d.%d, fence %llx:%lld (current %d), prio=%d\n",
-				  engine->name, n,
-				  port[n].context_id, count,
-				  rq->fence.context, rq->fence.seqno,
-				  hwsp_seqno(rq),
-				  rq_prio(rq));
-		} else {
-			GEM_BUG_ON(!n);
-			desc = 0;
-		}
-
-		write_desc(execlists, desc, n);
+		write_desc(execlists,
+			   rq ? execlists_update_context(rq) : 0,
+			   n);
 	}
 
 	/* we need to manually load the submit queue */
 	if (execlists->ctrl_reg)
 		writel(EL_CTRL_LOAD, execlists->ctrl_reg);
-
-	execlists_clear_active(execlists, EXECLISTS_ACTIVE_HWACK);
 }
 
 static bool ctx_single_port_submission(const struct intel_context *ce)
@@ -668,6 +709,7 @@ static bool can_merge_ctx(const struct intel_context *prev,
 static bool can_merge_rq(const struct i915_request *prev,
 			 const struct i915_request *next)
 {
+	GEM_BUG_ON(prev == next);
 	GEM_BUG_ON(!assert_priority_queue(prev, next));
 
 	if (!can_merge_ctx(prev->hw_context, next->hw_context))
@@ -676,58 +718,6 @@ static bool can_merge_rq(const struct i915_request *prev,
 	return true;
 }
 
-static void port_assign(struct execlist_port *port, struct i915_request *rq)
-{
-	GEM_BUG_ON(rq == port_request(port));
-
-	if (port_isset(port))
-		i915_request_put(port_request(port));
-
-	port_set(port, port_pack(i915_request_get(rq), port_count(port)));
-}
-
-static void inject_preempt_context(struct intel_engine_cs *engine)
-{
-	struct intel_engine_execlists *execlists = &engine->execlists;
-	struct intel_context *ce = engine->preempt_context;
-	unsigned int n;
-
-	GEM_BUG_ON(execlists->preempt_complete_status !=
-		   upper_32_bits(ce->lrc_desc));
-
-	/*
-	 * Switch to our empty preempt context so
-	 * the state of the GPU is known (idle).
-	 */
-	GEM_TRACE("%s\n", engine->name);
-	for (n = execlists_num_ports(execlists); --n; )
-		write_desc(execlists, 0, n);
-
-	write_desc(execlists, ce->lrc_desc, n);
-
-	/* we need to manually load the submit queue */
-	if (execlists->ctrl_reg)
-		writel(EL_CTRL_LOAD, execlists->ctrl_reg);
-
-	execlists_clear_active(execlists, EXECLISTS_ACTIVE_HWACK);
-	execlists_set_active(execlists, EXECLISTS_ACTIVE_PREEMPT);
-
-	(void)I915_SELFTEST_ONLY(execlists->preempt_hang.count++);
-}
-
-static void complete_preempt_context(struct intel_engine_execlists *execlists)
-{
-	GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT));
-
-	if (inject_preempt_hang(execlists))
-		return;
-
-	execlists_cancel_port_requests(execlists);
-	__unwind_incomplete_requests(container_of(execlists,
-						  struct intel_engine_cs,
-						  execlists));
-}
-
 static void virtual_update_register_offsets(u32 *regs,
 					    struct intel_engine_cs *engine)
 {
@@ -792,7 +782,7 @@ static bool virtual_matches(const struct virtual_engine *ve,
 	 * we reuse the register offsets). This is a very small
 	 * hystersis on the greedy seelction algorithm.
 	 */
-	inflight = READ_ONCE(ve->context.inflight);
+	inflight = intel_context_inflight(&ve->context);
 	if (inflight && inflight != engine)
 		return false;
 
@@ -815,13 +805,23 @@ static void virtual_xfer_breadcrumbs(struct virtual_engine *ve,
 	spin_unlock(&old->breadcrumbs.irq_lock);
 }
 
+static struct i915_request *
+last_active(const struct intel_engine_execlists *execlists)
+{
+	struct i915_request * const *last = execlists->active;
+
+	while (*last && i915_request_completed(*last))
+		last++;
+
+	return *last;
+}
+
 static void execlists_dequeue(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
-	const struct execlist_port * const last_port =
-		&execlists->port[execlists->port_mask];
-	struct i915_request *last = port_request(port);
+	struct i915_request **port = execlists->pending;
+	struct i915_request ** const last_port = port + execlists->port_mask;
+	struct i915_request *last;
 	struct rb_node *rb;
 	bool submit = false;
 
@@ -867,65 +867,72 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 		break;
 	}
 
+	/*
+	 * If the queue is higher priority than the last
+	 * request in the currently active context, submit afresh.
+	 * We will resubmit again afterwards in case we need to split
+	 * the active context to interject the preemption request,
+	 * i.e. we will retrigger preemption following the ack in case
+	 * of trouble.
+	 */
+	last = last_active(execlists);
 	if (last) {
-		/*
-		 * Don't resubmit or switch until all outstanding
-		 * preemptions (lite-restore) are seen. Then we
-		 * know the next preemption status we see corresponds
-		 * to this ELSP update.
-		 */
-		GEM_BUG_ON(!execlists_is_active(execlists,
-						EXECLISTS_ACTIVE_USER));
-		GEM_BUG_ON(!port_count(&port[0]));
-
-		/*
-		 * If we write to ELSP a second time before the HW has had
-		 * a chance to respond to the previous write, we can confuse
-		 * the HW and hit "undefined behaviour". After writing to ELSP,
-		 * we must then wait until we see a context-switch event from
-		 * the HW to indicate that it has had a chance to respond.
-		 */
-		if (!execlists_is_active(execlists, EXECLISTS_ACTIVE_HWACK))
-			return;
-
 		if (need_preempt(engine, last, rb)) {
-			inject_preempt_context(engine);
-			return;
-		}
+			GEM_TRACE("%s: preempting last=%llx:%lld, prio=%d, hint=%d\n",
+				  engine->name,
+				  last->fence.context,
+				  last->fence.seqno,
+				  last->sched.attr.priority,
+				  execlists->queue_priority_hint);
+			/*
+			 * Don't let the RING_HEAD advance past the breadcrumb
+			 * as we unwind (and until we resubmit) so that we do
+			 * not accidentally tell it to go backwards.
+			 */
+			ring_pause(engine) = 1;
 
-		/*
-		 * In theory, we could coalesce more requests onto
-		 * the second port (the first port is active, with
-		 * no preemptions pending). However, that means we
-		 * then have to deal with the possible lite-restore
-		 * of the second port (as we submit the ELSP, there
-		 * may be a context-switch) but also we may complete
-		 * the resubmission before the context-switch. Ergo,
-		 * coalescing onto the second port will cause a
-		 * preemption event, but we cannot predict whether
-		 * that will affect port[0] or port[1].
-		 *
-		 * If the second port is already active, we can wait
-		 * until the next context-switch before contemplating
-		 * new requests. The GPU will be busy and we should be
-		 * able to resubmit the new ELSP before it idles,
-		 * avoiding pipeline bubbles (momentary pauses where
-		 * the driver is unable to keep up the supply of new
-		 * work). However, we have to double check that the
-		 * priorities of the ports haven't been switch.
-		 */
-		if (port_count(&port[1]))
-			return;
+			/*
+			 * Note that we have not stopped the GPU at this point,
+			 * so we are unwinding the incomplete requests as they
+			 * remain inflight and so by the time we do complete
+			 * the preemption, some of the unwound requests may
+			 * complete!
+			 */
+			__unwind_incomplete_requests(engine);
 
-		/*
-		 * WaIdleLiteRestore:bdw,skl
-		 * Apply the wa NOOPs to prevent
-		 * ring:HEAD == rq:TAIL as we resubmit the
-		 * request. See gen8_emit_fini_breadcrumb() for
-		 * where we prepare the padding after the
-		 * end of the request.
-		 */
-		last->tail = last->wa_tail;
+			/*
+			 * If we need to return to the preempted context, we
+			 * need to skip the lite-restore and force it to
+			 * reload the RING_TAIL. Otherwise, the HW has a
+			 * tendency to ignore us rewinding the TAIL to the
+			 * end of an earlier request.
+			 */
+			last->hw_context->lrc_desc |= CTX_DESC_FORCE_RESTORE;
+			last = NULL;
+		} else {
+			/*
+			 * Otherwise if we already have a request pending
+			 * for execution after the current one, we can
+			 * just wait until the next CS event before
+			 * queuing more. In either case we will force a
+			 * lite-restore preemption event, but if we wait
+			 * we hopefully coalesce several updates into a single
+			 * submission.
+			 */
+			if (!list_is_last(&last->sched.link,
+					  &engine->active.requests))
+				return;
+
+			/*
+			 * WaIdleLiteRestore:bdw,skl
+			 * Apply the wa NOOPs to prevent
+			 * ring:HEAD == rq:TAIL as we resubmit the
+			 * request. See gen8_emit_fini_breadcrumb() for
+			 * where we prepare the padding after the
+			 * end of the request.
+			 */
+			last->tail = last->wa_tail;
+		}
 	}
 
 	while (rb) { /* XXX virtual is always taking precedence */
@@ -955,9 +962,24 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 				continue;
 			}
 
+			if (i915_request_completed(rq)) {
+				ve->request = NULL;
+				ve->base.execlists.queue_priority_hint = INT_MIN;
+				rb_erase_cached(rb, &execlists->virtual);
+				RB_CLEAR_NODE(rb);
+
+				rq->engine = engine;
+				__i915_request_submit(rq);
+
+				spin_unlock(&ve->base.active.lock);
+
+				rb = rb_first_cached(&execlists->virtual);
+				continue;
+			}
+
 			if (last && !can_merge_rq(last, rq)) {
 				spin_unlock(&ve->base.active.lock);
-				return; /* leave this rq for another engine */
+				return; /* leave this for another */
 			}
 
 			GEM_TRACE("%s: virtual rq=%llx:%lld%s, new engine? %s\n",
@@ -1006,9 +1028,10 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 			}
 
 			__i915_request_submit(rq);
-			trace_i915_request_in(rq, port_index(port, execlists));
-			submit = true;
-			last = rq;
+			if (!i915_request_completed(rq)) {
+				submit = true;
+				last = rq;
+			}
 		}
 
 		spin_unlock(&ve->base.active.lock);
@@ -1021,6 +1044,9 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 		int i;
 
 		priolist_for_each_request_consume(rq, rn, p, i) {
+			if (i915_request_completed(rq))
+				goto skip;
+
 			/*
 			 * Can we combine this request with the current port?
 			 * It has to be the same context/ringbuffer and not
@@ -1060,19 +1086,14 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 				    ctx_single_port_submission(rq->hw_context))
 					goto done;
 
-
-				if (submit)
-					port_assign(port, last);
+				*port = execlists_schedule_in(last, port - execlists->pending);
 				port++;
-
-				GEM_BUG_ON(port_isset(port));
 			}
 
-			__i915_request_submit(rq);
-			trace_i915_request_in(rq, port_index(port, execlists));
-
 			last = rq;
 			submit = true;
+skip:
+			__i915_request_submit(rq);
 		}
 
 		rb_erase_cached(&p->node, &execlists->queue);
@@ -1097,54 +1118,30 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 	 * interrupt for secondary ports).
 	 */
 	execlists->queue_priority_hint = queue_prio(execlists);
+	GEM_TRACE("%s: queue_priority_hint:%d, submit:%s\n",
+		  engine->name, execlists->queue_priority_hint,
+		  yesno(submit));
 
 	if (submit) {
-		port_assign(port, last);
+		*port = execlists_schedule_in(last, port - execlists->pending);
+		memset(port + 1, 0, (last_port - port) * sizeof(*port));
 		execlists_submit_ports(engine);
 	}
-
-	/* We must always keep the beast fed if we have work piled up */
-	GEM_BUG_ON(rb_first_cached(&execlists->queue) &&
-		   !port_isset(execlists->port));
-
-	/* Re-evaluate the executing context setup after each preemptive kick */
-	if (last)
-		execlists_user_begin(execlists, execlists->port);
-
-	/* If the engine is now idle, so should be the flag; and vice versa. */
-	GEM_BUG_ON(execlists_is_active(&engine->execlists,
-				       EXECLISTS_ACTIVE_USER) ==
-		   !port_isset(engine->execlists.port));
 }
 
 void
 execlists_cancel_port_requests(struct intel_engine_execlists * const execlists)
 {
-	struct execlist_port *port = execlists->port;
-	unsigned int num_ports = execlists_num_ports(execlists);
-
-	while (num_ports-- && port_isset(port)) {
-		struct i915_request *rq = port_request(port);
-
-		GEM_TRACE("%s:port%u fence %llx:%lld, (current %d)\n",
-			  rq->engine->name,
-			  (unsigned int)(port - execlists->port),
-			  rq->fence.context, rq->fence.seqno,
-			  hwsp_seqno(rq));
+	struct i915_request * const *port, *rq;
 
-		GEM_BUG_ON(!execlists->active);
-		execlists_context_schedule_out(rq,
-					       i915_request_completed(rq) ?
-					       INTEL_CONTEXT_SCHEDULE_OUT :
-					       INTEL_CONTEXT_SCHEDULE_PREEMPTED);
+	for (port = execlists->pending; (rq = *port); port++)
+		execlists_schedule_out(rq);
+	memset(execlists->pending, 0, sizeof(execlists->pending));
 
-		i915_request_put(rq);
-
-		memset(port, 0, sizeof(*port));
-		port++;
-	}
-
-	execlists_clear_all_active(execlists);
+	for (port = execlists->active; (rq = *port); port++)
+		execlists_schedule_out(rq);
+	execlists->active =
+		memset(execlists->inflight, 0, sizeof(execlists->inflight));
 }
 
 static inline void
@@ -1163,7 +1160,6 @@ reset_in_progress(const struct intel_engine_execlists *execlists)
 static void process_csb(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
 	const u32 * const buf = execlists->csb_status;
 	const u8 num_entries = execlists->csb_size;
 	u8 head, tail;
@@ -1198,9 +1194,7 @@ static void process_csb(struct intel_engine_cs *engine)
 	rmb();
 
 	do {
-		struct i915_request *rq;
 		unsigned int status;
-		unsigned int count;
 
 		if (++head == num_entries)
 			head = 0;
@@ -1223,68 +1217,37 @@ static void process_csb(struct intel_engine_cs *engine)
 		 * status notifier.
 		 */
 
-		GEM_TRACE("%s csb[%d]: status=0x%08x:0x%08x, active=0x%x\n",
+		GEM_TRACE("%s csb[%d]: status=0x%08x:0x%08x\n",
 			  engine->name, head,
-			  buf[2 * head + 0], buf[2 * head + 1],
-			  execlists->active);
+			  buf[2 * head + 0], buf[2 * head + 1]);
 
 		status = buf[2 * head];
-		if (status & (GEN8_CTX_STATUS_IDLE_ACTIVE |
-			      GEN8_CTX_STATUS_PREEMPTED))
-			execlists_set_active(execlists,
-					     EXECLISTS_ACTIVE_HWACK);
-		if (status & GEN8_CTX_STATUS_ACTIVE_IDLE)
-			execlists_clear_active(execlists,
-					       EXECLISTS_ACTIVE_HWACK);
-
-		if (!(status & GEN8_CTX_STATUS_COMPLETED_MASK))
-			continue;
+		if (status & GEN8_CTX_STATUS_IDLE_ACTIVE) {
+promote:
+			GEM_BUG_ON(!assert_pending_valid(execlists, "promote"));
+			execlists->active =
+				memcpy(execlists->inflight,
+				       execlists->pending,
+				       execlists_num_ports(execlists) *
+				       sizeof(*execlists->pending));
+			execlists->pending[0] = NULL;
 
-		/* We should never get a COMPLETED | IDLE_ACTIVE! */
-		GEM_BUG_ON(status & GEN8_CTX_STATUS_IDLE_ACTIVE);
+			if (!inject_preempt_hang(execlists))
+				ring_pause(engine) = 0;
+		} else if (status & GEN8_CTX_STATUS_PREEMPTED) {
+			struct i915_request * const *port = execlists->active;
 
-		if (status & GEN8_CTX_STATUS_COMPLETE &&
-		    buf[2*head + 1] == execlists->preempt_complete_status) {
-			GEM_TRACE("%s preempt-idle\n", engine->name);
-			complete_preempt_context(execlists);
-			continue;
-		}
-
-		if (status & GEN8_CTX_STATUS_PREEMPTED &&
-		    execlists_is_active(execlists,
-					EXECLISTS_ACTIVE_PREEMPT))
-			continue;
+			trace_ports(execlists, "preempted", execlists->active);
 
-		GEM_BUG_ON(!execlists_is_active(execlists,
-						EXECLISTS_ACTIVE_USER));
+			while (*port)
+				execlists_schedule_out(*port++);
 
-		rq = port_unpack(port, &count);
-		GEM_TRACE("%s out[0]: ctx=%d.%d, fence %llx:%lld (current %d), prio=%d\n",
-			  engine->name,
-			  port->context_id, count,
-			  rq ? rq->fence.context : 0,
-			  rq ? rq->fence.seqno : 0,
-			  rq ? hwsp_seqno(rq) : 0,
-			  rq ? rq_prio(rq) : 0);
+			goto promote;
+		} else if (*execlists->active) {
+			struct i915_request *rq = *execlists->active++;
 
-		/* Check the context/desc id for this event matches */
-		GEM_DEBUG_BUG_ON(buf[2 * head + 1] != port->context_id);
-
-		GEM_BUG_ON(count == 0);
-		if (--count == 0) {
-			/*
-			 * On the final event corresponding to the
-			 * submission of this context, we expect either
-			 * an element-switch event or a completion
-			 * event (and on completion, the active-idle
-			 * marker). No more preemptions, lite-restore
-			 * or otherwise.
-			 */
-			GEM_BUG_ON(status & GEN8_CTX_STATUS_PREEMPTED);
-			GEM_BUG_ON(port_isset(&port[1]) &&
-				   !(status & GEN8_CTX_STATUS_ELEMENT_SWITCH));
-			GEM_BUG_ON(!port_isset(&port[1]) &&
-				   !(status & GEN8_CTX_STATUS_ACTIVE_IDLE));
+			trace_ports(execlists, "completed",
+				    execlists->active - 1);
 
 			/*
 			 * We rely on the hardware being strongly
@@ -1293,21 +1256,10 @@ static void process_csb(struct intel_engine_cs *engine)
 			 * user interrupt and CSB is processed.
 			 */
 			GEM_BUG_ON(!i915_request_completed(rq));
+			execlists_schedule_out(rq);
 
-			execlists_context_schedule_out(rq,
-						       INTEL_CONTEXT_SCHEDULE_OUT);
-			i915_request_put(rq);
-
-			GEM_TRACE("%s completed ctx=%d\n",
-				  engine->name, port->context_id);
-
-			port = execlists_port_complete(execlists, port);
-			if (port_isset(port))
-				execlists_user_begin(execlists, port);
-			else
-				execlists_user_end(execlists);
-		} else {
-			port_set(port, port_pack(rq, count));
+			GEM_BUG_ON(execlists->active - execlists->inflight >
+				   execlists_num_ports(execlists));
 		}
 	} while (head != tail);
 
@@ -1332,7 +1284,7 @@ static void __execlists_submission_tasklet(struct intel_engine_cs *const engine)
 	lockdep_assert_held(&engine->active.lock);
 
 	process_csb(engine);
-	if (!execlists_is_active(&engine->execlists, EXECLISTS_ACTIVE_PREEMPT))
+	if (!engine->execlists.pending[0])
 		execlists_dequeue(engine);
 }
 
@@ -1345,11 +1297,6 @@ static void execlists_submission_tasklet(unsigned long data)
 	struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
 	unsigned long flags;
 
-	GEM_TRACE("%s awake?=%d, active=%x\n",
-		  engine->name,
-		  !!intel_wakeref_active(&engine->wakeref),
-		  engine->execlists.active);
-
 	spin_lock_irqsave(&engine->active.lock, flags);
 	__execlists_submission_tasklet(engine);
 	spin_unlock_irqrestore(&engine->active.lock, flags);
@@ -1376,12 +1323,16 @@ static void __submit_queue_imm(struct intel_engine_cs *engine)
 		tasklet_hi_schedule(&execlists->tasklet);
 }
 
-static void submit_queue(struct intel_engine_cs *engine, int prio)
+static void submit_queue(struct intel_engine_cs *engine,
+			 const struct i915_request *rq)
 {
-	if (prio > engine->execlists.queue_priority_hint) {
-		engine->execlists.queue_priority_hint = prio;
-		__submit_queue_imm(engine);
-	}
+	struct intel_engine_execlists *execlists = &engine->execlists;
+
+	if (rq_prio(rq) <= execlists->queue_priority_hint)
+		return;
+
+	execlists->queue_priority_hint = rq_prio(rq);
+	__submit_queue_imm(engine);
 }
 
 static void execlists_submit_request(struct i915_request *request)
@@ -1397,7 +1348,7 @@ static void execlists_submit_request(struct i915_request *request)
 	GEM_BUG_ON(RB_EMPTY_ROOT(&engine->execlists.queue.rb_root));
 	GEM_BUG_ON(list_empty(&request->sched.link));
 
-	submit_queue(engine, rq_prio(request));
+	submit_queue(engine, request);
 
 	spin_unlock_irqrestore(&engine->active.lock, flags);
 }
@@ -2048,27 +1999,13 @@ static void execlists_reset_prepare(struct intel_engine_cs *engine)
 	spin_unlock_irqrestore(&engine->active.lock, flags);
 }
 
-static bool lrc_regs_ok(const struct i915_request *rq)
-{
-	const struct intel_ring *ring = rq->ring;
-	const u32 *regs = rq->hw_context->lrc_reg_state;
-
-	/* Quick spot check for the common signs of context corruption */
-
-	if (regs[CTX_RING_BUFFER_CONTROL + 1] !=
-	    (RING_CTL_SIZE(ring->size) | RING_VALID))
-		return false;
-
-	if (regs[CTX_RING_BUFFER_START + 1] != i915_ggtt_offset(ring->vma))
-		return false;
-
-	return true;
-}
-
-static void reset_csb_pointers(struct intel_engine_execlists *execlists)
+static void reset_csb_pointers(struct intel_engine_cs *engine)
 {
+	struct intel_engine_execlists * const execlists = &engine->execlists;
 	const unsigned int reset_value = execlists->csb_size - 1;
 
+	ring_pause(engine) = 0;
+
 	/*
 	 * After a reset, the HW starts writing into CSB entry [0]. We
 	 * therefore have to set our HEAD pointer back one entry so that
@@ -2115,18 +2052,21 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
 	process_csb(engine); /* drain preemption events */
 
 	/* Following the reset, we need to reload the CSB read/write pointers */
-	reset_csb_pointers(&engine->execlists);
+	reset_csb_pointers(engine);
 
 	/*
 	 * Save the currently executing context, even if we completed
 	 * its request, it was still running at the time of the
 	 * reset and will have been clobbered.
 	 */
-	if (!port_isset(execlists->port))
-		goto out_clear;
+	rq = execlists_active(execlists);
+	if (!rq)
+		return;
 
-	rq = port_request(execlists->port);
 	ce = rq->hw_context;
+	GEM_BUG_ON(i915_active_is_idle(&ce->active));
+	GEM_BUG_ON(!i915_vma_is_pinned(ce->state));
+	rq = active_request(rq);
 
 	/*
 	 * Catch up with any missed context-switch interrupts.
@@ -2139,9 +2079,12 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
 	 */
 	execlists_cancel_port_requests(execlists);
 
-	rq = active_request(rq);
-	if (!rq)
+	if (!rq) {
+		ce->ring->head = ce->ring->tail;
 		goto out_replay;
+	}
+
+	ce->ring->head = intel_ring_wrap(ce->ring, rq->head);
 
 	/*
 	 * If this request hasn't started yet, e.g. it is waiting on a
@@ -2155,7 +2098,7 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
 	 * Otherwise, if we have not started yet, the request should replay
 	 * perfectly and we do not need to flag the result as being erroneous.
 	 */
-	if (!i915_request_started(rq) && lrc_regs_ok(rq))
+	if (!i915_request_started(rq))
 		goto out_replay;
 
 	/*
@@ -2170,7 +2113,7 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
 	 * image back to the expected values to skip over the guilty request.
 	 */
 	i915_reset_request(rq, stalled);
-	if (!stalled && lrc_regs_ok(rq))
+	if (!stalled)
 		goto out_replay;
 
 	/*
@@ -2190,17 +2133,13 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
 	execlists_init_reg_state(regs, ce, engine, ce->ring);
 
 out_replay:
-	/* Rerun the request; its payload has been neutered (if guilty). */
-	ce->ring->head =
-		rq ? intel_ring_wrap(ce->ring, rq->head) : ce->ring->tail;
+	GEM_TRACE("%s replay {head:%04x, tail:%04x\n",
+		  engine->name, ce->ring->head, ce->ring->tail);
 	intel_ring_update_space(ce->ring);
 	__execlists_update_reg_state(ce, engine);
 
 	/* Push back any incomplete requests for replay after the reset. */
 	__unwind_incomplete_requests(engine);
-
-out_clear:
-	execlists_clear_all_active(execlists);
 }
 
 static void execlists_reset(struct intel_engine_cs *engine, bool stalled)
@@ -2296,7 +2235,6 @@ static void execlists_cancel_requests(struct intel_engine_cs *engine)
 
 	execlists->queue_priority_hint = INT_MIN;
 	execlists->queue = RB_ROOT_CACHED;
-	GEM_BUG_ON(port_isset(execlists->port));
 
 	GEM_BUG_ON(__tasklet_is_enabled(&execlists->tasklet));
 	execlists->tasklet.func = nop_submission_tasklet;
@@ -2514,15 +2452,29 @@ static u32 *gen8_emit_wa_tail(struct i915_request *request, u32 *cs)
 	return cs;
 }
 
+static u32 *emit_preempt_busywait(struct i915_request *request, u32 *cs)
+{
+	*cs++ = MI_SEMAPHORE_WAIT |
+		MI_SEMAPHORE_GLOBAL_GTT |
+		MI_SEMAPHORE_POLL |
+		MI_SEMAPHORE_SAD_EQ_SDD;
+	*cs++ = 0;
+	*cs++ = intel_hws_preempt_address(request->engine);
+	*cs++ = 0;
+
+	return cs;
+}
+
 static u32 *gen8_emit_fini_breadcrumb(struct i915_request *request, u32 *cs)
 {
 	cs = gen8_emit_ggtt_write(cs,
 				  request->fence.seqno,
 				  request->timeline->hwsp_offset,
 				  0);
-
 	*cs++ = MI_USER_INTERRUPT;
+
 	*cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
+	cs = emit_preempt_busywait(request, cs);
 
 	request->tail = intel_ring_offset(request, cs);
 	assert_ring_tail_valid(request->ring, request->tail);
@@ -2543,9 +2495,10 @@ static u32 *gen8_emit_fini_breadcrumb_rcs(struct i915_request *request, u32 *cs)
 				    PIPE_CONTROL_FLUSH_ENABLE |
 				    PIPE_CONTROL_CS_STALL,
 				    0);
-
 	*cs++ = MI_USER_INTERRUPT;
+
 	*cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
+	cs = emit_preempt_busywait(request, cs);
 
 	request->tail = intel_ring_offset(request, cs);
 	assert_ring_tail_valid(request->ring, request->tail);
@@ -2594,8 +2547,7 @@ void intel_execlists_set_default_submission(struct intel_engine_cs *engine)
 	engine->flags |= I915_ENGINE_SUPPORTS_STATS;
 	if (!intel_vgpu_active(engine->i915))
 		engine->flags |= I915_ENGINE_HAS_SEMAPHORES;
-	if (engine->preempt_context &&
-	    HAS_LOGICAL_RING_PREEMPTION(engine->i915))
+	if (HAS_LOGICAL_RING_PREEMPTION(engine->i915))
 		engine->flags |= I915_ENGINE_HAS_PREEMPTION;
 }
 
@@ -2718,11 +2670,6 @@ int intel_execlists_submission_init(struct intel_engine_cs *engine)
 			i915_mmio_reg_offset(RING_ELSP(base));
 	}
 
-	execlists->preempt_complete_status = ~0u;
-	if (engine->preempt_context)
-		execlists->preempt_complete_status =
-			upper_32_bits(engine->preempt_context->lrc_desc);
-
 	execlists->csb_status =
 		&engine->status_page.addr[I915_HWS_CSB_BUF0_INDEX];
 
@@ -2734,7 +2681,7 @@ int intel_execlists_submission_init(struct intel_engine_cs *engine)
 	else
 		execlists->csb_size = GEN11_CSB_ENTRIES;
 
-	reset_csb_pointers(execlists);
+	reset_csb_pointers(engine);
 
 	return 0;
 }
@@ -2917,11 +2864,6 @@ populate_lr_context(struct intel_context *ce,
 	if (!engine->default_state)
 		regs[CTX_CONTEXT_CONTROL + 1] |=
 			_MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT);
-	if (ce->gem_context == engine->i915->preempt_context &&
-	    INTEL_GEN(engine->i915) < 11)
-		regs[CTX_CONTEXT_CONTROL + 1] |=
-			_MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT |
-					   CTX_CTRL_ENGINE_CTX_SAVE_INHIBIT);
 
 	ret = 0;
 err_unpin_ctx:
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index b7e9fddef270..a497cf7acb6a 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -1248,10 +1248,10 @@ static void error_record_engine_registers(struct i915_gpu_state *error,
 	}
 }
 
-static void record_request(struct i915_request *request,
+static void record_request(const struct i915_request *request,
 			   struct drm_i915_error_request *erq)
 {
-	struct i915_gem_context *ctx = request->gem_context;
+	const struct i915_gem_context *ctx = request->gem_context;
 
 	erq->flags = request->fence.flags;
 	erq->context = request->fence.context;
@@ -1315,20 +1315,15 @@ static void engine_record_requests(struct intel_engine_cs *engine,
 	ee->num_requests = count;
 }
 
-static void error_record_engine_execlists(struct intel_engine_cs *engine,
+static void error_record_engine_execlists(const struct intel_engine_cs *engine,
 					  struct drm_i915_error_engine *ee)
 {
 	const struct intel_engine_execlists * const execlists = &engine->execlists;
-	unsigned int n;
+	struct i915_request * const *port = execlists->active;
+	unsigned int n = 0;
 
-	for (n = 0; n < execlists_num_ports(execlists); n++) {
-		struct i915_request *rq = port_request(&execlists->port[n]);
-
-		if (!rq)
-			break;
-
-		record_request(rq, &ee->execlist[n]);
-	}
+	while (*port)
+		record_request(*port++, &ee->execlist[n++]);
 
 	ee->num_ports = n;
 }
diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
index 7083e6ab92c5..0c99694faab7 100644
--- a/drivers/gpu/drm/i915/i915_request.c
+++ b/drivers/gpu/drm/i915/i915_request.c
@@ -276,6 +276,12 @@ static bool i915_request_retire(struct i915_request *rq)
 
 	local_irq_disable();
 
+	/*
+	 * We only loosely track inflight requests across preemption,
+	 * and so we may find ourselves attempting to retire a _completed_
+	 * request that we have removed from the HW and put back on a run
+	 * queue.
+	 */
 	spin_lock(&rq->engine->active.lock);
 	list_del(&rq->sched.link);
 	spin_unlock(&rq->engine->active.lock);
diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
index edbbdfec24ab..bebc1e9b4a5e 100644
--- a/drivers/gpu/drm/i915/i915_request.h
+++ b/drivers/gpu/drm/i915/i915_request.h
@@ -28,6 +28,7 @@
 #include <linux/dma-fence.h>
 #include <linux/lockdep.h>
 
+#include "gt/intel_context_types.h"
 #include "gt/intel_engine_types.h"
 
 #include "i915_gem.h"
diff --git a/drivers/gpu/drm/i915/i915_scheduler.c b/drivers/gpu/drm/i915/i915_scheduler.c
index 2e9b38bdc33c..b1ba3e65cd52 100644
--- a/drivers/gpu/drm/i915/i915_scheduler.c
+++ b/drivers/gpu/drm/i915/i915_scheduler.c
@@ -179,8 +179,7 @@ static inline int rq_prio(const struct i915_request *rq)
 
 static void kick_submission(struct intel_engine_cs *engine, int prio)
 {
-	const struct i915_request *inflight =
-		port_request(engine->execlists.port);
+	const struct i915_request *inflight = *engine->execlists.active;
 
 	/*
 	 * If we are already the currently executing context, don't
diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
index 2987219a6300..4920ff9aba62 100644
--- a/drivers/gpu/drm/i915/i915_utils.h
+++ b/drivers/gpu/drm/i915/i915_utils.h
@@ -131,6 +131,18 @@ __check_struct_size(size_t base, size_t arr, size_t count, size_t *size)
 	((typeof(ptr))((unsigned long)(ptr) | __bits));			\
 })
 
+#define ptr_count_dec(p_ptr) do {					\
+	typeof(p_ptr) __p = (p_ptr);					\
+	unsigned long __v = (unsigned long)(*__p);			\
+	*__p = (typeof(*p_ptr))(--__v);					\
+} while (0)
+
+#define ptr_count_inc(p_ptr) do {					\
+	typeof(p_ptr) __p = (p_ptr);					\
+	unsigned long __v = (unsigned long)(*__p);			\
+	*__p = (typeof(*p_ptr))(++__v);					\
+} while (0)
+
 #define page_mask_bits(ptr) ptr_mask_bits(ptr, PAGE_SHIFT)
 #define page_unmask_bits(ptr) ptr_unmask_bits(ptr, PAGE_SHIFT)
 #define page_pack_bits(ptr, bits) ptr_pack_bits(ptr, bits, PAGE_SHIFT)
diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c
index db531ebc7704..12c22359fdac 100644
--- a/drivers/gpu/drm/i915/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/intel_guc_submission.c
@@ -32,7 +32,11 @@
 #include "intel_guc_submission.h"
 #include "i915_drv.h"
 
-#define GUC_PREEMPT_FINISHED		0x1
+enum {
+	GUC_PREEMPT_NONE = 0,
+	GUC_PREEMPT_INPROGRESS,
+	GUC_PREEMPT_FINISHED,
+};
 #define GUC_PREEMPT_BREADCRUMB_DWORDS	0x8
 #define GUC_PREEMPT_BREADCRUMB_BYTES	\
 	(sizeof(u32) * GUC_PREEMPT_BREADCRUMB_DWORDS)
@@ -537,15 +541,11 @@ static void guc_add_request(struct intel_guc *guc, struct i915_request *rq)
 	u32 ctx_desc = lower_32_bits(rq->hw_context->lrc_desc);
 	u32 ring_tail = intel_ring_set_tail(rq->ring, rq->tail) / sizeof(u64);
 
-	spin_lock(&client->wq_lock);
-
 	guc_wq_item_append(client, engine->guc_id, ctx_desc,
 			   ring_tail, rq->fence.seqno);
 	guc_ring_doorbell(client);
 
 	client->submissions[engine->id] += 1;
-
-	spin_unlock(&client->wq_lock);
 }
 
 /*
@@ -631,8 +631,9 @@ static void inject_preempt_context(struct work_struct *work)
 	data[6] = intel_guc_ggtt_offset(guc, guc->shared_data);
 
 	if (WARN_ON(intel_guc_send(guc, data, ARRAY_SIZE(data)))) {
-		execlists_clear_active(&engine->execlists,
-				       EXECLISTS_ACTIVE_PREEMPT);
+		intel_write_status_page(engine,
+					I915_GEM_HWS_PREEMPT,
+					GUC_PREEMPT_NONE);
 		tasklet_schedule(&engine->execlists.tasklet);
 	}
 
@@ -672,8 +673,6 @@ static void complete_preempt_context(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists *execlists = &engine->execlists;
 
-	GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT));
-
 	if (inject_preempt_hang(execlists))
 		return;
 
@@ -681,89 +680,90 @@ static void complete_preempt_context(struct intel_engine_cs *engine)
 	execlists_unwind_incomplete_requests(execlists);
 
 	wait_for_guc_preempt_report(engine);
-	intel_write_status_page(engine, I915_GEM_HWS_PREEMPT, 0);
+	intel_write_status_page(engine, I915_GEM_HWS_PREEMPT, GUC_PREEMPT_NONE);
 }
 
-/**
- * guc_submit() - Submit commands through GuC
- * @engine: engine associated with the commands
- *
- * The only error here arises if the doorbell hardware isn't functioning
- * as expected, which really shouln't happen.
- */
-static void guc_submit(struct intel_engine_cs *engine)
+static void guc_submit(struct intel_engine_cs *engine,
+		       struct i915_request **out,
+		       struct i915_request **end)
 {
 	struct intel_guc *guc = &engine->i915->guc;
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
-	unsigned int n;
+	struct intel_guc_client *client = guc->execbuf_client;
 
-	for (n = 0; n < execlists_num_ports(execlists); n++) {
-		struct i915_request *rq;
-		unsigned int count;
+	spin_lock(&client->wq_lock);
 
-		rq = port_unpack(&port[n], &count);
-		if (rq && count == 0) {
-			port_set(&port[n], port_pack(rq, ++count));
+	do {
+		struct i915_request *rq = *out++;
 
-			flush_ggtt_writes(rq->ring->vma);
+		flush_ggtt_writes(rq->ring->vma);
+		guc_add_request(guc, rq);
+	} while (out != end);
 
-			guc_add_request(guc, rq);
-		}
-	}
+	spin_unlock(&client->wq_lock);
 }
 
-static void port_assign(struct execlist_port *port, struct i915_request *rq)
+static inline int rq_prio(const struct i915_request *rq)
 {
-	GEM_BUG_ON(port_isset(port));
-
-	port_set(port, i915_request_get(rq));
+	return rq->sched.attr.priority | __NO_PREEMPTION;
 }
 
-static inline int rq_prio(const struct i915_request *rq)
+static struct i915_request *schedule_in(struct i915_request *rq, int idx)
 {
-	return rq->sched.attr.priority;
+	trace_i915_request_in(rq, idx);
+
+	if (!rq->hw_context->inflight)
+		rq->hw_context->inflight = rq->engine;
+	intel_context_inflight_inc(rq->hw_context);
+
+	return i915_request_get(rq);
 }
 
-static inline int port_prio(const struct execlist_port *port)
+static void schedule_out(struct i915_request *rq)
 {
-	return rq_prio(port_request(port)) | __NO_PREEMPTION;
+	trace_i915_request_out(rq);
+
+	intel_context_inflight_dec(rq->hw_context);
+	if (!intel_context_inflight_count(rq->hw_context))
+		rq->hw_context->inflight = NULL;
+
+	i915_request_put(rq);
 }
 
-static bool __guc_dequeue(struct intel_engine_cs *engine)
+static void __guc_dequeue(struct intel_engine_cs *engine)
 {
 	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
-	struct i915_request *last = NULL;
-	const struct execlist_port * const last_port =
-		&execlists->port[execlists->port_mask];
+	struct i915_request **first = execlists->inflight;
+	struct i915_request ** const last_port = first + execlists->port_mask;
+	struct i915_request *last = first[0];
+	struct i915_request **port;
 	bool submit = false;
 	struct rb_node *rb;
 
 	lockdep_assert_held(&engine->active.lock);
 
-	if (port_isset(port)) {
+	if (last) {
 		if (intel_engine_has_preemption(engine)) {
 			struct guc_preempt_work *preempt_work =
 				&engine->i915->guc.preempt_work[engine->id];
 			int prio = execlists->queue_priority_hint;
 
-			if (i915_scheduler_need_preempt(prio,
-							port_prio(port))) {
-				execlists_set_active(execlists,
-						     EXECLISTS_ACTIVE_PREEMPT);
+			if (i915_scheduler_need_preempt(prio, rq_prio(last))) {
+				intel_write_status_page(engine,
+							I915_GEM_HWS_PREEMPT,
+							GUC_PREEMPT_INPROGRESS);
 				queue_work(engine->i915->guc.preempt_wq,
 					   &preempt_work->work);
-				return false;
+				return;
 			}
 		}
 
-		port++;
-		if (port_isset(port))
-			return false;
+		if (*++first)
+			return;
+
+		last = NULL;
 	}
-	GEM_BUG_ON(port_isset(port));
 
+	port = first;
 	while ((rb = rb_first_cached(&execlists->queue))) {
 		struct i915_priolist *p = to_priolist(rb);
 		struct i915_request *rq, *rn;
@@ -774,18 +774,15 @@ static bool __guc_dequeue(struct intel_engine_cs *engine)
 				if (port == last_port)
 					goto done;
 
-				if (submit)
-					port_assign(port, last);
+				*port = schedule_in(last,
+						    port - execlists->inflight);
 				port++;
 			}
 
 			list_del_init(&rq->sched.link);
-
 			__i915_request_submit(rq);
-			trace_i915_request_in(rq, port_index(port, execlists));
-
-			last = rq;
 			submit = true;
+			last = rq;
 		}
 
 		rb_erase_cached(&p->node, &execlists->queue);
@@ -794,58 +791,41 @@ static bool __guc_dequeue(struct intel_engine_cs *engine)
 done:
 	execlists->queue_priority_hint =
 		rb ? to_priolist(rb)->priority : INT_MIN;
-	if (submit)
-		port_assign(port, last);
-	if (last)
-		execlists_user_begin(execlists, execlists->port);
-
-	/* We must always keep the beast fed if we have work piled up */
-	GEM_BUG_ON(port_isset(execlists->port) &&
-		   !execlists_is_active(execlists, EXECLISTS_ACTIVE_USER));
-	GEM_BUG_ON(rb_first_cached(&execlists->queue) &&
-		   !port_isset(execlists->port));
-
-	return submit;
-}
-
-static void guc_dequeue(struct intel_engine_cs *engine)
-{
-	if (__guc_dequeue(engine))
-		guc_submit(engine);
+	if (submit) {
+		*port = schedule_in(last, port - execlists->inflight);
+		*++port = NULL;
+		guc_submit(engine, first, port);
+	}
+	execlists->active = execlists->inflight;
 }
 
 static void guc_submission_tasklet(unsigned long data)
 {
 	struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
 	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct execlist_port *port = execlists->port;
-	struct i915_request *rq;
+	struct i915_request **port, *rq;
 	unsigned long flags;
 
 	spin_lock_irqsave(&engine->active.lock, flags);
 
-	rq = port_request(port);
-	while (rq && i915_request_completed(rq)) {
-		trace_i915_request_out(rq);
-		i915_request_put(rq);
+	for (port = execlists->inflight; (rq = *port); port++) {
+		if (!i915_request_completed(rq))
+			break;
 
-		port = execlists_port_complete(execlists, port);
-		if (port_isset(port)) {
-			execlists_user_begin(execlists, port);
-			rq = port_request(port);
-		} else {
-			execlists_user_end(execlists);
-			rq = NULL;
-		}
+		schedule_out(rq);
+	}
+	if (port != execlists->inflight) {
+		int idx = port - execlists->inflight;
+		int rem = ARRAY_SIZE(execlists->inflight) - idx;
+		memmove(execlists->inflight, port, rem * sizeof(*port));
 	}
 
-	if (execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT) &&
-	    intel_read_status_page(engine, I915_GEM_HWS_PREEMPT) ==
+	if (intel_read_status_page(engine, I915_GEM_HWS_PREEMPT) ==
 	    GUC_PREEMPT_FINISHED)
 		complete_preempt_context(engine);
 
-	if (!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT))
-		guc_dequeue(engine);
+	if (!intel_read_status_page(engine, I915_GEM_HWS_PREEMPT))
+		__guc_dequeue(engine);
 
 	spin_unlock_irqrestore(&engine->active.lock, flags);
 }
@@ -959,7 +939,6 @@ static void guc_cancel_requests(struct intel_engine_cs *engine)
 
 	execlists->queue_priority_hint = INT_MIN;
 	execlists->queue = RB_ROOT_CACHED;
-	GEM_BUG_ON(port_isset(execlists->port));
 
 	spin_unlock_irqrestore(&engine->active.lock, flags);
 }
@@ -1422,7 +1401,7 @@ int intel_guc_submission_enable(struct intel_guc *guc)
 	 * and it is guaranteed that it will remove the work item from the
 	 * queue before our request is completed.
 	 */
-	BUILD_BUG_ON(ARRAY_SIZE(engine->execlists.port) *
+	BUILD_BUG_ON(ARRAY_SIZE(engine->execlists.inflight) *
 		     sizeof(struct guc_wq_item) *
 		     I915_NUM_ENGINES > GUC_WQ_SIZE);
 
diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c
index 298bb7116c51..1a5b9e284ca9 100644
--- a/drivers/gpu/drm/i915/selftests/i915_request.c
+++ b/drivers/gpu/drm/i915/selftests/i915_request.c
@@ -366,13 +366,15 @@ static int __igt_breadcrumbs_smoketest(void *arg)
 
 		if (!wait_event_timeout(wait->wait,
 					i915_sw_fence_done(wait),
-					HZ / 2)) {
+					5 * HZ)) {
 			struct i915_request *rq = requests[count - 1];
 
-			pr_err("waiting for %d fences (last %llx:%lld) on %s timed out!\n",
-			       count,
+			pr_err("waiting for %d/%d fences (last %llx:%lld) on %s timed out!\n",
+			       atomic_read(&wait->pending), count,
 			       rq->fence.context, rq->fence.seqno,
 			       t->engine->name);
+			GEM_TRACE_DUMP();
+
 			i915_gem_set_wedged(t->engine->i915);
 			GEM_BUG_ON(!i915_request_completed(rq));
 			i915_sw_fence_wait(wait);
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* ✗ Fi.CI.CHECKPATCH: warning for series starting with drm/i915/execlists: Preempt-to-busy (rev2)
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
                   ` (5 preceding siblings ...)
  2019-06-20  8:24 ` [PATCH] " Chris Wilson
@ 2019-06-20  9:23 ` Patchwork
  2019-06-20  9:25 ` ✗ Fi.CI.SPARSE: " Patchwork
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Patchwork @ 2019-06-20  9:23 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with drm/i915/execlists: Preempt-to-busy (rev2)
URL   : https://patchwork.freedesktop.org/series/62431/
State : warning

== Summary ==

$ dim checkpatch origin/drm-tip
4769517df2d7 drm/i915/execlists: Preempt-to-busy
-:1505: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'p_ptr' - possible side-effects?
#1505: FILE: drivers/gpu/drm/i915/i915_utils.h:134:
+#define ptr_count_dec(p_ptr) do {					\
+	typeof(p_ptr) __p = (p_ptr);					\
+	unsigned long __v = (unsigned long)(*__p);			\
+	*__p = (typeof(*p_ptr))(--__v);					\
+} while (0)

-:1511: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'p_ptr' - possible side-effects?
#1511: FILE: drivers/gpu/drm/i915/i915_utils.h:140:
+#define ptr_count_inc(p_ptr) do {					\
+	typeof(p_ptr) __p = (p_ptr);					\
+	unsigned long __v = (unsigned long)(*__p);			\
+	*__p = (typeof(*p_ptr))(++__v);					\
+} while (0)

-:1794: WARNING:LINE_SPACING: Missing a blank line after declarations
#1794: FILE: drivers/gpu/drm/i915/intel_guc_submission.c:820:
+		int rem = ARRAY_SIZE(execlists->inflight) - idx;
+		memmove(execlists->inflight, port, rem * sizeof(*port));

total: 0 errors, 1 warnings, 2 checks, 1693 lines checked
0b5d7fbe498b drm/i915/execlists: Minimalistic timeslicing
-:345: WARNING:LONG_LINE: line over 100 characters
#345: FILE: drivers/gpu/drm/i915/gt/selftest_lrc.c:211:
+			      2 * RUNTIME_INFO(outer->i915)->num_engines * (count + 2) * (count + 3)) < 0) {

total: 0 errors, 1 warnings, 0 checks, 426 lines checked
bfa0c571526c drm/i915/execlists: Force preemption

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* ✗ Fi.CI.SPARSE: warning for series starting with drm/i915/execlists: Preempt-to-busy (rev2)
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
                   ` (6 preceding siblings ...)
  2019-06-20  9:23 ` ✗ Fi.CI.CHECKPATCH: warning for series starting with drm/i915/execlists: Preempt-to-busy (rev2) Patchwork
@ 2019-06-20  9:25 ` Patchwork
  2019-06-20 12:52 ` ✓ Fi.CI.BAT: success " Patchwork
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Patchwork @ 2019-06-20  9:25 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with drm/i915/execlists: Preempt-to-busy (rev2)
URL   : https://patchwork.freedesktop.org/series/62431/
State : warning

== Summary ==

$ dim sparse origin/drm-tip
Sparse version: v0.5.2
Commit: drm/i915/execlists: Preempt-to-busy
-drivers/gpu/drm/i915/selftests/../i915_utils.h:220:16: warning: expression using sizeof(void)
+drivers/gpu/drm/i915/selftests/../i915_utils.h:232:16: warning: expression using sizeof(void)

Commit: drm/i915/execlists: Minimalistic timeslicing
+drivers/gpu/drm/i915/gt/intel_lrc.c:881:16: warning: expression using sizeof(void)
+drivers/gpu/drm/i915/gt/intel_lrc.c:881:16: warning: expression using sizeof(void)

Commit: drm/i915/execlists: Force preemption
+
+drivers/gpu/drm/i915/i915_utils.h:232:16: warning: expression using sizeof(void)
+Error in reading or end of file.

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] drm/i915/execlists: Preempt-to-busy
  2019-06-20  8:24 ` [PATCH] " Chris Wilson
@ 2019-06-20 12:41   ` Mika Kuoppala
  2019-06-20 13:01     ` Chris Wilson
  0 siblings, 1 reply; 23+ messages in thread
From: Mika Kuoppala @ 2019-06-20 12:41 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx

Chris Wilson <chris@chris-wilson.co.uk> writes:

> When using a global seqno, we required a precise stop-the-workd event to
> handle preemption and unwind the global seqno counter. To accomplish
> this, we would preempt to a special out-of-band context and wait for the
> machine to report that it was idle. Given an idle machine, we could very
> precisely see which requests had completed and which we needed to feed
> back into the run queue.
>
> However, now that we have scrapped the global seqno, we no longer need
> to precisely unwind the global counter and only track requests by their
> per-context seqno. This allows us to loosely unwind inflight requests
> while scheduling a preemption, with the enormous caveat that the
> requests we put back on the run queue are still _inflight_ (until the
> preemption request is complete). This makes request tracking much more
> messy, as at any point then we can see a completed request that we
> believe is not currently scheduled for execution. We also have to be
> careful not to rewind RING_TAIL past RING_HEAD on preempting to the
> running context, and for this we use a semaphore to prevent completion
> of the request before continuing.
>
> To accomplish this feat, we change how we track requests scheduled to
> the HW. Instead of appending our requests onto a single list as we
> submit, we track each submission to ELSP as its own block. Then upon
> receiving the CS preemption event, we promote the pending block to the
> inflight block (discarding what was previously being tracked). As normal
> CS completion events arrive, we then remove stale entries from the
> inflight tracker.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
> Skip the bonus asserts if the context is already completed.
> ---
>  drivers/gpu/drm/i915/gem/i915_gem_context.c   |   2 +-
>  drivers/gpu/drm/i915/gt/intel_context_types.h |   5 +
>  drivers/gpu/drm/i915/gt/intel_engine.h        |  61 +-
>  drivers/gpu/drm/i915/gt/intel_engine_cs.c     |  63 +-
>  drivers/gpu/drm/i915/gt/intel_engine_types.h  |  52 +-
>  drivers/gpu/drm/i915/gt/intel_lrc.c           | 678 ++++++++----------
>  drivers/gpu/drm/i915/i915_gpu_error.c         |  19 +-
>  drivers/gpu/drm/i915/i915_request.c           |   6 +
>  drivers/gpu/drm/i915/i915_request.h           |   1 +
>  drivers/gpu/drm/i915/i915_scheduler.c         |   3 +-
>  drivers/gpu/drm/i915/i915_utils.h             |  12 +
>  drivers/gpu/drm/i915/intel_guc_submission.c   | 175 ++---
>  drivers/gpu/drm/i915/selftests/i915_request.c |   8 +-
>  13 files changed, 475 insertions(+), 610 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> index 0f2c22a3bcb6..35871c8a42a6 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> @@ -646,7 +646,7 @@ static void init_contexts(struct drm_i915_private *i915)
>  
>  static bool needs_preempt_context(struct drm_i915_private *i915)
>  {
> -	return HAS_EXECLISTS(i915);
> +	return USES_GUC_SUBMISSION(i915);
>  }
>  
>  int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
> diff --git a/drivers/gpu/drm/i915/gt/intel_context_types.h b/drivers/gpu/drm/i915/gt/intel_context_types.h
> index 08049ee91cee..4c0e211c715d 100644
> --- a/drivers/gpu/drm/i915/gt/intel_context_types.h
> +++ b/drivers/gpu/drm/i915/gt/intel_context_types.h
> @@ -13,6 +13,7 @@
>  #include <linux/types.h>
>  
>  #include "i915_active_types.h"
> +#include "i915_utils.h"
>  #include "intel_engine_types.h"
>  #include "intel_sseu.h"
>  
> @@ -38,6 +39,10 @@ struct intel_context {
>  	struct i915_gem_context *gem_context;
>  	struct intel_engine_cs *engine;
>  	struct intel_engine_cs *inflight;
> +#define intel_context_inflight(ce) ptr_mask_bits((ce)->inflight, 2)
> +#define intel_context_inflight_count(ce)  ptr_unmask_bits((ce)->inflight, 2)
> +#define intel_context_inflight_inc(ce) ptr_count_inc(&(ce)->inflight)
> +#define intel_context_inflight_dec(ce) ptr_count_dec(&(ce)->inflight)

Just curious here that what you consider the advantages of carrying
this info with the pointer?

>  
>  	struct list_head signal_link;
>  	struct list_head signals;
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine.h b/drivers/gpu/drm/i915/gt/intel_engine.h
> index 2f1c6871ee95..9bb6ff76680e 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine.h
> +++ b/drivers/gpu/drm/i915/gt/intel_engine.h
> @@ -125,71 +125,26 @@ hangcheck_action_to_str(const enum intel_engine_hangcheck_action a)
>  
>  void intel_engines_set_scheduler_caps(struct drm_i915_private *i915);
>  
> -static inline void
> -execlists_set_active(struct intel_engine_execlists *execlists,
> -		     unsigned int bit)
> -{
> -	__set_bit(bit, (unsigned long *)&execlists->active);
> -}
> -
> -static inline bool
> -execlists_set_active_once(struct intel_engine_execlists *execlists,
> -			  unsigned int bit)
> -{
> -	return !__test_and_set_bit(bit, (unsigned long *)&execlists->active);
> -}
> -
> -static inline void
> -execlists_clear_active(struct intel_engine_execlists *execlists,
> -		       unsigned int bit)
> -{
> -	__clear_bit(bit, (unsigned long *)&execlists->active);
> -}
> -
> -static inline void
> -execlists_clear_all_active(struct intel_engine_execlists *execlists)
> +static inline unsigned int
> +execlists_num_ports(const struct intel_engine_execlists * const execlists)
>  {
> -	execlists->active = 0;
> +	return execlists->port_mask + 1;
>  }
>  
> -static inline bool
> -execlists_is_active(const struct intel_engine_execlists *execlists,
> -		    unsigned int bit)
> +static inline struct i915_request *
> +execlists_active(const struct intel_engine_execlists *execlists)
>  {
> -	return test_bit(bit, (unsigned long *)&execlists->active);
> +	GEM_BUG_ON(execlists->active - execlists->inflight >
> +		   execlists_num_ports(execlists));
> +	return READ_ONCE(*execlists->active);
>  }
>  
> -void execlists_user_begin(struct intel_engine_execlists *execlists,
> -			  const struct execlist_port *port);
> -void execlists_user_end(struct intel_engine_execlists *execlists);
> -
>  void
>  execlists_cancel_port_requests(struct intel_engine_execlists * const execlists);
>  
>  struct i915_request *
>  execlists_unwind_incomplete_requests(struct intel_engine_execlists *execlists);
>  
> -static inline unsigned int
> -execlists_num_ports(const struct intel_engine_execlists * const execlists)
> -{
> -	return execlists->port_mask + 1;
> -}
> -
> -static inline struct execlist_port *
> -execlists_port_complete(struct intel_engine_execlists * const execlists,
> -			struct execlist_port * const port)
> -{
> -	const unsigned int m = execlists->port_mask;
> -
> -	GEM_BUG_ON(port_index(port, execlists) != 0);
> -	GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_USER));
> -
> -	memmove(port, port + 1, m * sizeof(struct execlist_port));
> -	memset(port + m, 0, sizeof(struct execlist_port));
> -
> -	return port;
> -}
> -
>  static inline u32
>  intel_read_status_page(const struct intel_engine_cs *engine, int reg)
>  {
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
> index 7fd33e81c2d9..d45328e254dc 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c
> +++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
> @@ -508,6 +508,10 @@ void intel_engine_init_execlists(struct intel_engine_cs *engine)
>  	GEM_BUG_ON(!is_power_of_2(execlists_num_ports(execlists)));
>  	GEM_BUG_ON(execlists_num_ports(execlists) > EXECLIST_MAX_PORTS);
>  
> +	memset(execlists->pending, 0, sizeof(execlists->pending));
> +	execlists->active =
> +		memset(execlists->inflight, 0, sizeof(execlists->inflight));
> +
>  	execlists->queue_priority_hint = INT_MIN;
>  	execlists->queue = RB_ROOT_CACHED;
>  }
> @@ -1152,7 +1156,7 @@ bool intel_engine_is_idle(struct intel_engine_cs *engine)
>  		return true;
>  
>  	/* Waiting to drain ELSP? */
> -	if (READ_ONCE(engine->execlists.active)) {
> +	if (execlists_active(&engine->execlists)) {
>  		struct tasklet_struct *t = &engine->execlists.tasklet;
>  
>  		synchronize_hardirq(engine->i915->drm.irq);
> @@ -1169,7 +1173,7 @@ bool intel_engine_is_idle(struct intel_engine_cs *engine)
>  		/* Otherwise flush the tasklet if it was on another cpu */
>  		tasklet_unlock_wait(t);
>  
> -		if (READ_ONCE(engine->execlists.active))
> +		if (execlists_active(&engine->execlists))
>  			return false;
>  	}
>  
> @@ -1367,6 +1371,7 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine,
>  	}
>  
>  	if (HAS_EXECLISTS(dev_priv)) {
> +		struct i915_request * const *port, *rq;
>  		const u32 *hws =
>  			&engine->status_page.addr[I915_HWS_CSB_BUF0_INDEX];
>  		const u8 num_entries = execlists->csb_size;
> @@ -1399,27 +1404,33 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine,
>  		}
>  
>  		spin_lock_irqsave(&engine->active.lock, flags);
> -		for (idx = 0; idx < execlists_num_ports(execlists); idx++) {
> -			struct i915_request *rq;
> -			unsigned int count;
> +		for (port = execlists->active; (rq = *port); port++) {
> +			char hdr[80];
> +			int len;
> +
> +			len = snprintf(hdr, sizeof(hdr),
> +				       "\t\tActive[%d: ",
> +				       (int)(port - execlists->active));
> +			if (!i915_request_signaled(rq))
> +				len += snprintf(hdr + len, sizeof(hdr) - len,
> +						"ring:{start:%08x, hwsp:%08x, seqno:%08x}, ",
> +						i915_ggtt_offset(rq->ring->vma),
> +						rq->timeline->hwsp_offset,
> +						hwsp_seqno(rq));
> +			snprintf(hdr + len, sizeof(hdr) - len, "rq: ");
> +			print_request(m, rq, hdr);
> +		}
> +		for (port = execlists->pending; (rq = *port); port++) {
>  			char hdr[80];
>  
> -			rq = port_unpack(&execlists->port[idx], &count);
> -			if (!rq) {
> -				drm_printf(m, "\t\tELSP[%d] idle\n", idx);
> -			} else if (!i915_request_signaled(rq)) {
> -				snprintf(hdr, sizeof(hdr),
> -					 "\t\tELSP[%d] count=%d, ring:{start:%08x, hwsp:%08x, seqno:%08x}, rq: ",
> -					 idx, count,
> -					 i915_ggtt_offset(rq->ring->vma),
> -					 rq->timeline->hwsp_offset,
> -					 hwsp_seqno(rq));
> -				print_request(m, rq, hdr);
> -			} else {
> -				print_request(m, rq, "\t\tELSP[%d] rq: ");
> -			}
> +			snprintf(hdr, sizeof(hdr),
> +				 "\t\tPending[%d] ring:{start:%08x, hwsp:%08x, seqno:%08x}, rq: ",
> +				 (int)(port - execlists->pending),
> +				 i915_ggtt_offset(rq->ring->vma),
> +				 rq->timeline->hwsp_offset,
> +				 hwsp_seqno(rq));
> +			print_request(m, rq, hdr);
>  		}
> -		drm_printf(m, "\t\tHW active? 0x%x\n", execlists->active);
>  		spin_unlock_irqrestore(&engine->active.lock, flags);
>  	} else if (INTEL_GEN(dev_priv) > 6) {
>  		drm_printf(m, "\tPP_DIR_BASE: 0x%08x\n",
> @@ -1583,15 +1594,19 @@ int intel_enable_engine_stats(struct intel_engine_cs *engine)
>  	}
>  
>  	if (engine->stats.enabled++ == 0) {
> -		const struct execlist_port *port = execlists->port;
> -		unsigned int num_ports = execlists_num_ports(execlists);
> +		struct i915_request * const *port;
> +		struct i915_request *rq;
>  
>  		engine->stats.enabled_at = ktime_get();
>  
>  		/* XXX submission method oblivious? */
> -		while (num_ports-- && port_isset(port)) {
> +		for (port = execlists->active; (rq = *port); port++)
>  			engine->stats.active++;
> -			port++;
> +
> +		for (port = execlists->pending; (rq = *port); port++) {
> +			/* Exclude any contexts already counted in active */
> +			if (intel_context_inflight_count(rq->hw_context) == 1)
> +				engine->stats.active++;
>  		}
>  
>  		if (engine->stats.active)
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine_types.h b/drivers/gpu/drm/i915/gt/intel_engine_types.h
> index 43e975a26016..411b7a807b99 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine_types.h
> +++ b/drivers/gpu/drm/i915/gt/intel_engine_types.h
> @@ -172,51 +172,10 @@ struct intel_engine_execlists {
>  	 */
>  	u32 __iomem *ctrl_reg;
>  
> -	/**
> -	 * @port: execlist port states
> -	 *
> -	 * For each hardware ELSP (ExecList Submission Port) we keep
> -	 * track of the last request and the number of times we submitted
> -	 * that port to hw. We then count the number of times the hw reports
> -	 * a context completion or preemption. As only one context can
> -	 * be active on hw, we limit resubmission of context to port[0]. This
> -	 * is called Lite Restore, of the context.
> -	 */
> -	struct execlist_port {
> -		/**
> -		 * @request_count: combined request and submission count
> -		 */
> -		struct i915_request *request_count;
> -#define EXECLIST_COUNT_BITS 2
> -#define port_request(p) ptr_mask_bits((p)->request_count, EXECLIST_COUNT_BITS)
> -#define port_count(p) ptr_unmask_bits((p)->request_count, EXECLIST_COUNT_BITS)
> -#define port_pack(rq, count) ptr_pack_bits(rq, count, EXECLIST_COUNT_BITS)
> -#define port_unpack(p, count) ptr_unpack_bits((p)->request_count, count, EXECLIST_COUNT_BITS)
> -#define port_set(p, packed) ((p)->request_count = (packed))
> -#define port_isset(p) ((p)->request_count)
> -#define port_index(p, execlists) ((p) - (execlists)->port)
> -
> -		/**
> -		 * @context_id: context ID for port
> -		 */
> -		GEM_DEBUG_DECL(u32 context_id);
> -
>  #define EXECLIST_MAX_PORTS 2
> -	} port[EXECLIST_MAX_PORTS];
> -
> -	/**
> -	 * @active: is the HW active? We consider the HW as active after
> -	 * submitting any context for execution and until we have seen the
> -	 * last context completion event. After that, we do not expect any
> -	 * more events until we submit, and so can park the HW.
> -	 *
> -	 * As we have a small number of different sources from which we feed
> -	 * the HW, we track the state of each inside a single bitfield.
> -	 */
> -	unsigned int active;
> -#define EXECLISTS_ACTIVE_USER 0
> -#define EXECLISTS_ACTIVE_PREEMPT 1
> -#define EXECLISTS_ACTIVE_HWACK 2
> +	struct i915_request * const *active;
> +	struct i915_request *inflight[EXECLIST_MAX_PORTS + 1 /* sentinel */];
> +	struct i915_request *pending[EXECLIST_MAX_PORTS + 1];
>  
>  	/**
>  	 * @port_mask: number of execlist ports - 1
> @@ -257,11 +216,6 @@ struct intel_engine_execlists {
>  	 */
>  	u32 *csb_status;
>  
> -	/**
> -	 * @preempt_complete_status: expected CSB upon completing preemption
> -	 */
> -	u32 preempt_complete_status;
> -
>  	/**
>  	 * @csb_size: context status buffer FIFO size
>  	 */
> diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
> index 82b7ace62d97..b1e45c651660 100644
> --- a/drivers/gpu/drm/i915/gt/intel_lrc.c
> +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
> @@ -161,6 +161,8 @@
>  #define GEN8_CTX_STATUS_COMPLETED_MASK \
>  	 (GEN8_CTX_STATUS_COMPLETE | GEN8_CTX_STATUS_PREEMPTED)
>  
> +#define CTX_DESC_FORCE_RESTORE BIT_ULL(2)
> +
>  /* Typical size of the average request (2 pipecontrols and a MI_BB) */
>  #define EXECLISTS_REQUEST_SIZE 64 /* bytes */
>  #define WA_TAIL_DWORDS 2
> @@ -221,6 +223,14 @@ static void execlists_init_reg_state(u32 *reg_state,
>  				     struct intel_engine_cs *engine,
>  				     struct intel_ring *ring);
>  
> +static inline u32 intel_hws_preempt_address(struct intel_engine_cs *engine)
> +{
> +	return (i915_ggtt_offset(engine->status_page.vma) +
> +		I915_GEM_HWS_PREEMPT_ADDR);
> +}
> +
> +#define ring_pause(E) ((E)->status_page.addr[I915_GEM_HWS_PREEMPT])

Scary. Please lets make a function of ring_pause and use
intel_write_status_page in it.

So I guess you have and you want squeeze the latency fruit.

When we have everything in place, CI is green and
everyone is happy, then we tear it down?

> +
>  static inline struct i915_priolist *to_priolist(struct rb_node *rb)
>  {
>  	return rb_entry(rb, struct i915_priolist, node);
> @@ -271,12 +281,6 @@ static inline bool need_preempt(const struct intel_engine_cs *engine,
>  {
>  	int last_prio;
>  
> -	if (!engine->preempt_context)
> -		return false;
> -
> -	if (i915_request_completed(rq))
> -		return false;
> -
>  	/*
>  	 * Check if the current priority hint merits a preemption attempt.
>  	 *
> @@ -338,9 +342,6 @@ __maybe_unused static inline bool
>  assert_priority_queue(const struct i915_request *prev,
>  		      const struct i915_request *next)
>  {
> -	const struct intel_engine_execlists *execlists =
> -		&prev->engine->execlists;
> -
>  	/*
>  	 * Without preemption, the prev may refer to the still active element
>  	 * which we refuse to let go.
> @@ -348,7 +349,7 @@ assert_priority_queue(const struct i915_request *prev,
>  	 * Even with preemption, there are times when we think it is better not
>  	 * to preempt and leave an ostensibly lower priority request in flight.
>  	 */
> -	if (port_request(execlists->port) == prev)
> +	if (i915_request_is_active(prev))
>  		return true;
>  
>  	return rq_prio(prev) >= rq_prio(next);
> @@ -442,13 +443,11 @@ __unwind_incomplete_requests(struct intel_engine_cs *engine)
>  		struct intel_engine_cs *owner;
>  
>  		if (i915_request_completed(rq))
> -			break;
> +			continue; /* XXX */

Yeah, but what is the plan with the XXX.


>  
>  		__i915_request_unsubmit(rq);
>  		unwind_wa_tail(rq);
>  
> -		GEM_BUG_ON(rq->hw_context->inflight);
> -
>  		/*
>  		 * Push the request back into the queue for later resubmission.
>  		 * If this request is not native to this physical engine (i.e.
> @@ -500,32 +499,32 @@ execlists_context_status_change(struct i915_request *rq, unsigned long status)
>  				   status, rq);
>  }
>  
> -inline void
> -execlists_user_begin(struct intel_engine_execlists *execlists,
> -		     const struct execlist_port *port)
> +static inline struct i915_request *
> +execlists_schedule_in(struct i915_request *rq, int idx)
>  {
> -	execlists_set_active_once(execlists, EXECLISTS_ACTIVE_USER);
> -}
> +	struct intel_context *ce = rq->hw_context;
> +	int count;
>  
> -inline void
> -execlists_user_end(struct intel_engine_execlists *execlists)
> -{
> -	execlists_clear_active(execlists, EXECLISTS_ACTIVE_USER);
> -}
> +	trace_i915_request_in(rq, idx);
>  
> -static inline void
> -execlists_context_schedule_in(struct i915_request *rq)
> -{
> -	GEM_BUG_ON(rq->hw_context->inflight);
> +	count = intel_context_inflight_count(ce);
> +	if (!count) {
> +		intel_context_get(ce);
> +		ce->inflight = rq->engine;
> +
> +		execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_IN);
> +		intel_engine_context_in(ce->inflight);
> +	}
>  
> -	execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_IN);
> -	intel_engine_context_in(rq->engine);
> -	rq->hw_context->inflight = rq->engine;
> +	intel_context_inflight_inc(ce);
> +	GEM_BUG_ON(intel_context_inflight(ce) != rq->engine);
> +
> +	return i915_request_get(rq);
>  }
>  
> -static void kick_siblings(struct i915_request *rq)
> +static void kick_siblings(struct i915_request *rq, struct intel_context *ce)
>  {
> -	struct virtual_engine *ve = to_virtual_engine(rq->hw_context->engine);
> +	struct virtual_engine *ve = container_of(ce, typeof(*ve), context);
>  	struct i915_request *next = READ_ONCE(ve->request);
>  
>  	if (next && next->execution_mask & ~rq->execution_mask)
> @@ -533,29 +532,42 @@ static void kick_siblings(struct i915_request *rq)
>  }
>  
>  static inline void
> -execlists_context_schedule_out(struct i915_request *rq, unsigned long status)
> +execlists_schedule_out(struct i915_request *rq)
>  {
> -	rq->hw_context->inflight = NULL;
> -	intel_engine_context_out(rq->engine);
> -	execlists_context_status_change(rq, status);
> +	struct intel_context *ce = rq->hw_context;
> +
> +	GEM_BUG_ON(!intel_context_inflight_count(ce));
> +
>  	trace_i915_request_out(rq);
>  
> -	/*
> -	 * If this is part of a virtual engine, its next request may have
> -	 * been blocked waiting for access to the active context. We have
> -	 * to kick all the siblings again in case we need to switch (e.g.
> -	 * the next request is not runnable on this engine). Hopefully,
> -	 * we will already have submitted the next request before the
> -	 * tasklet runs and do not need to rebuild each virtual tree
> -	 * and kick everyone again.
> -	 */
> -	if (rq->engine != rq->hw_context->engine)
> -		kick_siblings(rq);
> +	intel_context_inflight_dec(ce);
> +	if (!intel_context_inflight_count(ce)) {
> +		intel_engine_context_out(ce->inflight);
> +		execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_OUT);
> +
> +		ce->inflight = NULL;
> +		intel_context_put(ce);
> +
> +		/*
> +		 * If this is part of a virtual engine, its next request may
> +		 * have been blocked waiting for access to the active context.
> +		 * We have to kick all the siblings again in case we need to
> +		 * switch (e.g. the next request is not runnable on this
> +		 * engine). Hopefully, we will already have submitted the next
> +		 * request before the tasklet runs and do not need to rebuild
> +		 * each virtual tree and kick everyone again.
> +		 */
> +		if (rq->engine != ce->engine)
> +			kick_siblings(rq, ce);
> +	}
> +
> +	i915_request_put(rq);
>  }
>  
> -static u64 execlists_update_context(struct i915_request *rq)
> +static u64 execlists_update_context(const struct i915_request *rq)
>  {
>  	struct intel_context *ce = rq->hw_context;
> +	u64 desc;
>  
>  	ce->lrc_reg_state[CTX_RING_TAIL + 1] =
>  		intel_ring_set_tail(rq->ring, rq->tail);
> @@ -576,7 +588,11 @@ static u64 execlists_update_context(struct i915_request *rq)
>  	 * wmb).
>  	 */
>  	mb();
> -	return ce->lrc_desc;
> +
> +	desc = ce->lrc_desc;
> +	ce->lrc_desc &= ~CTX_DESC_FORCE_RESTORE;
> +
> +	return desc;
>  }
>  
>  static inline void write_desc(struct intel_engine_execlists *execlists, u64 desc, u32 port)
> @@ -590,12 +606,59 @@ static inline void write_desc(struct intel_engine_execlists *execlists, u64 desc
>  	}
>  }
>  
> +static __maybe_unused void
> +trace_ports(const struct intel_engine_execlists *execlists,
> +	    const char *msg,
> +	    struct i915_request * const *ports)
> +{
> +	const struct intel_engine_cs *engine =
> +		container_of(execlists, typeof(*engine), execlists);
> +
> +	GEM_TRACE("%s: %s { %llx:%lld%s, %llx:%lld }\n",
> +		  engine->name, msg,
> +		  ports[0]->fence.context,
> +		  ports[0]->fence.seqno,
> +		  i915_request_completed(ports[0]) ? "!" :
> +		  i915_request_started(ports[0]) ? "*" :
> +		  "",
> +		  ports[1] ? ports[1]->fence.context : 0,
> +		  ports[1] ? ports[1]->fence.seqno : 0);
> +}
> +
> +static __maybe_unused bool
> +assert_pending_valid(const struct intel_engine_execlists *execlists,
> +		     const char *msg)
> +{
> +	struct i915_request * const *port, *rq;
> +	struct intel_context *ce = NULL;
> +
> +	trace_ports(execlists, msg, execlists->pending);
> +
> +	if (execlists->pending[execlists_num_ports(execlists)])
> +		return false;
> +
> +	for (port = execlists->pending; (rq = *port); port++) {
> +		if (ce == rq->hw_context)
> +			return false;
> +
> +		ce = rq->hw_context;
> +		if (i915_request_completed(rq))
> +			continue;
> +
> +		GEM_BUG_ON(i915_active_is_idle(&ce->active));
> +		GEM_BUG_ON(!i915_vma_is_pinned(ce->state));
> +	}
> +
> +	return ce;
> +}
> +
>  static void execlists_submit_ports(struct intel_engine_cs *engine)
>  {
>  	struct intel_engine_execlists *execlists = &engine->execlists;
> -	struct execlist_port *port = execlists->port;
>  	unsigned int n;
>  
> +	GEM_BUG_ON(!assert_pending_valid(execlists, "submit"));
> +
>  	/*
>  	 * We can skip acquiring intel_runtime_pm_get() here as it was taken
>  	 * on our behalf by the request (see i915_gem_mark_busy()) and it will
> @@ -613,38 +676,16 @@ static void execlists_submit_ports(struct intel_engine_cs *engine)
>  	 * of elsq entries, keep this in mind before changing the loop below.
>  	 */
>  	for (n = execlists_num_ports(execlists); n--; ) {
> -		struct i915_request *rq;
> -		unsigned int count;
> -		u64 desc;
> +		struct i915_request *rq = execlists->pending[n];
>  
> -		rq = port_unpack(&port[n], &count);
> -		if (rq) {
> -			GEM_BUG_ON(count > !n);
> -			if (!count++)
> -				execlists_context_schedule_in(rq);
> -			port_set(&port[n], port_pack(rq, count));
> -			desc = execlists_update_context(rq);
> -			GEM_DEBUG_EXEC(port[n].context_id = upper_32_bits(desc));
> -
> -			GEM_TRACE("%s in[%d]:  ctx=%d.%d, fence %llx:%lld (current %d), prio=%d\n",
> -				  engine->name, n,
> -				  port[n].context_id, count,
> -				  rq->fence.context, rq->fence.seqno,
> -				  hwsp_seqno(rq),
> -				  rq_prio(rq));
> -		} else {
> -			GEM_BUG_ON(!n);
> -			desc = 0;
> -		}
> -
> -		write_desc(execlists, desc, n);
> +		write_desc(execlists,
> +			   rq ? execlists_update_context(rq) : 0,
> +			   n);
>  	}
>  
>  	/* we need to manually load the submit queue */
>  	if (execlists->ctrl_reg)
>  		writel(EL_CTRL_LOAD, execlists->ctrl_reg);
> -
> -	execlists_clear_active(execlists, EXECLISTS_ACTIVE_HWACK);
>  }
>  
>  static bool ctx_single_port_submission(const struct intel_context *ce)
> @@ -668,6 +709,7 @@ static bool can_merge_ctx(const struct intel_context *prev,
>  static bool can_merge_rq(const struct i915_request *prev,
>  			 const struct i915_request *next)
>  {
> +	GEM_BUG_ON(prev == next);
>  	GEM_BUG_ON(!assert_priority_queue(prev, next));
>  
>  	if (!can_merge_ctx(prev->hw_context, next->hw_context))
> @@ -676,58 +718,6 @@ static bool can_merge_rq(const struct i915_request *prev,
>  	return true;
>  }
>  
> -static void port_assign(struct execlist_port *port, struct i915_request *rq)
> -{
> -	GEM_BUG_ON(rq == port_request(port));
> -
> -	if (port_isset(port))
> -		i915_request_put(port_request(port));
> -
> -	port_set(port, port_pack(i915_request_get(rq), port_count(port)));
> -}
> -
> -static void inject_preempt_context(struct intel_engine_cs *engine)
> -{
> -	struct intel_engine_execlists *execlists = &engine->execlists;
> -	struct intel_context *ce = engine->preempt_context;
> -	unsigned int n;
> -
> -	GEM_BUG_ON(execlists->preempt_complete_status !=
> -		   upper_32_bits(ce->lrc_desc));
> -
> -	/*
> -	 * Switch to our empty preempt context so
> -	 * the state of the GPU is known (idle).
> -	 */
> -	GEM_TRACE("%s\n", engine->name);
> -	for (n = execlists_num_ports(execlists); --n; )
> -		write_desc(execlists, 0, n);
> -
> -	write_desc(execlists, ce->lrc_desc, n);
> -
> -	/* we need to manually load the submit queue */
> -	if (execlists->ctrl_reg)
> -		writel(EL_CTRL_LOAD, execlists->ctrl_reg);
> -
> -	execlists_clear_active(execlists, EXECLISTS_ACTIVE_HWACK);
> -	execlists_set_active(execlists, EXECLISTS_ACTIVE_PREEMPT);
> -
> -	(void)I915_SELFTEST_ONLY(execlists->preempt_hang.count++);
> -}
> -
> -static void complete_preempt_context(struct intel_engine_execlists *execlists)
> -{
> -	GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT));
> -
> -	if (inject_preempt_hang(execlists))
> -		return;
> -
> -	execlists_cancel_port_requests(execlists);
> -	__unwind_incomplete_requests(container_of(execlists,
> -						  struct intel_engine_cs,
> -						  execlists));
> -}
> -
>  static void virtual_update_register_offsets(u32 *regs,
>  					    struct intel_engine_cs *engine)
>  {
> @@ -792,7 +782,7 @@ static bool virtual_matches(const struct virtual_engine *ve,
>  	 * we reuse the register offsets). This is a very small
>  	 * hystersis on the greedy seelction algorithm.
>  	 */
> -	inflight = READ_ONCE(ve->context.inflight);
> +	inflight = intel_context_inflight(&ve->context);
>  	if (inflight && inflight != engine)
>  		return false;
>  
> @@ -815,13 +805,23 @@ static void virtual_xfer_breadcrumbs(struct virtual_engine *ve,
>  	spin_unlock(&old->breadcrumbs.irq_lock);
>  }
>  
> +static struct i915_request *
> +last_active(const struct intel_engine_execlists *execlists)
> +{
> +	struct i915_request * const *last = execlists->active;
> +
> +	while (*last && i915_request_completed(*last))
> +		last++;
> +
> +	return *last;
> +}
> +
>  static void execlists_dequeue(struct intel_engine_cs *engine)
>  {
>  	struct intel_engine_execlists * const execlists = &engine->execlists;
> -	struct execlist_port *port = execlists->port;
> -	const struct execlist_port * const last_port =
> -		&execlists->port[execlists->port_mask];
> -	struct i915_request *last = port_request(port);
> +	struct i915_request **port = execlists->pending;
> +	struct i915_request ** const last_port = port + execlists->port_mask;
> +	struct i915_request *last;
>  	struct rb_node *rb;
>  	bool submit = false;
>  
> @@ -867,65 +867,72 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>  		break;
>  	}
>  
> +	/*
> +	 * If the queue is higher priority than the last
> +	 * request in the currently active context, submit afresh.
> +	 * We will resubmit again afterwards in case we need to split
> +	 * the active context to interject the preemption request,
> +	 * i.e. we will retrigger preemption following the ack in case
> +	 * of trouble.
> +	 */
> +	last = last_active(execlists);
>  	if (last) {
> -		/*
> -		 * Don't resubmit or switch until all outstanding
> -		 * preemptions (lite-restore) are seen. Then we
> -		 * know the next preemption status we see corresponds
> -		 * to this ELSP update.
> -		 */
> -		GEM_BUG_ON(!execlists_is_active(execlists,
> -						EXECLISTS_ACTIVE_USER));
> -		GEM_BUG_ON(!port_count(&port[0]));
> -
> -		/*
> -		 * If we write to ELSP a second time before the HW has had
> -		 * a chance to respond to the previous write, we can confuse
> -		 * the HW and hit "undefined behaviour". After writing to ELSP,
> -		 * we must then wait until we see a context-switch event from
> -		 * the HW to indicate that it has had a chance to respond.
> -		 */
> -		if (!execlists_is_active(execlists, EXECLISTS_ACTIVE_HWACK))
> -			return;
> -
>  		if (need_preempt(engine, last, rb)) {
> -			inject_preempt_context(engine);
> -			return;
> -		}
> +			GEM_TRACE("%s: preempting last=%llx:%lld, prio=%d, hint=%d\n",
> +				  engine->name,
> +				  last->fence.context,
> +				  last->fence.seqno,
> +				  last->sched.attr.priority,
> +				  execlists->queue_priority_hint);
> +			/*
> +			 * Don't let the RING_HEAD advance past the breadcrumb
> +			 * as we unwind (and until we resubmit) so that we do
> +			 * not accidentally tell it to go backwards.
> +			 */
> +			ring_pause(engine) = 1;
>  
> -		/*
> -		 * In theory, we could coalesce more requests onto
> -		 * the second port (the first port is active, with
> -		 * no preemptions pending). However, that means we
> -		 * then have to deal with the possible lite-restore
> -		 * of the second port (as we submit the ELSP, there
> -		 * may be a context-switch) but also we may complete
> -		 * the resubmission before the context-switch. Ergo,
> -		 * coalescing onto the second port will cause a
> -		 * preemption event, but we cannot predict whether
> -		 * that will affect port[0] or port[1].
> -		 *
> -		 * If the second port is already active, we can wait
> -		 * until the next context-switch before contemplating
> -		 * new requests. The GPU will be busy and we should be
> -		 * able to resubmit the new ELSP before it idles,
> -		 * avoiding pipeline bubbles (momentary pauses where
> -		 * the driver is unable to keep up the supply of new
> -		 * work). However, we have to double check that the
> -		 * priorities of the ports haven't been switch.
> -		 */
> -		if (port_count(&port[1]))
> -			return;
> +			/*
> +			 * Note that we have not stopped the GPU at this point,
> +			 * so we are unwinding the incomplete requests as they
> +			 * remain inflight and so by the time we do complete
> +			 * the preemption, some of the unwound requests may
> +			 * complete!
> +			 */
> +			__unwind_incomplete_requests(engine);
>  
> -		/*
> -		 * WaIdleLiteRestore:bdw,skl
> -		 * Apply the wa NOOPs to prevent
> -		 * ring:HEAD == rq:TAIL as we resubmit the
> -		 * request. See gen8_emit_fini_breadcrumb() for
> -		 * where we prepare the padding after the
> -		 * end of the request.
> -		 */
> -		last->tail = last->wa_tail;
> +			/*
> +			 * If we need to return to the preempted context, we
> +			 * need to skip the lite-restore and force it to
> +			 * reload the RING_TAIL. Otherwise, the HW has a
> +			 * tendency to ignore us rewinding the TAIL to the
> +			 * end of an earlier request.
> +			 */
> +			last->hw_context->lrc_desc |= CTX_DESC_FORCE_RESTORE;
> +			last = NULL;
> +		} else {
> +			/*
> +			 * Otherwise if we already have a request pending
> +			 * for execution after the current one, we can
> +			 * just wait until the next CS event before
> +			 * queuing more. In either case we will force a
> +			 * lite-restore preemption event, but if we wait
> +			 * we hopefully coalesce several updates into a single
> +			 * submission.
> +			 */
> +			if (!list_is_last(&last->sched.link,
> +					  &engine->active.requests))
> +				return;
> +
> +			/*
> +			 * WaIdleLiteRestore:bdw,skl
> +			 * Apply the wa NOOPs to prevent
> +			 * ring:HEAD == rq:TAIL as we resubmit the
> +			 * request. See gen8_emit_fini_breadcrumb() for
> +			 * where we prepare the padding after the
> +			 * end of the request.
> +			 */
> +			last->tail = last->wa_tail;
> +		}
>  	}
>  
>  	while (rb) { /* XXX virtual is always taking precedence */
> @@ -955,9 +962,24 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>  				continue;
>  			}
>  
> +			if (i915_request_completed(rq)) {
> +				ve->request = NULL;
> +				ve->base.execlists.queue_priority_hint = INT_MIN;
> +				rb_erase_cached(rb, &execlists->virtual);
> +				RB_CLEAR_NODE(rb);
> +
> +				rq->engine = engine;
> +				__i915_request_submit(rq);
> +
> +				spin_unlock(&ve->base.active.lock);
> +
> +				rb = rb_first_cached(&execlists->virtual);
> +				continue;
> +			}
> +
>  			if (last && !can_merge_rq(last, rq)) {
>  				spin_unlock(&ve->base.active.lock);
> -				return; /* leave this rq for another engine */
> +				return; /* leave this for another */
>  			}
>  
>  			GEM_TRACE("%s: virtual rq=%llx:%lld%s, new engine? %s\n",
> @@ -1006,9 +1028,10 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>  			}
>  
>  			__i915_request_submit(rq);
> -			trace_i915_request_in(rq, port_index(port, execlists));
> -			submit = true;
> -			last = rq;
> +			if (!i915_request_completed(rq)) {
> +				submit = true;
> +				last = rq;
> +			}
>  		}
>  
>  		spin_unlock(&ve->base.active.lock);
> @@ -1021,6 +1044,9 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>  		int i;
>  
>  		priolist_for_each_request_consume(rq, rn, p, i) {
> +			if (i915_request_completed(rq))
> +				goto skip;
> +
>  			/*
>  			 * Can we combine this request with the current port?
>  			 * It has to be the same context/ringbuffer and not
> @@ -1060,19 +1086,14 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>  				    ctx_single_port_submission(rq->hw_context))
>  					goto done;
>  
> -
> -				if (submit)
> -					port_assign(port, last);
> +				*port = execlists_schedule_in(last, port - execlists->pending);
>  				port++;
> -
> -				GEM_BUG_ON(port_isset(port));
>  			}
>  
> -			__i915_request_submit(rq);
> -			trace_i915_request_in(rq, port_index(port, execlists));
> -
>  			last = rq;
>  			submit = true;
> +skip:
> +			__i915_request_submit(rq);
>  		}
>  
>  		rb_erase_cached(&p->node, &execlists->queue);
> @@ -1097,54 +1118,30 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>  	 * interrupt for secondary ports).
>  	 */
>  	execlists->queue_priority_hint = queue_prio(execlists);
> +	GEM_TRACE("%s: queue_priority_hint:%d, submit:%s\n",
> +		  engine->name, execlists->queue_priority_hint,
> +		  yesno(submit));
>  
>  	if (submit) {
> -		port_assign(port, last);
> +		*port = execlists_schedule_in(last, port - execlists->pending);
> +		memset(port + 1, 0, (last_port - port) * sizeof(*port));
>  		execlists_submit_ports(engine);
>  	}
> -
> -	/* We must always keep the beast fed if we have work piled up */
> -	GEM_BUG_ON(rb_first_cached(&execlists->queue) &&
> -		   !port_isset(execlists->port));
> -
> -	/* Re-evaluate the executing context setup after each preemptive kick */
> -	if (last)
> -		execlists_user_begin(execlists, execlists->port);
> -
> -	/* If the engine is now idle, so should be the flag; and vice versa. */
> -	GEM_BUG_ON(execlists_is_active(&engine->execlists,
> -				       EXECLISTS_ACTIVE_USER) ==
> -		   !port_isset(engine->execlists.port));
>  }
>  
>  void
>  execlists_cancel_port_requests(struct intel_engine_execlists * const execlists)
>  {
> -	struct execlist_port *port = execlists->port;
> -	unsigned int num_ports = execlists_num_ports(execlists);
> -
> -	while (num_ports-- && port_isset(port)) {
> -		struct i915_request *rq = port_request(port);
> -
> -		GEM_TRACE("%s:port%u fence %llx:%lld, (current %d)\n",
> -			  rq->engine->name,
> -			  (unsigned int)(port - execlists->port),
> -			  rq->fence.context, rq->fence.seqno,
> -			  hwsp_seqno(rq));
> +	struct i915_request * const *port, *rq;
>  
> -		GEM_BUG_ON(!execlists->active);
> -		execlists_context_schedule_out(rq,
> -					       i915_request_completed(rq) ?
> -					       INTEL_CONTEXT_SCHEDULE_OUT :
> -					       INTEL_CONTEXT_SCHEDULE_PREEMPTED);
> +	for (port = execlists->pending; (rq = *port); port++)
> +		execlists_schedule_out(rq);
> +	memset(execlists->pending, 0, sizeof(execlists->pending));
>  
> -		i915_request_put(rq);
> -
> -		memset(port, 0, sizeof(*port));
> -		port++;
> -	}
> -
> -	execlists_clear_all_active(execlists);
> +	for (port = execlists->active; (rq = *port); port++)
> +		execlists_schedule_out(rq);
> +	execlists->active =
> +		memset(execlists->inflight, 0, sizeof(execlists->inflight));
>  }
>  
>  static inline void
> @@ -1163,7 +1160,6 @@ reset_in_progress(const struct intel_engine_execlists *execlists)
>  static void process_csb(struct intel_engine_cs *engine)
>  {
>  	struct intel_engine_execlists * const execlists = &engine->execlists;
> -	struct execlist_port *port = execlists->port;
>  	const u32 * const buf = execlists->csb_status;
>  	const u8 num_entries = execlists->csb_size;
>  	u8 head, tail;
> @@ -1198,9 +1194,7 @@ static void process_csb(struct intel_engine_cs *engine)
>  	rmb();
>  
>  	do {
> -		struct i915_request *rq;
>  		unsigned int status;
> -		unsigned int count;
>  
>  		if (++head == num_entries)
>  			head = 0;
> @@ -1223,68 +1217,37 @@ static void process_csb(struct intel_engine_cs *engine)
>  		 * status notifier.
>  		 */
>  
> -		GEM_TRACE("%s csb[%d]: status=0x%08x:0x%08x, active=0x%x\n",
> +		GEM_TRACE("%s csb[%d]: status=0x%08x:0x%08x\n",
>  			  engine->name, head,
> -			  buf[2 * head + 0], buf[2 * head + 1],
> -			  execlists->active);
> +			  buf[2 * head + 0], buf[2 * head + 1]);
>  
>  		status = buf[2 * head];
> -		if (status & (GEN8_CTX_STATUS_IDLE_ACTIVE |
> -			      GEN8_CTX_STATUS_PREEMPTED))
> -			execlists_set_active(execlists,
> -					     EXECLISTS_ACTIVE_HWACK);
> -		if (status & GEN8_CTX_STATUS_ACTIVE_IDLE)
> -			execlists_clear_active(execlists,
> -					       EXECLISTS_ACTIVE_HWACK);
> -
> -		if (!(status & GEN8_CTX_STATUS_COMPLETED_MASK))
> -			continue;
> +		if (status & GEN8_CTX_STATUS_IDLE_ACTIVE) {
> +promote:
> +			GEM_BUG_ON(!assert_pending_valid(execlists, "promote"));
> +			execlists->active =
> +				memcpy(execlists->inflight,
> +				       execlists->pending,
> +				       execlists_num_ports(execlists) *
> +				       sizeof(*execlists->pending));
> +			execlists->pending[0] = NULL;

I can't decide if comment or a helper inline function would
serve better as documentation of between inflight and pending
movement.

I guess it is better to be left as a future work after
the dust settles.

Just general yearning for a similar kind of level of documentation
steps as in dequeue.

>  
> -		/* We should never get a COMPLETED | IDLE_ACTIVE! */
> -		GEM_BUG_ON(status & GEN8_CTX_STATUS_IDLE_ACTIVE);

Is our assert coverage going to suffer?

> +			if (!inject_preempt_hang(execlists))
> +				ring_pause(engine) = 0;
> +		} else if (status & GEN8_CTX_STATUS_PREEMPTED) {
> +			struct i915_request * const *port = execlists->active;
>  
> -		if (status & GEN8_CTX_STATUS_COMPLETE &&
> -		    buf[2*head + 1] == execlists->preempt_complete_status) {
> -			GEM_TRACE("%s preempt-idle\n", engine->name);
> -			complete_preempt_context(execlists);
> -			continue;
> -		}
> -
> -		if (status & GEN8_CTX_STATUS_PREEMPTED &&
> -		    execlists_is_active(execlists,
> -					EXECLISTS_ACTIVE_PREEMPT))
> -			continue;
> +			trace_ports(execlists, "preempted", execlists->active);
>  
> -		GEM_BUG_ON(!execlists_is_active(execlists,
> -						EXECLISTS_ACTIVE_USER));
> +			while (*port)
> +				execlists_schedule_out(*port++);
>  
> -		rq = port_unpack(port, &count);
> -		GEM_TRACE("%s out[0]: ctx=%d.%d, fence %llx:%lld (current %d), prio=%d\n",
> -			  engine->name,
> -			  port->context_id, count,
> -			  rq ? rq->fence.context : 0,
> -			  rq ? rq->fence.seqno : 0,
> -			  rq ? hwsp_seqno(rq) : 0,
> -			  rq ? rq_prio(rq) : 0);
> +			goto promote;
> +		} else if (*execlists->active) {
> +			struct i915_request *rq = *execlists->active++;
>  
> -		/* Check the context/desc id for this event matches */
> -		GEM_DEBUG_BUG_ON(buf[2 * head + 1] != port->context_id);
> -
> -		GEM_BUG_ON(count == 0);
> -		if (--count == 0) {
> -			/*
> -			 * On the final event corresponding to the
> -			 * submission of this context, we expect either
> -			 * an element-switch event or a completion
> -			 * event (and on completion, the active-idle
> -			 * marker). No more preemptions, lite-restore
> -			 * or otherwise.
> -			 */
> -			GEM_BUG_ON(status & GEN8_CTX_STATUS_PREEMPTED);
> -			GEM_BUG_ON(port_isset(&port[1]) &&
> -				   !(status & GEN8_CTX_STATUS_ELEMENT_SWITCH));
> -			GEM_BUG_ON(!port_isset(&port[1]) &&
> -				   !(status & GEN8_CTX_STATUS_ACTIVE_IDLE));
> +			trace_ports(execlists, "completed",
> +				    execlists->active - 1);
>  
>  			/*
>  			 * We rely on the hardware being strongly
> @@ -1293,21 +1256,10 @@ static void process_csb(struct intel_engine_cs *engine)
>  			 * user interrupt and CSB is processed.
>  			 */
>  			GEM_BUG_ON(!i915_request_completed(rq));
> +			execlists_schedule_out(rq);
>  
> -			execlists_context_schedule_out(rq,
> -						       INTEL_CONTEXT_SCHEDULE_OUT);
> -			i915_request_put(rq);
> -
> -			GEM_TRACE("%s completed ctx=%d\n",
> -				  engine->name, port->context_id);
> -
> -			port = execlists_port_complete(execlists, port);
> -			if (port_isset(port))
> -				execlists_user_begin(execlists, port);
> -			else
> -				execlists_user_end(execlists);
> -		} else {
> -			port_set(port, port_pack(rq, count));
> +			GEM_BUG_ON(execlists->active - execlists->inflight >
> +				   execlists_num_ports(execlists));
>  		}
>  	} while (head != tail);
>  
> @@ -1332,7 +1284,7 @@ static void __execlists_submission_tasklet(struct intel_engine_cs *const engine)
>  	lockdep_assert_held(&engine->active.lock);
>  
>  	process_csb(engine);
> -	if (!execlists_is_active(&engine->execlists, EXECLISTS_ACTIVE_PREEMPT))
> +	if (!engine->execlists.pending[0])
>  		execlists_dequeue(engine);
>  }
>  
> @@ -1345,11 +1297,6 @@ static void execlists_submission_tasklet(unsigned long data)
>  	struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
>  	unsigned long flags;
>  
> -	GEM_TRACE("%s awake?=%d, active=%x\n",
> -		  engine->name,
> -		  !!intel_wakeref_active(&engine->wakeref),
> -		  engine->execlists.active);
> -
>  	spin_lock_irqsave(&engine->active.lock, flags);
>  	__execlists_submission_tasklet(engine);
>  	spin_unlock_irqrestore(&engine->active.lock, flags);
> @@ -1376,12 +1323,16 @@ static void __submit_queue_imm(struct intel_engine_cs *engine)
>  		tasklet_hi_schedule(&execlists->tasklet);
>  }
>  
> -static void submit_queue(struct intel_engine_cs *engine, int prio)
> +static void submit_queue(struct intel_engine_cs *engine,
> +			 const struct i915_request *rq)
>  {
> -	if (prio > engine->execlists.queue_priority_hint) {
> -		engine->execlists.queue_priority_hint = prio;
> -		__submit_queue_imm(engine);
> -	}
> +	struct intel_engine_execlists *execlists = &engine->execlists;
> +
> +	if (rq_prio(rq) <= execlists->queue_priority_hint)
> +		return;
> +
> +	execlists->queue_priority_hint = rq_prio(rq);
> +	__submit_queue_imm(engine);
>  }
>  
>  static void execlists_submit_request(struct i915_request *request)
> @@ -1397,7 +1348,7 @@ static void execlists_submit_request(struct i915_request *request)
>  	GEM_BUG_ON(RB_EMPTY_ROOT(&engine->execlists.queue.rb_root));
>  	GEM_BUG_ON(list_empty(&request->sched.link));
>  
> -	submit_queue(engine, rq_prio(request));
> +	submit_queue(engine, request);
>  
>  	spin_unlock_irqrestore(&engine->active.lock, flags);
>  }
> @@ -2048,27 +1999,13 @@ static void execlists_reset_prepare(struct intel_engine_cs *engine)
>  	spin_unlock_irqrestore(&engine->active.lock, flags);
>  }
>  
> -static bool lrc_regs_ok(const struct i915_request *rq)
> -{
> -	const struct intel_ring *ring = rq->ring;
> -	const u32 *regs = rq->hw_context->lrc_reg_state;
> -
> -	/* Quick spot check for the common signs of context corruption */
> -
> -	if (regs[CTX_RING_BUFFER_CONTROL + 1] !=
> -	    (RING_CTL_SIZE(ring->size) | RING_VALID))
> -		return false;
> -
> -	if (regs[CTX_RING_BUFFER_START + 1] != i915_ggtt_offset(ring->vma))
> -		return false;
> -
> -	return true;
> -}
> -
> -static void reset_csb_pointers(struct intel_engine_execlists *execlists)
> +static void reset_csb_pointers(struct intel_engine_cs *engine)
>  {
> +	struct intel_engine_execlists * const execlists = &engine->execlists;
>  	const unsigned int reset_value = execlists->csb_size - 1;
>  
> +	ring_pause(engine) = 0;
> +
>  	/*
>  	 * After a reset, the HW starts writing into CSB entry [0]. We
>  	 * therefore have to set our HEAD pointer back one entry so that
> @@ -2115,18 +2052,21 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
>  	process_csb(engine); /* drain preemption events */
>  
>  	/* Following the reset, we need to reload the CSB read/write pointers */
> -	reset_csb_pointers(&engine->execlists);
> +	reset_csb_pointers(engine);
>  
>  	/*
>  	 * Save the currently executing context, even if we completed
>  	 * its request, it was still running at the time of the
>  	 * reset and will have been clobbered.
>  	 */
> -	if (!port_isset(execlists->port))
> -		goto out_clear;
> +	rq = execlists_active(execlists);
> +	if (!rq)
> +		return;
>  
> -	rq = port_request(execlists->port);
>  	ce = rq->hw_context;
> +	GEM_BUG_ON(i915_active_is_idle(&ce->active));
> +	GEM_BUG_ON(!i915_vma_is_pinned(ce->state));
> +	rq = active_request(rq);
>  
>  	/*
>  	 * Catch up with any missed context-switch interrupts.
> @@ -2139,9 +2079,12 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
>  	 */
>  	execlists_cancel_port_requests(execlists);
>  
> -	rq = active_request(rq);
> -	if (!rq)
> +	if (!rq) {
> +		ce->ring->head = ce->ring->tail;
>  		goto out_replay;
> +	}
> +
> +	ce->ring->head = intel_ring_wrap(ce->ring, rq->head);
>  
>  	/*
>  	 * If this request hasn't started yet, e.g. it is waiting on a
> @@ -2155,7 +2098,7 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
>  	 * Otherwise, if we have not started yet, the request should replay
>  	 * perfectly and we do not need to flag the result as being erroneous.
>  	 */
> -	if (!i915_request_started(rq) && lrc_regs_ok(rq))
> +	if (!i915_request_started(rq))
>  		goto out_replay;
>  
>  	/*
> @@ -2170,7 +2113,7 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
>  	 * image back to the expected values to skip over the guilty request.
>  	 */
>  	i915_reset_request(rq, stalled);
> -	if (!stalled && lrc_regs_ok(rq))
> +	if (!stalled)
>  		goto out_replay;
>  
>  	/*
> @@ -2190,17 +2133,13 @@ static void __execlists_reset(struct intel_engine_cs *engine, bool stalled)
>  	execlists_init_reg_state(regs, ce, engine, ce->ring);
>  
>  out_replay:
> -	/* Rerun the request; its payload has been neutered (if guilty). */
> -	ce->ring->head =
> -		rq ? intel_ring_wrap(ce->ring, rq->head) : ce->ring->tail;
> +	GEM_TRACE("%s replay {head:%04x, tail:%04x\n",
> +		  engine->name, ce->ring->head, ce->ring->tail);
>  	intel_ring_update_space(ce->ring);
>  	__execlists_update_reg_state(ce, engine);
>  
>  	/* Push back any incomplete requests for replay after the reset. */
>  	__unwind_incomplete_requests(engine);
> -
> -out_clear:
> -	execlists_clear_all_active(execlists);
>  }
>  
>  static void execlists_reset(struct intel_engine_cs *engine, bool stalled)
> @@ -2296,7 +2235,6 @@ static void execlists_cancel_requests(struct intel_engine_cs *engine)
>  
>  	execlists->queue_priority_hint = INT_MIN;
>  	execlists->queue = RB_ROOT_CACHED;
> -	GEM_BUG_ON(port_isset(execlists->port));
>  
>  	GEM_BUG_ON(__tasklet_is_enabled(&execlists->tasklet));
>  	execlists->tasklet.func = nop_submission_tasklet;
> @@ -2514,15 +2452,29 @@ static u32 *gen8_emit_wa_tail(struct i915_request *request, u32 *cs)
>  	return cs;
>  }
>  
> +static u32 *emit_preempt_busywait(struct i915_request *request, u32 *cs)
> +{
> +	*cs++ = MI_SEMAPHORE_WAIT |
> +		MI_SEMAPHORE_GLOBAL_GTT |
> +		MI_SEMAPHORE_POLL |
> +		MI_SEMAPHORE_SAD_EQ_SDD;
> +	*cs++ = 0;
> +	*cs++ = intel_hws_preempt_address(request->engine);
> +	*cs++ = 0;
> +
> +	return cs;
> +}
> +
>  static u32 *gen8_emit_fini_breadcrumb(struct i915_request *request, u32 *cs)
>  {
>  	cs = gen8_emit_ggtt_write(cs,
>  				  request->fence.seqno,
>  				  request->timeline->hwsp_offset,
>  				  0);
> -
>  	*cs++ = MI_USER_INTERRUPT;
> +
>  	*cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;

This was discussed in irc, could warrant a comment here of
why this is needed. Precious info.

> +	cs = emit_preempt_busywait(request, cs);
>  
>  	request->tail = intel_ring_offset(request, cs);
>  	assert_ring_tail_valid(request->ring, request->tail);
> @@ -2543,9 +2495,10 @@ static u32 *gen8_emit_fini_breadcrumb_rcs(struct i915_request *request, u32 *cs)
>  				    PIPE_CONTROL_FLUSH_ENABLE |
>  				    PIPE_CONTROL_CS_STALL,
>  				    0);
> -
>  	*cs++ = MI_USER_INTERRUPT;
> +
>  	*cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;

Comment could be copied here. Or a common helper created for common parts.

-Mika

> +	cs = emit_preempt_busywait(request, cs);
>  
>  	request->tail = intel_ring_offset(request, cs);
>  	assert_ring_tail_valid(request->ring, request->tail);
> @@ -2594,8 +2547,7 @@ void intel_execlists_set_default_submission(struct intel_engine_cs *engine)
>  	engine->flags |= I915_ENGINE_SUPPORTS_STATS;
>  	if (!intel_vgpu_active(engine->i915))
>  		engine->flags |= I915_ENGINE_HAS_SEMAPHORES;
> -	if (engine->preempt_context &&
> -	    HAS_LOGICAL_RING_PREEMPTION(engine->i915))
> +	if (HAS_LOGICAL_RING_PREEMPTION(engine->i915))
>  		engine->flags |= I915_ENGINE_HAS_PREEMPTION;
>  }
>  
> @@ -2718,11 +2670,6 @@ int intel_execlists_submission_init(struct intel_engine_cs *engine)
>  			i915_mmio_reg_offset(RING_ELSP(base));
>  	}
>  
> -	execlists->preempt_complete_status = ~0u;
> -	if (engine->preempt_context)
> -		execlists->preempt_complete_status =
> -			upper_32_bits(engine->preempt_context->lrc_desc);
> -
>  	execlists->csb_status =
>  		&engine->status_page.addr[I915_HWS_CSB_BUF0_INDEX];
>  
> @@ -2734,7 +2681,7 @@ int intel_execlists_submission_init(struct intel_engine_cs *engine)
>  	else
>  		execlists->csb_size = GEN11_CSB_ENTRIES;
>  
> -	reset_csb_pointers(execlists);
> +	reset_csb_pointers(engine);
>  
>  	return 0;
>  }
> @@ -2917,11 +2864,6 @@ populate_lr_context(struct intel_context *ce,
>  	if (!engine->default_state)
>  		regs[CTX_CONTEXT_CONTROL + 1] |=
>  			_MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT);
> -	if (ce->gem_context == engine->i915->preempt_context &&
> -	    INTEL_GEN(engine->i915) < 11)
> -		regs[CTX_CONTEXT_CONTROL + 1] |=
> -			_MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT |
> -					   CTX_CTRL_ENGINE_CTX_SAVE_INHIBIT);
>  
>  	ret = 0;
>  err_unpin_ctx:
> diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
> index b7e9fddef270..a497cf7acb6a 100644
> --- a/drivers/gpu/drm/i915/i915_gpu_error.c
> +++ b/drivers/gpu/drm/i915/i915_gpu_error.c
> @@ -1248,10 +1248,10 @@ static void error_record_engine_registers(struct i915_gpu_state *error,
>  	}
>  }
>  
> -static void record_request(struct i915_request *request,
> +static void record_request(const struct i915_request *request,
>  			   struct drm_i915_error_request *erq)
>  {
> -	struct i915_gem_context *ctx = request->gem_context;
> +	const struct i915_gem_context *ctx = request->gem_context;
>  
>  	erq->flags = request->fence.flags;
>  	erq->context = request->fence.context;
> @@ -1315,20 +1315,15 @@ static void engine_record_requests(struct intel_engine_cs *engine,
>  	ee->num_requests = count;
>  }
>  
> -static void error_record_engine_execlists(struct intel_engine_cs *engine,
> +static void error_record_engine_execlists(const struct intel_engine_cs *engine,
>  					  struct drm_i915_error_engine *ee)
>  {
>  	const struct intel_engine_execlists * const execlists = &engine->execlists;
> -	unsigned int n;
> +	struct i915_request * const *port = execlists->active;
> +	unsigned int n = 0;
>  
> -	for (n = 0; n < execlists_num_ports(execlists); n++) {
> -		struct i915_request *rq = port_request(&execlists->port[n]);
> -
> -		if (!rq)
> -			break;
> -
> -		record_request(rq, &ee->execlist[n]);
> -	}
> +	while (*port)
> +		record_request(*port++, &ee->execlist[n++]);
>  
>  	ee->num_ports = n;
>  }
> diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
> index 7083e6ab92c5..0c99694faab7 100644
> --- a/drivers/gpu/drm/i915/i915_request.c
> +++ b/drivers/gpu/drm/i915/i915_request.c
> @@ -276,6 +276,12 @@ static bool i915_request_retire(struct i915_request *rq)
>  
>  	local_irq_disable();
>  
> +	/*
> +	 * We only loosely track inflight requests across preemption,
> +	 * and so we may find ourselves attempting to retire a _completed_
> +	 * request that we have removed from the HW and put back on a run
> +	 * queue.
> +	 */
>  	spin_lock(&rq->engine->active.lock);
>  	list_del(&rq->sched.link);
>  	spin_unlock(&rq->engine->active.lock);
> diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
> index edbbdfec24ab..bebc1e9b4a5e 100644
> --- a/drivers/gpu/drm/i915/i915_request.h
> +++ b/drivers/gpu/drm/i915/i915_request.h
> @@ -28,6 +28,7 @@
>  #include <linux/dma-fence.h>
>  #include <linux/lockdep.h>
>  
> +#include "gt/intel_context_types.h"
>  #include "gt/intel_engine_types.h"
>  
>  #include "i915_gem.h"
> diff --git a/drivers/gpu/drm/i915/i915_scheduler.c b/drivers/gpu/drm/i915/i915_scheduler.c
> index 2e9b38bdc33c..b1ba3e65cd52 100644
> --- a/drivers/gpu/drm/i915/i915_scheduler.c
> +++ b/drivers/gpu/drm/i915/i915_scheduler.c
> @@ -179,8 +179,7 @@ static inline int rq_prio(const struct i915_request *rq)
>  
>  static void kick_submission(struct intel_engine_cs *engine, int prio)
>  {
> -	const struct i915_request *inflight =
> -		port_request(engine->execlists.port);
> +	const struct i915_request *inflight = *engine->execlists.active;
>  
>  	/*
>  	 * If we are already the currently executing context, don't
> diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
> index 2987219a6300..4920ff9aba62 100644
> --- a/drivers/gpu/drm/i915/i915_utils.h
> +++ b/drivers/gpu/drm/i915/i915_utils.h
> @@ -131,6 +131,18 @@ __check_struct_size(size_t base, size_t arr, size_t count, size_t *size)
>  	((typeof(ptr))((unsigned long)(ptr) | __bits));			\
>  })
>  
> +#define ptr_count_dec(p_ptr) do {					\
> +	typeof(p_ptr) __p = (p_ptr);					\
> +	unsigned long __v = (unsigned long)(*__p);			\
> +	*__p = (typeof(*p_ptr))(--__v);					\
> +} while (0)
> +
> +#define ptr_count_inc(p_ptr) do {					\
> +	typeof(p_ptr) __p = (p_ptr);					\
> +	unsigned long __v = (unsigned long)(*__p);			\
> +	*__p = (typeof(*p_ptr))(++__v);					\
> +} while (0)
> +
>  #define page_mask_bits(ptr) ptr_mask_bits(ptr, PAGE_SHIFT)
>  #define page_unmask_bits(ptr) ptr_unmask_bits(ptr, PAGE_SHIFT)
>  #define page_pack_bits(ptr, bits) ptr_pack_bits(ptr, bits, PAGE_SHIFT)
> diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c
> index db531ebc7704..12c22359fdac 100644
> --- a/drivers/gpu/drm/i915/intel_guc_submission.c
> +++ b/drivers/gpu/drm/i915/intel_guc_submission.c
> @@ -32,7 +32,11 @@
>  #include "intel_guc_submission.h"
>  #include "i915_drv.h"
>  
> -#define GUC_PREEMPT_FINISHED		0x1
> +enum {
> +	GUC_PREEMPT_NONE = 0,
> +	GUC_PREEMPT_INPROGRESS,
> +	GUC_PREEMPT_FINISHED,
> +};
>  #define GUC_PREEMPT_BREADCRUMB_DWORDS	0x8
>  #define GUC_PREEMPT_BREADCRUMB_BYTES	\
>  	(sizeof(u32) * GUC_PREEMPT_BREADCRUMB_DWORDS)
> @@ -537,15 +541,11 @@ static void guc_add_request(struct intel_guc *guc, struct i915_request *rq)
>  	u32 ctx_desc = lower_32_bits(rq->hw_context->lrc_desc);
>  	u32 ring_tail = intel_ring_set_tail(rq->ring, rq->tail) / sizeof(u64);
>  
> -	spin_lock(&client->wq_lock);
> -
>  	guc_wq_item_append(client, engine->guc_id, ctx_desc,
>  			   ring_tail, rq->fence.seqno);
>  	guc_ring_doorbell(client);
>  
>  	client->submissions[engine->id] += 1;
> -
> -	spin_unlock(&client->wq_lock);
>  }
>  
>  /*
> @@ -631,8 +631,9 @@ static void inject_preempt_context(struct work_struct *work)
>  	data[6] = intel_guc_ggtt_offset(guc, guc->shared_data);
>  
>  	if (WARN_ON(intel_guc_send(guc, data, ARRAY_SIZE(data)))) {
> -		execlists_clear_active(&engine->execlists,
> -				       EXECLISTS_ACTIVE_PREEMPT);
> +		intel_write_status_page(engine,
> +					I915_GEM_HWS_PREEMPT,
> +					GUC_PREEMPT_NONE);
>  		tasklet_schedule(&engine->execlists.tasklet);
>  	}
>  
> @@ -672,8 +673,6 @@ static void complete_preempt_context(struct intel_engine_cs *engine)
>  {
>  	struct intel_engine_execlists *execlists = &engine->execlists;
>  
> -	GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT));
> -
>  	if (inject_preempt_hang(execlists))
>  		return;
>  
> @@ -681,89 +680,90 @@ static void complete_preempt_context(struct intel_engine_cs *engine)
>  	execlists_unwind_incomplete_requests(execlists);
>  
>  	wait_for_guc_preempt_report(engine);
> -	intel_write_status_page(engine, I915_GEM_HWS_PREEMPT, 0);
> +	intel_write_status_page(engine, I915_GEM_HWS_PREEMPT, GUC_PREEMPT_NONE);
>  }
>  
> -/**
> - * guc_submit() - Submit commands through GuC
> - * @engine: engine associated with the commands
> - *
> - * The only error here arises if the doorbell hardware isn't functioning
> - * as expected, which really shouln't happen.
> - */
> -static void guc_submit(struct intel_engine_cs *engine)
> +static void guc_submit(struct intel_engine_cs *engine,
> +		       struct i915_request **out,
> +		       struct i915_request **end)
>  {
>  	struct intel_guc *guc = &engine->i915->guc;
> -	struct intel_engine_execlists * const execlists = &engine->execlists;
> -	struct execlist_port *port = execlists->port;
> -	unsigned int n;
> +	struct intel_guc_client *client = guc->execbuf_client;
>  
> -	for (n = 0; n < execlists_num_ports(execlists); n++) {
> -		struct i915_request *rq;
> -		unsigned int count;
> +	spin_lock(&client->wq_lock);
>  
> -		rq = port_unpack(&port[n], &count);
> -		if (rq && count == 0) {
> -			port_set(&port[n], port_pack(rq, ++count));
> +	do {
> +		struct i915_request *rq = *out++;
>  
> -			flush_ggtt_writes(rq->ring->vma);
> +		flush_ggtt_writes(rq->ring->vma);
> +		guc_add_request(guc, rq);
> +	} while (out != end);
>  
> -			guc_add_request(guc, rq);
> -		}
> -	}
> +	spin_unlock(&client->wq_lock);
>  }
>  
> -static void port_assign(struct execlist_port *port, struct i915_request *rq)
> +static inline int rq_prio(const struct i915_request *rq)
>  {
> -	GEM_BUG_ON(port_isset(port));
> -
> -	port_set(port, i915_request_get(rq));
> +	return rq->sched.attr.priority | __NO_PREEMPTION;
>  }
>  
> -static inline int rq_prio(const struct i915_request *rq)
> +static struct i915_request *schedule_in(struct i915_request *rq, int idx)
>  {
> -	return rq->sched.attr.priority;
> +	trace_i915_request_in(rq, idx);
> +
> +	if (!rq->hw_context->inflight)
> +		rq->hw_context->inflight = rq->engine;
> +	intel_context_inflight_inc(rq->hw_context);
> +
> +	return i915_request_get(rq);
>  }
>  
> -static inline int port_prio(const struct execlist_port *port)
> +static void schedule_out(struct i915_request *rq)
>  {
> -	return rq_prio(port_request(port)) | __NO_PREEMPTION;
> +	trace_i915_request_out(rq);
> +
> +	intel_context_inflight_dec(rq->hw_context);
> +	if (!intel_context_inflight_count(rq->hw_context))
> +		rq->hw_context->inflight = NULL;
> +
> +	i915_request_put(rq);
>  }
>  
> -static bool __guc_dequeue(struct intel_engine_cs *engine)
> +static void __guc_dequeue(struct intel_engine_cs *engine)
>  {
>  	struct intel_engine_execlists * const execlists = &engine->execlists;
> -	struct execlist_port *port = execlists->port;
> -	struct i915_request *last = NULL;
> -	const struct execlist_port * const last_port =
> -		&execlists->port[execlists->port_mask];
> +	struct i915_request **first = execlists->inflight;
> +	struct i915_request ** const last_port = first + execlists->port_mask;
> +	struct i915_request *last = first[0];
> +	struct i915_request **port;
>  	bool submit = false;
>  	struct rb_node *rb;
>  
>  	lockdep_assert_held(&engine->active.lock);
>  
> -	if (port_isset(port)) {
> +	if (last) {
>  		if (intel_engine_has_preemption(engine)) {
>  			struct guc_preempt_work *preempt_work =
>  				&engine->i915->guc.preempt_work[engine->id];
>  			int prio = execlists->queue_priority_hint;
>  
> -			if (i915_scheduler_need_preempt(prio,
> -							port_prio(port))) {
> -				execlists_set_active(execlists,
> -						     EXECLISTS_ACTIVE_PREEMPT);
> +			if (i915_scheduler_need_preempt(prio, rq_prio(last))) {
> +				intel_write_status_page(engine,
> +							I915_GEM_HWS_PREEMPT,
> +							GUC_PREEMPT_INPROGRESS);
>  				queue_work(engine->i915->guc.preempt_wq,
>  					   &preempt_work->work);
> -				return false;
> +				return;
>  			}
>  		}
>  
> -		port++;
> -		if (port_isset(port))
> -			return false;
> +		if (*++first)
> +			return;
> +
> +		last = NULL;
>  	}
> -	GEM_BUG_ON(port_isset(port));
>  
> +	port = first;
>  	while ((rb = rb_first_cached(&execlists->queue))) {
>  		struct i915_priolist *p = to_priolist(rb);
>  		struct i915_request *rq, *rn;
> @@ -774,18 +774,15 @@ static bool __guc_dequeue(struct intel_engine_cs *engine)
>  				if (port == last_port)
>  					goto done;
>  
> -				if (submit)
> -					port_assign(port, last);
> +				*port = schedule_in(last,
> +						    port - execlists->inflight);
>  				port++;
>  			}
>  
>  			list_del_init(&rq->sched.link);
> -
>  			__i915_request_submit(rq);
> -			trace_i915_request_in(rq, port_index(port, execlists));
> -
> -			last = rq;
>  			submit = true;
> +			last = rq;
>  		}
>  
>  		rb_erase_cached(&p->node, &execlists->queue);
> @@ -794,58 +791,41 @@ static bool __guc_dequeue(struct intel_engine_cs *engine)
>  done:
>  	execlists->queue_priority_hint =
>  		rb ? to_priolist(rb)->priority : INT_MIN;
> -	if (submit)
> -		port_assign(port, last);
> -	if (last)
> -		execlists_user_begin(execlists, execlists->port);
> -
> -	/* We must always keep the beast fed if we have work piled up */
> -	GEM_BUG_ON(port_isset(execlists->port) &&
> -		   !execlists_is_active(execlists, EXECLISTS_ACTIVE_USER));
> -	GEM_BUG_ON(rb_first_cached(&execlists->queue) &&
> -		   !port_isset(execlists->port));
> -
> -	return submit;
> -}
> -
> -static void guc_dequeue(struct intel_engine_cs *engine)
> -{
> -	if (__guc_dequeue(engine))
> -		guc_submit(engine);
> +	if (submit) {
> +		*port = schedule_in(last, port - execlists->inflight);
> +		*++port = NULL;
> +		guc_submit(engine, first, port);
> +	}
> +	execlists->active = execlists->inflight;
>  }
>  
>  static void guc_submission_tasklet(unsigned long data)
>  {
>  	struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
>  	struct intel_engine_execlists * const execlists = &engine->execlists;
> -	struct execlist_port *port = execlists->port;
> -	struct i915_request *rq;
> +	struct i915_request **port, *rq;
>  	unsigned long flags;
>  
>  	spin_lock_irqsave(&engine->active.lock, flags);
>  
> -	rq = port_request(port);
> -	while (rq && i915_request_completed(rq)) {
> -		trace_i915_request_out(rq);
> -		i915_request_put(rq);
> +	for (port = execlists->inflight; (rq = *port); port++) {
> +		if (!i915_request_completed(rq))
> +			break;
>  
> -		port = execlists_port_complete(execlists, port);
> -		if (port_isset(port)) {
> -			execlists_user_begin(execlists, port);
> -			rq = port_request(port);
> -		} else {
> -			execlists_user_end(execlists);
> -			rq = NULL;
> -		}
> +		schedule_out(rq);
> +	}
> +	if (port != execlists->inflight) {
> +		int idx = port - execlists->inflight;
> +		int rem = ARRAY_SIZE(execlists->inflight) - idx;
> +		memmove(execlists->inflight, port, rem * sizeof(*port));
>  	}
>  
> -	if (execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT) &&
> -	    intel_read_status_page(engine, I915_GEM_HWS_PREEMPT) ==
> +	if (intel_read_status_page(engine, I915_GEM_HWS_PREEMPT) ==
>  	    GUC_PREEMPT_FINISHED)
>  		complete_preempt_context(engine);
>  
> -	if (!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT))
> -		guc_dequeue(engine);
> +	if (!intel_read_status_page(engine, I915_GEM_HWS_PREEMPT))
> +		__guc_dequeue(engine);
>  
>  	spin_unlock_irqrestore(&engine->active.lock, flags);
>  }
> @@ -959,7 +939,6 @@ static void guc_cancel_requests(struct intel_engine_cs *engine)
>  
>  	execlists->queue_priority_hint = INT_MIN;
>  	execlists->queue = RB_ROOT_CACHED;
> -	GEM_BUG_ON(port_isset(execlists->port));
>  
>  	spin_unlock_irqrestore(&engine->active.lock, flags);
>  }
> @@ -1422,7 +1401,7 @@ int intel_guc_submission_enable(struct intel_guc *guc)
>  	 * and it is guaranteed that it will remove the work item from the
>  	 * queue before our request is completed.
>  	 */
> -	BUILD_BUG_ON(ARRAY_SIZE(engine->execlists.port) *
> +	BUILD_BUG_ON(ARRAY_SIZE(engine->execlists.inflight) *
>  		     sizeof(struct guc_wq_item) *
>  		     I915_NUM_ENGINES > GUC_WQ_SIZE);
>  
> diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c
> index 298bb7116c51..1a5b9e284ca9 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_request.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_request.c
> @@ -366,13 +366,15 @@ static int __igt_breadcrumbs_smoketest(void *arg)
>  
>  		if (!wait_event_timeout(wait->wait,
>  					i915_sw_fence_done(wait),
> -					HZ / 2)) {
> +					5 * HZ)) {
>  			struct i915_request *rq = requests[count - 1];
>  
> -			pr_err("waiting for %d fences (last %llx:%lld) on %s timed out!\n",
> -			       count,
> +			pr_err("waiting for %d/%d fences (last %llx:%lld) on %s timed out!\n",
> +			       atomic_read(&wait->pending), count,
>  			       rq->fence.context, rq->fence.seqno,
>  			       t->engine->name);
> +			GEM_TRACE_DUMP();
> +
>  			i915_gem_set_wedged(t->engine->i915);
>  			GEM_BUG_ON(!i915_request_completed(rq));
>  			i915_sw_fence_wait(wait);
> -- 
> 2.20.1
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* ✓ Fi.CI.BAT: success for series starting with drm/i915/execlists: Preempt-to-busy (rev2)
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
                   ` (7 preceding siblings ...)
  2019-06-20  9:25 ` ✗ Fi.CI.SPARSE: " Patchwork
@ 2019-06-20 12:52 ` Patchwork
  2019-06-20 13:16 ` Patchwork
  2019-06-20 15:38 ` ✗ Fi.CI.IGT: failure " Patchwork
  10 siblings, 0 replies; 23+ messages in thread
From: Patchwork @ 2019-06-20 12:52 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with drm/i915/execlists: Preempt-to-busy (rev2)
URL   : https://patchwork.freedesktop.org/series/62431/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_6312 -> Patchwork_13361
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/

Known issues
------------

  Here are the changes found in Patchwork_13361 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@i915_module_load@reload:
    - fi-icl-u3:          [PASS][1] -> [DMESG-WARN][2] ([fdo#107724])
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-icl-u3/igt@i915_module_load@reload.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/fi-icl-u3/igt@i915_module_load@reload.html

  * igt@i915_selftest@live_hangcheck:
    - fi-icl-u2:          [PASS][3] -> [INCOMPLETE][4] ([fdo#107713] / [fdo#108569])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-icl-u2/igt@i915_selftest@live_hangcheck.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/fi-icl-u2/igt@i915_selftest@live_hangcheck.html

  
#### Possible fixes ####

  * igt@gem_exec_suspend@basic-s3:
    - fi-blb-e6850:       [INCOMPLETE][5] ([fdo#107718]) -> [PASS][6]
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-blb-e6850/igt@gem_exec_suspend@basic-s3.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/fi-blb-e6850/igt@gem_exec_suspend@basic-s3.html

  * igt@kms_chamelium@hdmi-hpd-fast:
    - fi-kbl-7500u:       [FAIL][7] ([fdo#109485]) -> [PASS][8]
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-kbl-7500u/igt@kms_chamelium@hdmi-hpd-fast.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/fi-kbl-7500u/igt@kms_chamelium@hdmi-hpd-fast.html

  
  [fdo#107713]: https://bugs.freedesktop.org/show_bug.cgi?id=107713
  [fdo#107718]: https://bugs.freedesktop.org/show_bug.cgi?id=107718
  [fdo#107724]: https://bugs.freedesktop.org/show_bug.cgi?id=107724
  [fdo#108569]: https://bugs.freedesktop.org/show_bug.cgi?id=108569
  [fdo#109485]: https://bugs.freedesktop.org/show_bug.cgi?id=109485


Participating hosts (49 -> 45)
------------------------------

  Additional (5): fi-cml-u2 fi-bxt-j4205 fi-gdg-551 fi-icl-dsi fi-cml-u 
  Missing    (9): fi-kbl-soraka fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-bsw-cyan fi-apl-guc fi-icl-y fi-byt-clapper fi-bdw-samus 


Build changes
-------------

  * Linux: CI_DRM_6312 -> Patchwork_13361

  CI_DRM_6312: 034e3ac6a2d180d188da927388b60c7e62c5655b @ git://anongit.freedesktop.org/gfx-ci/linux
  IGT_5061: c88ced79a7b71aec58f1d9c5c599ac2f431bcf7a @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
  Patchwork_13361: bfa0c571526c8b843afc7e42fe124e39ff9d81d3 @ git://anongit.freedesktop.org/gfx-ci/linux


== Linux commits ==

bfa0c571526c drm/i915/execlists: Force preemption
0b5d7fbe498b drm/i915/execlists: Minimalistic timeslicing
4769517df2d7 drm/i915/execlists: Preempt-to-busy

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] drm/i915/execlists: Preempt-to-busy
  2019-06-20 12:41   ` Mika Kuoppala
@ 2019-06-20 13:01     ` Chris Wilson
  2019-06-20 13:23       ` Mika Kuoppala
  0 siblings, 1 reply; 23+ messages in thread
From: Chris Wilson @ 2019-06-20 13:01 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx

Quoting Mika Kuoppala (2019-06-20 13:41:26)
> Chris Wilson <chris@chris-wilson.co.uk> writes:
> > @@ -38,6 +39,10 @@ struct intel_context {
> >       struct i915_gem_context *gem_context;
> >       struct intel_engine_cs *engine;
> >       struct intel_engine_cs *inflight;
> > +#define intel_context_inflight(ce) ptr_mask_bits((ce)->inflight, 2)
> > +#define intel_context_inflight_count(ce)  ptr_unmask_bits((ce)->inflight, 2)
> > +#define intel_context_inflight_inc(ce) ptr_count_inc(&(ce)->inflight)
> > +#define intel_context_inflight_dec(ce) ptr_count_dec(&(ce)->inflight)
> 
> Just curious here that what you consider the advantages of carrying
> this info with the pointer?

Packing. I just need a bit to track status, and one for overflow.

> > +static inline u32 intel_hws_preempt_address(struct intel_engine_cs *engine)
> > +{
> > +     return (i915_ggtt_offset(engine->status_page.vma) +
> > +             I915_GEM_HWS_PREEMPT_ADDR);
> > +}
> > +
> > +#define ring_pause(E) ((E)->status_page.addr[I915_GEM_HWS_PREEMPT])
> 
> Scary. Please lets make a function of ring_pause and use
> intel_write_status_page in it.

I'd rather not unless you do __intel_write_state_page.

> So I guess you have and you want squeeze the latency fruit.
> 
> When we have everything in place, CI is green and
> everyone is happy, then we tear it down?

Been there, done that.

> > @@ -442,13 +443,11 @@ __unwind_incomplete_requests(struct intel_engine_cs *engine)
> >               struct intel_engine_cs *owner;
> >  
> >               if (i915_request_completed(rq))
> > -                     break;
> > +                     continue; /* XXX */
> 
> Yeah, but what is the plan with the XXX.

Mulling over tracking context not requests. We still end up with having
to scan history within a context, so not yet seeing anything to
encourage me to make the change. I worry about long request queues
causing preemption latency, as this list is currently only trimmed in
retirement.

One idea in the background is for a scheduler (contemplating something
like the isosynchronous MuQSS) and that might call for a change to
using contexts as the primary, with requests within the contexts.

> > @@ -1223,68 +1217,37 @@ static void process_csb(struct intel_engine_cs *engine)
> >                * status notifier.
> >                */
> >  
> > -             GEM_TRACE("%s csb[%d]: status=0x%08x:0x%08x, active=0x%x\n",
> > +             GEM_TRACE("%s csb[%d]: status=0x%08x:0x%08x\n",
> >                         engine->name, head,
> > -                       buf[2 * head + 0], buf[2 * head + 1],
> > -                       execlists->active);
> > +                       buf[2 * head + 0], buf[2 * head + 1]);
> >  
> >               status = buf[2 * head];
> > -             if (status & (GEN8_CTX_STATUS_IDLE_ACTIVE |
> > -                           GEN8_CTX_STATUS_PREEMPTED))
> > -                     execlists_set_active(execlists,
> > -                                          EXECLISTS_ACTIVE_HWACK);
> > -             if (status & GEN8_CTX_STATUS_ACTIVE_IDLE)
> > -                     execlists_clear_active(execlists,
> > -                                            EXECLISTS_ACTIVE_HWACK);
> > -
> > -             if (!(status & GEN8_CTX_STATUS_COMPLETED_MASK))
> > -                     continue;
> > +             if (status & GEN8_CTX_STATUS_IDLE_ACTIVE) {
> > +promote:
> > +                     GEM_BUG_ON(!assert_pending_valid(execlists, "promote"));
> > +                     execlists->active =
> > +                             memcpy(execlists->inflight,
> > +                                    execlists->pending,
> > +                                    execlists_num_ports(execlists) *
> > +                                    sizeof(*execlists->pending));
> > +                     execlists->pending[0] = NULL;
> 
> I can't decide if comment or a helper inline function would
> serve better as documentation of between inflight and pending
> movement.

The magic is just this function, I think process_csb() reads quite
nicely with the 3 branches and switching between different states. It's
about 8 lines without the comments and asserts.

> I guess it is better to be left as a future work after
> the dust settles.
> 
> Just general yearning for a similar kind of level of documentation
> steps as in dequeue.
> 
> >  
> > -             /* We should never get a COMPLETED | IDLE_ACTIVE! */
> > -             GEM_BUG_ON(status & GEN8_CTX_STATUS_IDLE_ACTIVE);
> 
> Is our assert coverage going to suffer?

You've looked at the added asserts and tracing; I claim we get stronger.

> > @@ -2514,15 +2452,29 @@ static u32 *gen8_emit_wa_tail(struct i915_request *request, u32 *cs)
> >       return cs;
> >  }
> >  
> > +static u32 *emit_preempt_busywait(struct i915_request *request, u32 *cs)
> > +{
> > +     *cs++ = MI_SEMAPHORE_WAIT |
> > +             MI_SEMAPHORE_GLOBAL_GTT |
> > +             MI_SEMAPHORE_POLL |
> > +             MI_SEMAPHORE_SAD_EQ_SDD;
> > +     *cs++ = 0;
> > +     *cs++ = intel_hws_preempt_address(request->engine);
> > +     *cs++ = 0;
> > +
> > +     return cs;
> > +}
> > +
> >  static u32 *gen8_emit_fini_breadcrumb(struct i915_request *request, u32 *cs)
> >  {
> >       cs = gen8_emit_ggtt_write(cs,
> >                                 request->fence.seqno,
> >                                 request->timeline->hwsp_offset,
> >                                 0);
> > -
> >       *cs++ = MI_USER_INTERRUPT;
> > +
> >       *cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
> 
> This was discussed in irc, could warrant a comment here of
> why this is needed. Precious info.

Why the ARB, for reasons of yore. The comment for why we need it is
actually in bb_start.

commit 279f5a00c9a9b39f4f6e9813e6d4da8c181d34c8
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Thu Oct 5 20:10:05 2017 +0100

    drm/i915/execlists: Add a comment for the extra MI_ARB_ENABLE


> > +     cs = emit_preempt_busywait(request, cs);

Why we use the semaphore? That should be explained in dequeue upon
setting up the preemption.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* ✓ Fi.CI.BAT: success for series starting with drm/i915/execlists: Preempt-to-busy (rev2)
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
                   ` (8 preceding siblings ...)
  2019-06-20 12:52 ` ✓ Fi.CI.BAT: success " Patchwork
@ 2019-06-20 13:16 ` Patchwork
  2019-06-20 15:38 ` ✗ Fi.CI.IGT: failure " Patchwork
  10 siblings, 0 replies; 23+ messages in thread
From: Patchwork @ 2019-06-20 13:16 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with drm/i915/execlists: Preempt-to-busy (rev2)
URL   : https://patchwork.freedesktop.org/series/62431/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_6312 -> Patchwork_13361
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/

Known issues
------------

  Here are the changes found in Patchwork_13361 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@i915_module_load@reload:
    - fi-icl-u3:          [PASS][1] -> [DMESG-WARN][2] ([fdo#107724])
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-icl-u3/igt@i915_module_load@reload.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/fi-icl-u3/igt@i915_module_load@reload.html

  * igt@i915_selftest@live_hangcheck:
    - fi-icl-u2:          [PASS][3] -> [INCOMPLETE][4] ([fdo#107713] / [fdo#108569])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-icl-u2/igt@i915_selftest@live_hangcheck.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/fi-icl-u2/igt@i915_selftest@live_hangcheck.html

  
#### Possible fixes ####

  * igt@gem_exec_suspend@basic-s3:
    - fi-blb-e6850:       [INCOMPLETE][5] ([fdo#107718]) -> [PASS][6]
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-blb-e6850/igt@gem_exec_suspend@basic-s3.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/fi-blb-e6850/igt@gem_exec_suspend@basic-s3.html

  * igt@kms_chamelium@hdmi-hpd-fast:
    - fi-kbl-7500u:       [FAIL][7] ([fdo#109485]) -> [PASS][8]
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/fi-kbl-7500u/igt@kms_chamelium@hdmi-hpd-fast.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/fi-kbl-7500u/igt@kms_chamelium@hdmi-hpd-fast.html

  
  [fdo#107713]: https://bugs.freedesktop.org/show_bug.cgi?id=107713
  [fdo#107718]: https://bugs.freedesktop.org/show_bug.cgi?id=107718
  [fdo#107724]: https://bugs.freedesktop.org/show_bug.cgi?id=107724
  [fdo#108569]: https://bugs.freedesktop.org/show_bug.cgi?id=108569
  [fdo#109485]: https://bugs.freedesktop.org/show_bug.cgi?id=109485


Participating hosts (49 -> 45)
------------------------------

  Additional (5): fi-cml-u2 fi-bxt-j4205 fi-gdg-551 fi-icl-dsi fi-cml-u 
  Missing    (9): fi-kbl-soraka fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-bsw-cyan fi-apl-guc fi-icl-y fi-byt-clapper fi-bdw-samus 


Build changes
-------------

  * Linux: CI_DRM_6312 -> Patchwork_13361

  CI_DRM_6312: 034e3ac6a2d180d188da927388b60c7e62c5655b @ git://anongit.freedesktop.org/gfx-ci/linux
  IGT_5061: c88ced79a7b71aec58f1d9c5c599ac2f431bcf7a @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
  Patchwork_13361: bfa0c571526c8b843afc7e42fe124e39ff9d81d3 @ git://anongit.freedesktop.org/gfx-ci/linux


== Linux commits ==

bfa0c571526c drm/i915/execlists: Force preemption
0b5d7fbe498b drm/i915/execlists: Minimalistic timeslicing
4769517df2d7 drm/i915/execlists: Preempt-to-busy

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] drm/i915/execlists: Preempt-to-busy
  2019-06-20 13:01     ` Chris Wilson
@ 2019-06-20 13:23       ` Mika Kuoppala
  0 siblings, 0 replies; 23+ messages in thread
From: Mika Kuoppala @ 2019-06-20 13:23 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx

Chris Wilson <chris@chris-wilson.co.uk> writes:

> Quoting Mika Kuoppala (2019-06-20 13:41:26)
>> Chris Wilson <chris@chris-wilson.co.uk> writes:
>> > @@ -38,6 +39,10 @@ struct intel_context {
>> >       struct i915_gem_context *gem_context;
>> >       struct intel_engine_cs *engine;
>> >       struct intel_engine_cs *inflight;
>> > +#define intel_context_inflight(ce) ptr_mask_bits((ce)->inflight, 2)
>> > +#define intel_context_inflight_count(ce)  ptr_unmask_bits((ce)->inflight, 2)
>> > +#define intel_context_inflight_inc(ce) ptr_count_inc(&(ce)->inflight)
>> > +#define intel_context_inflight_dec(ce) ptr_count_dec(&(ce)->inflight)
>> 
>> Just curious here that what you consider the advantages of carrying
>> this info with the pointer?
>
> Packing. I just need a bit to track status, and one for overflow.
>
>> > +static inline u32 intel_hws_preempt_address(struct intel_engine_cs *engine)
>> > +{
>> > +     return (i915_ggtt_offset(engine->status_page.vma) +
>> > +             I915_GEM_HWS_PREEMPT_ADDR);
>> > +}
>> > +
>> > +#define ring_pause(E) ((E)->status_page.addr[I915_GEM_HWS_PREEMPT])
>> 
>> Scary. Please lets make a function of ring_pause and use
>> intel_write_status_page in it.
>
> I'd rather not unless you do __intel_write_state_page.
>
>> So I guess you have and you want squeeze the latency fruit.
>> 
>> When we have everything in place, CI is green and
>> everyone is happy, then we tear it down?
>
> Been there, done that.

My fears come from csb. Granted, it is a diffent
thing with a different direction of writes.

>
>> > @@ -442,13 +443,11 @@ __unwind_incomplete_requests(struct intel_engine_cs *engine)
>> >               struct intel_engine_cs *owner;
>> >  
>> >               if (i915_request_completed(rq))
>> > -                     break;
>> > +                     continue; /* XXX */
>> 
>> Yeah, but what is the plan with the XXX.
>
> Mulling over tracking context not requests. We still end up with having
> to scan history within a context, so not yet seeing anything to
> encourage me to make the change. I worry about long request queues
> causing preemption latency, as this list is currently only trimmed in
> retirement.
>
> One idea in the background is for a scheduler (contemplating something
> like the isosynchronous MuQSS) and that might call for a change to
> using contexts as the primary, with requests within the contexts.
>
>> > @@ -1223,68 +1217,37 @@ static void process_csb(struct intel_engine_cs *engine)
>> >                * status notifier.
>> >                */
>> >  
>> > -             GEM_TRACE("%s csb[%d]: status=0x%08x:0x%08x, active=0x%x\n",
>> > +             GEM_TRACE("%s csb[%d]: status=0x%08x:0x%08x\n",
>> >                         engine->name, head,
>> > -                       buf[2 * head + 0], buf[2 * head + 1],
>> > -                       execlists->active);
>> > +                       buf[2 * head + 0], buf[2 * head + 1]);
>> >  
>> >               status = buf[2 * head];
>> > -             if (status & (GEN8_CTX_STATUS_IDLE_ACTIVE |
>> > -                           GEN8_CTX_STATUS_PREEMPTED))
>> > -                     execlists_set_active(execlists,
>> > -                                          EXECLISTS_ACTIVE_HWACK);
>> > -             if (status & GEN8_CTX_STATUS_ACTIVE_IDLE)
>> > -                     execlists_clear_active(execlists,
>> > -                                            EXECLISTS_ACTIVE_HWACK);
>> > -
>> > -             if (!(status & GEN8_CTX_STATUS_COMPLETED_MASK))
>> > -                     continue;
>> > +             if (status & GEN8_CTX_STATUS_IDLE_ACTIVE) {
>> > +promote:
>> > +                     GEM_BUG_ON(!assert_pending_valid(execlists, "promote"));
>> > +                     execlists->active =
>> > +                             memcpy(execlists->inflight,
>> > +                                    execlists->pending,
>> > +                                    execlists_num_ports(execlists) *
>> > +                                    sizeof(*execlists->pending));
>> > +                     execlists->pending[0] = NULL;
>> 
>> I can't decide if comment or a helper inline function would
>> serve better as documentation of between inflight and pending
>> movement.
>
> The magic is just this function, I think process_csb() reads quite
> nicely with the 3 branches and switching between different states. It's
> about 8 lines without the comments and asserts.
>

Agreed. It is more compact and more readable with this patch.

>> I guess it is better to be left as a future work after
>> the dust settles.
>> 
>> Just general yearning for a similar kind of level of documentation
>> steps as in dequeue.
>> 
>> >  
>> > -             /* We should never get a COMPLETED | IDLE_ACTIVE! */
>> > -             GEM_BUG_ON(status & GEN8_CTX_STATUS_IDLE_ACTIVE);
>> 
>> Is our assert coverage going to suffer?
>
> You've looked at the added asserts and tracing; I claim we get stronger.
>
>> > @@ -2514,15 +2452,29 @@ static u32 *gen8_emit_wa_tail(struct i915_request *request, u32 *cs)
>> >       return cs;
>> >  }
>> >  
>> > +static u32 *emit_preempt_busywait(struct i915_request *request, u32 *cs)
>> > +{
>> > +     *cs++ = MI_SEMAPHORE_WAIT |
>> > +             MI_SEMAPHORE_GLOBAL_GTT |
>> > +             MI_SEMAPHORE_POLL |
>> > +             MI_SEMAPHORE_SAD_EQ_SDD;
>> > +     *cs++ = 0;
>> > +     *cs++ = intel_hws_preempt_address(request->engine);
>> > +     *cs++ = 0;
>> > +
>> > +     return cs;
>> > +}
>> > +
>> >  static u32 *gen8_emit_fini_breadcrumb(struct i915_request *request, u32 *cs)
>> >  {
>> >       cs = gen8_emit_ggtt_write(cs,
>> >                                 request->fence.seqno,
>> >                                 request->timeline->hwsp_offset,
>> >                                 0);
>> > -
>> >       *cs++ = MI_USER_INTERRUPT;
>> > +
>> >       *cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
>> 
>> This was discussed in irc, could warrant a comment here of
>> why this is needed. Precious info.
>
> Why the ARB, for reasons of yore. The comment for why we need it is
> actually in bb_start.
>
> commit 279f5a00c9a9b39f4f6e9813e6d4da8c181d34c8
> Author: Chris Wilson <chris@chris-wilson.co.uk>
> Date:   Thu Oct 5 20:10:05 2017 +0100
>
>     drm/i915/execlists: Add a comment for the extra MI_ARB_ENABLE

Ok, looks like it.

I do like the new way of handling ports.

Reviewed-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>

>
>
>> > +     cs = emit_preempt_busywait(request, cs);
>
> Why we use the semaphore? That should be explained in dequeue upon
> setting up the preemption.
> -Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/3] drm/i915/execlists: Minimalistic timeslicing
  2019-06-20  7:05 ` [PATCH 2/3] drm/i915/execlists: Minimalistic timeslicing Chris Wilson
@ 2019-06-20 13:51   ` Mika Kuoppala
  2019-06-20 13:57     ` Chris Wilson
  0 siblings, 1 reply; 23+ messages in thread
From: Mika Kuoppala @ 2019-06-20 13:51 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx

Chris Wilson <chris@chris-wilson.co.uk> writes:

> If we have multiple contexts of equal priority pending execution,
> activate a timer to demote the currently executing context in favour of
> the next in the queue when that timeslice expires. This enforces
> fairness between contexts (so long as they allow preemption -- forced
> preemption, in the future, will kick those who do not obey) and allows
> us to avoid userspace blocking forward progress with e.g. unbounded
> MI_SEMAPHORE_WAIT.
>
> For the starting point here, we use the jiffie as our timeslice so that
> we should be reasonably efficient wrt frequent CPU wakeups.
>
> Testcase: igt/gem_exec_scheduler/semaphore-resolve
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/gt/intel_engine_types.h |   6 +
>  drivers/gpu/drm/i915/gt/intel_lrc.c          | 111 +++++++++
>  drivers/gpu/drm/i915/gt/selftest_lrc.c       | 223 +++++++++++++++++++
>  drivers/gpu/drm/i915/i915_scheduler.c        |   1 +
>  drivers/gpu/drm/i915/i915_scheduler_types.h  |   1 +
>  5 files changed, 342 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine_types.h b/drivers/gpu/drm/i915/gt/intel_engine_types.h
> index 411b7a807b99..6591d6bd2692 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine_types.h
> +++ b/drivers/gpu/drm/i915/gt/intel_engine_types.h
> @@ -12,6 +12,7 @@
>  #include <linux/kref.h>
>  #include <linux/list.h>
>  #include <linux/llist.h>
> +#include <linux/timer.h>
>  #include <linux/types.h>
>  
>  #include "i915_gem.h"
> @@ -149,6 +150,11 @@ struct intel_engine_execlists {
>  	 */
>  	struct tasklet_struct tasklet;
>  
> +	/**
> +	 * @timer: kick the current context if its timeslice expires
> +	 */
> +	struct timer_list timer;
> +
>  	/**
>  	 * @default_priolist: priority list for I915_PRIORITY_NORMAL
>  	 */
> diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
> index dc077b536fa5..fca79adb4aa3 100644
> --- a/drivers/gpu/drm/i915/gt/intel_lrc.c
> +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
> @@ -255,6 +255,7 @@ static int effective_prio(const struct i915_request *rq)
>  		prio |= I915_PRIORITY_NOSEMAPHORE;
>  
>  	/* Restrict mere WAIT boosts from triggering preemption */
> +	BUILD_BUG_ON(__NO_PREEMPTION & ~I915_PRIORITY_MASK); /* only internal */
>  	return prio | __NO_PREEMPTION;
>  }
>  
> @@ -813,6 +814,81 @@ last_active(const struct intel_engine_execlists *execlists)
>  	return *last;
>  }
>  
> +static void
> +defer_request(struct i915_request * const rq, struct list_head * const pl)
> +{
> +	struct i915_dependency *p;
> +
> +	/*
> +	 * We want to move the interrupted request to the back of
> +	 * the round-robin list (i.e. its priority level), but
> +	 * in doing so, we must then move all requests that were in
> +	 * flight and were waiting for the interrupted request to
> +	 * be run after it again.
> +	 */
> +	list_move_tail(&rq->sched.link, pl);
> +
> +	list_for_each_entry(p, &rq->sched.waiters_list, wait_link) {
> +		struct i915_request *w =
> +			container_of(p->waiter, typeof(*w), sched);
> +
> +		/* Leave semaphores spinning on the other engines */
> +		if (w->engine != rq->engine)
> +			continue;
> +
> +		/* No waiter should start before the active request completed */
> +		GEM_BUG_ON(i915_request_started(w));
> +
> +		GEM_BUG_ON(rq_prio(w) > rq_prio(rq));
> +		if (rq_prio(w) < rq_prio(rq))
> +			continue;
> +
> +		if (list_empty(&w->sched.link))
> +			continue; /* Not yet submitted; unready */
> +
> +		/*
> +		 * This should be very shallow as it is limited by the
> +		 * number of requests that can fit in a ring (<64) and

s/and/or ?

> +		 * the number of contexts that can be in flight on this
> +		 * engine.
> +		 */
> +		defer_request(w, pl);

So the stack frame will be 64*(3*8 + preample/return) at worst case?
can be over 2k

> +	}
> +}
> +
> +static void defer_active(struct intel_engine_cs *engine)
> +{
> +	struct i915_request *rq;
> +
> +	rq = __unwind_incomplete_requests(engine);
> +	if (!rq)
> +		return;
> +
> +	defer_request(rq, i915_sched_lookup_priolist(engine, rq_prio(rq)));
> +}
> +
> +static bool
> +need_timeslice(struct intel_engine_cs *engine, const struct i915_request *rq)
> +{
> +	int hint;
> +
> +	if (list_is_last(&rq->sched.link, &engine->active.requests))
> +		return false;
> +
> +	hint = max(rq_prio(list_next_entry(rq, sched.link)),
> +		   engine->execlists.queue_priority_hint);
> +
> +	return hint >= rq_prio(rq);
> +}
> +
> +static bool
> +enable_timeslice(struct intel_engine_cs *engine)
> +{
> +	struct i915_request *last = last_active(&engine->execlists);
> +
> +	return last && need_timeslice(engine, last);
> +}
> +
>  static void execlists_dequeue(struct intel_engine_cs *engine)
>  {
>  	struct intel_engine_execlists * const execlists = &engine->execlists;
> @@ -906,6 +982,27 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>  			 */
>  			last->hw_context->lrc_desc |= CTX_DESC_FORCE_RESTORE;
>  			last = NULL;
> +		} else if (need_timeslice(engine, last) &&
> +			   !timer_pending(&engine->execlists.timer)) {
> +			GEM_TRACE("%s: expired last=%llx:%lld, prio=%d, hint=%d\n",
> +				  engine->name,
> +				  last->fence.context,
> +				  last->fence.seqno,
> +				  last->sched.attr.priority,
> +				  execlists->queue_priority_hint);
> +
> +			ring_pause(engine) = 1;
> +			defer_active(engine);
> +
> +			/*
> +			 * Unlike for preemption, if we rewind and continue
> +			 * executing the same context as previously active,
> +			 * the order of execution will remain the same and
> +			 * the tail will only advance. We do not need to
> +			 * force a full context restore, as a lite-restore
> +			 * is sufficient to resample the monotonic TAIL.
> +			 */

I would have asked about the force preemption without this fine comment.

But this is a similar as the other kind of preemption. So what happens
when the contexts are not the same?

-Mika

> +			last = NULL;
>  		} else {
>  			/*
>  			 * Otherwise if we already have a request pending
> @@ -1229,6 +1326,9 @@ static void process_csb(struct intel_engine_cs *engine)
>  				       sizeof(*execlists->pending));
>  			execlists->pending[0] = NULL;
>  
> +			if (enable_timeslice(engine))
> +				mod_timer(&execlists->timer, jiffies + 1);
> +
>  			if (!inject_preempt_hang(execlists))
>  				ring_pause(engine) = 0;
>  		} else if (status & GEN8_CTX_STATUS_PREEMPTED) {
> @@ -1299,6 +1399,15 @@ static void execlists_submission_tasklet(unsigned long data)
>  	spin_unlock_irqrestore(&engine->active.lock, flags);
>  }
>  
> +static void execlists_submission_timer(struct timer_list *timer)
> +{
> +	struct intel_engine_cs *engine =
> +		from_timer(engine, timer, execlists.timer);
> +
> +	/* Kick the tasklet for some interrupt coalescing and reset handling */
> +	tasklet_hi_schedule(&engine->execlists.tasklet);
> +}
> +
>  static void queue_request(struct intel_engine_cs *engine,
>  			  struct i915_sched_node *node,
>  			  int prio)
> @@ -2524,6 +2633,7 @@ static int gen8_init_rcs_context(struct i915_request *rq)
>  
>  static void execlists_park(struct intel_engine_cs *engine)
>  {
> +	del_timer_sync(&engine->execlists.timer);
>  	intel_engine_park(engine);
>  }
>  
> @@ -2621,6 +2731,7 @@ int intel_execlists_submission_setup(struct intel_engine_cs *engine)
>  
>  	tasklet_init(&engine->execlists.tasklet,
>  		     execlists_submission_tasklet, (unsigned long)engine);
> +	timer_setup(&engine->execlists.timer, execlists_submission_timer, 0);
>  
>  	logical_ring_default_vfuncs(engine);
>  	logical_ring_default_irqs(engine);
> diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c b/drivers/gpu/drm/i915/gt/selftest_lrc.c
> index 401e8b539297..0c97f953e908 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_lrc.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c
> @@ -79,6 +79,225 @@ static int live_sanitycheck(void *arg)
>  	return err;
>  }
>  
> +static int
> +emit_semaphore_chain(struct i915_request *rq, struct i915_vma *vma, int idx)
> +{
> +	u32 *cs;
> +
> +	cs = intel_ring_begin(rq, 10);
> +	if (IS_ERR(cs))
> +		return PTR_ERR(cs);
> +
> +	*cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
> +
> +	*cs++ = MI_SEMAPHORE_WAIT |
> +		MI_SEMAPHORE_GLOBAL_GTT |
> +		MI_SEMAPHORE_POLL |
> +		MI_SEMAPHORE_SAD_NEQ_SDD;
> +	*cs++ = 0;
> +	*cs++ = i915_ggtt_offset(vma) + 4 * idx;
> +	*cs++ = 0;
> +
> +	if (idx > 0) {
> +		*cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
> +		*cs++ = i915_ggtt_offset(vma) + 4 * (idx - 1);
> +		*cs++ = 0;
> +		*cs++ = 1;
> +	} else {
> +		*cs++ = MI_NOOP;
> +		*cs++ = MI_NOOP;
> +		*cs++ = MI_NOOP;
> +		*cs++ = MI_NOOP;
> +	}
> +
> +	*cs++ = MI_ARB_ON_OFF | MI_ARB_DISABLE;
> +
> +	intel_ring_advance(rq, cs);
> +	return 0;
> +}
> +
> +static struct i915_request *
> +semaphore_queue(struct intel_engine_cs *engine, struct i915_vma *vma, int idx)
> +{
> +	struct i915_gem_context *ctx;
> +	struct i915_request *rq;
> +	int err;
> +
> +	ctx = kernel_context(engine->i915);
> +	if (!ctx)
> +		return ERR_PTR(-ENOMEM);
> +
> +	rq = igt_request_alloc(ctx, engine);
> +	if (IS_ERR(rq))
> +		goto out_ctx;
> +
> +	err = emit_semaphore_chain(rq, vma, idx);
> +	i915_request_add(rq);
> +	if (err)
> +		rq = ERR_PTR(err);
> +
> +out_ctx:
> +	kernel_context_close(ctx);
> +	return rq;
> +}
> +
> +static int
> +release_queue(struct intel_engine_cs *engine,
> +	      struct i915_vma *vma,
> +	      int idx)
> +{
> +	struct i915_sched_attr attr = {
> +		.priority = I915_USER_PRIORITY(I915_PRIORITY_MAX),
> +	};
> +	struct i915_request *rq;
> +	u32 *cs;
> +
> +	rq = i915_request_create(engine->kernel_context);
> +	if (IS_ERR(rq))
> +		return PTR_ERR(rq);
> +
> +	cs = intel_ring_begin(rq, 4);
> +	if (IS_ERR(cs)) {
> +		i915_request_add(rq);
> +		return PTR_ERR(cs);
> +	}
> +
> +	*cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
> +	*cs++ = i915_ggtt_offset(vma) + 4 * (idx - 1);
> +	*cs++ = 0;
> +	*cs++ = 1;
> +
> +	intel_ring_advance(rq, cs);
> +	i915_request_add(rq);
> +
> +	engine->schedule(rq, &attr);
> +
> +	return 0;
> +}
> +
> +static int
> +slice_semaphore_queue(struct intel_engine_cs *outer,
> +		      struct i915_vma *vma,
> +		      int count)
> +{
> +	struct intel_engine_cs *engine;
> +	struct i915_request *head;
> +	enum intel_engine_id id;
> +	int err, i, n = 0;
> +
> +	head = semaphore_queue(outer, vma, n++);
> +	if (IS_ERR(head))
> +		return PTR_ERR(head);
> +
> +	i915_request_get(head);
> +	for_each_engine(engine, outer->i915, id) {
> +		for (i = 0; i < count; i++) {
> +			struct i915_request *rq;
> +
> +			rq = semaphore_queue(engine, vma, n++);
> +			if (IS_ERR(rq)) {
> +				err = PTR_ERR(rq);
> +				goto out;
> +			}
> +		}
> +	}
> +
> +	err = release_queue(outer, vma, n);
> +	if (err)
> +		goto out;
> +
> +	if (i915_request_wait(head,
> +			      I915_WAIT_LOCKED,
> +			      2 * RUNTIME_INFO(outer->i915)->num_engines * (count + 2) * (count + 3)) < 0) {
> +		pr_err("Failed to slice along semaphore chain of length (%d, %d)!\n",
> +		       count, n);
> +		GEM_TRACE_DUMP();
> +		i915_gem_set_wedged(outer->i915);
> +		err = -EIO;
> +	}
> +
> +out:
> +	i915_request_put(head);
> +	return err;
> +}
> +
> +static int live_timeslice_preempt(void *arg)
> +{
> +	struct drm_i915_private *i915 = arg;
> +	struct drm_i915_gem_object *obj;
> +	intel_wakeref_t wakeref;
> +	struct i915_vma *vma;
> +	void *vaddr;
> +	int err = 0;
> +	int count;
> +
> +	/*
> +	 * If a request takes too long, we would like to give other users
> +	 * a fair go on the GPU. In particular, users may create batches
> +	 * that wait upon external input, where that input may even be
> +	 * supplied by another GPU job. To avoid blocking forever, we
> +	 * need to preempt the current task and replace it with another
> +	 * ready task.
> +	 */
> +
> +	mutex_lock(&i915->drm.struct_mutex);
> +	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> +
> +	obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
> +	if (IS_ERR(obj)) {
> +		err = PTR_ERR(obj);
> +		goto err_unlock;
> +	}
> +
> +	vma = i915_vma_instance(obj, &i915->ggtt.vm, NULL);
> +	if (IS_ERR(vma)) {
> +		err = PTR_ERR(vma);
> +		goto err_obj;
> +	}
> +
> +	vaddr = i915_gem_object_pin_map(obj, I915_MAP_WC);
> +	if (IS_ERR(vaddr)) {
> +		err = PTR_ERR(vaddr);
> +		goto err_obj;
> +	}
> +
> +	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
> +	if (err)
> +		goto err_map;
> +
> +	for_each_prime_number_from(count, 1, 16) {
> +		struct intel_engine_cs *engine;
> +		enum intel_engine_id id;
> +
> +		for_each_engine(engine, i915, id) {
> +			memset(vaddr, 0, PAGE_SIZE);
> +
> +			err = slice_semaphore_queue(engine, vma, count);
> +			if (err)
> +				goto err_pin;
> +
> +			if (igt_flush_test(i915, I915_WAIT_LOCKED)) {
> +				err = -EIO;
> +				goto err_pin;
> +			}
> +		}
> +	}
> +
> +err_pin:
> +	i915_vma_unpin(vma);
> +err_map:
> +	i915_gem_object_unpin_map(obj);
> +err_obj:
> +	i915_gem_object_put(obj);
> +err_unlock:
> +	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +		err = -EIO;
> +	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> +	mutex_unlock(&i915->drm.struct_mutex);
> +
> +	return err;
> +}
> +
>  static int live_busywait_preempt(void *arg)
>  {
>  	struct drm_i915_private *i915 = arg;
> @@ -398,6 +617,9 @@ static int live_late_preempt(void *arg)
>  	if (!ctx_lo)
>  		goto err_ctx_hi;
>  
> +	/* Make sure ctx_lo stays before ctx_hi until we trigger preemption. */
> +	ctx_lo->sched.priority = I915_USER_PRIORITY(1);
> +
>  	for_each_engine(engine, i915, id) {
>  		struct igt_live_test t;
>  		struct i915_request *rq;
> @@ -1812,6 +2034,7 @@ int intel_execlists_live_selftests(struct drm_i915_private *i915)
>  {
>  	static const struct i915_subtest tests[] = {
>  		SUBTEST(live_sanitycheck),
> +		SUBTEST(live_timeslice_preempt),
>  		SUBTEST(live_busywait_preempt),
>  		SUBTEST(live_preempt),
>  		SUBTEST(live_late_preempt),
> diff --git a/drivers/gpu/drm/i915/i915_scheduler.c b/drivers/gpu/drm/i915/i915_scheduler.c
> index b1ba3e65cd52..0bd452e851d8 100644
> --- a/drivers/gpu/drm/i915/i915_scheduler.c
> +++ b/drivers/gpu/drm/i915/i915_scheduler.c
> @@ -394,6 +394,7 @@ bool __i915_sched_node_add_dependency(struct i915_sched_node *node,
>  		list_add(&dep->wait_link, &signal->waiters_list);
>  		list_add(&dep->signal_link, &node->signalers_list);
>  		dep->signaler = signal;
> +		dep->waiter = node;
>  		dep->flags = flags;
>  
>  		/* Keep track of whether anyone on this chain has a semaphore */
> diff --git a/drivers/gpu/drm/i915/i915_scheduler_types.h b/drivers/gpu/drm/i915/i915_scheduler_types.h
> index 3e309631bd0b..aad81acba9dc 100644
> --- a/drivers/gpu/drm/i915/i915_scheduler_types.h
> +++ b/drivers/gpu/drm/i915/i915_scheduler_types.h
> @@ -62,6 +62,7 @@ struct i915_sched_node {
>  
>  struct i915_dependency {
>  	struct i915_sched_node *signaler;
> +	struct i915_sched_node *waiter;
>  	struct list_head signal_link;
>  	struct list_head wait_link;
>  	struct list_head dfs_link;
> -- 
> 2.20.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/3] drm/i915/execlists: Minimalistic timeslicing
  2019-06-20 13:51   ` Mika Kuoppala
@ 2019-06-20 13:57     ` Chris Wilson
  2019-06-20 14:13       ` Mika Kuoppala
  0 siblings, 1 reply; 23+ messages in thread
From: Chris Wilson @ 2019-06-20 13:57 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx

Quoting Mika Kuoppala (2019-06-20 14:51:24)
> > +static void
> > +defer_request(struct i915_request * const rq, struct list_head * const pl)
> > +{
> > +     struct i915_dependency *p;
> > +
> > +     /*
> > +      * We want to move the interrupted request to the back of
> > +      * the round-robin list (i.e. its priority level), but
> > +      * in doing so, we must then move all requests that were in
> > +      * flight and were waiting for the interrupted request to
> > +      * be run after it again.
> > +      */
> > +     list_move_tail(&rq->sched.link, pl);
> > +
> > +     list_for_each_entry(p, &rq->sched.waiters_list, wait_link) {
> > +             struct i915_request *w =
> > +                     container_of(p->waiter, typeof(*w), sched);
> > +
> > +             /* Leave semaphores spinning on the other engines */
> > +             if (w->engine != rq->engine)
> > +                     continue;
> > +
> > +             /* No waiter should start before the active request completed */
> > +             GEM_BUG_ON(i915_request_started(w));
> > +
> > +             GEM_BUG_ON(rq_prio(w) > rq_prio(rq));
> > +             if (rq_prio(w) < rq_prio(rq))
> > +                     continue;
> > +
> > +             if (list_empty(&w->sched.link))
> > +                     continue; /* Not yet submitted; unready */
> > +
> > +             /*
> > +              * This should be very shallow as it is limited by the
> > +              * number of requests that can fit in a ring (<64) and
> 
> s/and/or ?

I think "and" works better as each context has their own ring, so it's a
multiplicative effect.

> > +              * the number of contexts that can be in flight on this
> > +              * engine.
> > +              */
> > +             defer_request(w, pl);
> 
> So the stack frame will be 64*(3*8 + preample/return) at worst case?
> can be over 2k

Ok, that makes it sound scary -- but we are well within the 8k irq
limit. (Even interrupts now have 2 pages iirc, but even at 4k we are
well within bounds.)

> > @@ -906,6 +982,27 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
> >                        */
> >                       last->hw_context->lrc_desc |= CTX_DESC_FORCE_RESTORE;
> >                       last = NULL;
> > +             } else if (need_timeslice(engine, last) &&
> > +                        !timer_pending(&engine->execlists.timer)) {
> > +                     GEM_TRACE("%s: expired last=%llx:%lld, prio=%d, hint=%d\n",
> > +                               engine->name,
> > +                               last->fence.context,
> > +                               last->fence.seqno,
> > +                               last->sched.attr.priority,
> > +                               execlists->queue_priority_hint);
> > +
> > +                     ring_pause(engine) = 1;
> > +                     defer_active(engine);
> > +
> > +                     /*
> > +                      * Unlike for preemption, if we rewind and continue
> > +                      * executing the same context as previously active,
> > +                      * the order of execution will remain the same and
> > +                      * the tail will only advance. We do not need to
> > +                      * force a full context restore, as a lite-restore
> > +                      * is sufficient to resample the monotonic TAIL.
> > +                      */
> 
> I would have asked about the force preemption without this fine comment.
> 
> But this is a similar as the other kind of preemption. So what happens
> when the contexts are not the same?

It's just a normal preemption event. The old ring regs are saved and we
don't try and scribble over them. Any future use of the old context will
have the same RING_TAIL as before or later (new request) so we will
never try to program a backwards step.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 3/3] drm/i915/execlists: Force preemption
  2019-06-20  7:05 ` [PATCH 3/3] drm/i915/execlists: Force preemption Chris Wilson
@ 2019-06-20 14:00   ` Mika Kuoppala
  2019-06-20 14:08     ` Chris Wilson
  0 siblings, 1 reply; 23+ messages in thread
From: Mika Kuoppala @ 2019-06-20 14:00 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx

Chris Wilson <chris@chris-wilson.co.uk> writes:

> If the preempted context takes too long to relinquish control, e.g. it
> is stuck inside a shader with arbitration disabled, evict that context
> with an engine reset. This ensures that preemptions are reasonably
> responsive, providing a tighter QoS for the more important context at
> the cost of flagging unresponsive contexts more frequently (i.e. instead
> of using an ~10s hangcheck, we now evict at ~10ms).  The challenge of
> lies in picking a timeout that can be reasonably serviced by HW for
> typical workloads, balancing the existing clients against the needs for
> responsiveness.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> ---
>  drivers/gpu/drm/i915/Kconfig.profile | 12 ++++++
>  drivers/gpu/drm/i915/gt/intel_lrc.c  | 56 ++++++++++++++++++++++++++--
>  2 files changed, 65 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/Kconfig.profile b/drivers/gpu/drm/i915/Kconfig.profile
> index 48df8889a88a..8273d3baafe4 100644
> --- a/drivers/gpu/drm/i915/Kconfig.profile
> +++ b/drivers/gpu/drm/i915/Kconfig.profile
> @@ -25,3 +25,15 @@ config DRM_I915_SPIN_REQUEST
>  	  May be 0 to disable the initial spin. In practice, we estimate
>  	  the cost of enabling the interrupt (if currently disabled) to be
>  	  a few microseconds.
> +
> +config DRM_I915_PREEMPT_TIMEOUT
> +	int "Preempt timeout (ms)"
> +	default 10 # milliseconds
> +	help
> +	  How long to wait (in milliseconds) for a preemption event to occur
> +	  when submitting a new context via execlists. If the current context
> +	  does not hit an arbitration point and yield to HW before the timer
> +	  expires, the HW will be reset to allow the more important context
> +	  to execute.
> +
> +	  May be 0 to disable the timeout.
> diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
> index fca79adb4aa3..e8d7deba3e49 100644
> --- a/drivers/gpu/drm/i915/gt/intel_lrc.c
> +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
> @@ -889,6 +889,15 @@ enable_timeslice(struct intel_engine_cs *engine)
>  	return last && need_timeslice(engine, last);
>  }
>  
> +static unsigned long preempt_expires(void)
> +{
> +	unsigned long timeout =

could be const

> +		msecs_to_jiffies_timeout(CONFIG_DRM_I915_PREEMPT_TIMEOUT);
> +
> +	barrier();

This needs a comment. I fail to connect the dots as jiffies
is volatile by nature.

> +	return jiffies + timeout;
> +}
> +
>  static void execlists_dequeue(struct intel_engine_cs *engine)
>  {
>  	struct intel_engine_execlists * const execlists = &engine->execlists;
> @@ -1220,6 +1229,9 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>  		*port = execlists_schedule_in(last, port - execlists->pending);
>  		memset(port + 1, 0, (last_port - port) * sizeof(*port));
>  		execlists_submit_ports(engine);
> +
> +		if (CONFIG_DRM_I915_PREEMPT_TIMEOUT)
> +			mod_timer(&execlists->timer, preempt_expires());
>  	}
>  }
>  
> @@ -1376,13 +1388,48 @@ static void process_csb(struct intel_engine_cs *engine)
>  	invalidate_csb_entries(&buf[0], &buf[num_entries - 1]);
>  }
>  
> -static void __execlists_submission_tasklet(struct intel_engine_cs *const engine)
> +static bool __execlists_submission_tasklet(struct intel_engine_cs *const engine)
>  {
>  	lockdep_assert_held(&engine->active.lock);
>  
>  	process_csb(engine);
> -	if (!engine->execlists.pending[0])
> +	if (!engine->execlists.pending[0]) {
>  		execlists_dequeue(engine);
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static void preempt_reset(struct intel_engine_cs *engine)
> +{
> +	const unsigned int bit = I915_RESET_ENGINE + engine->id;
> +	unsigned long *lock = &engine->i915->gpu_error.flags;
> +
> +	if (test_and_set_bit(bit, lock))
> +		return;
> +
> +	tasklet_disable_nosync(&engine->execlists.tasklet);
> +	spin_unlock(&engine->active.lock);
> +

Why do we need to drop the lock?
-Mika

> +	i915_reset_engine(engine, "preemption time out");
> +
> +	spin_lock(&engine->active.lock);
> +	tasklet_enable(&engine->execlists.tasklet);
> +
> +	clear_bit(bit, lock);
> +	wake_up_bit(lock, bit);
> +}
> +
> +static bool preempt_timeout(struct intel_engine_cs *const engine)
> +{
> +	if (!CONFIG_DRM_I915_PREEMPT_TIMEOUT)
> +		return false;
> +
> +	if (!intel_engine_has_preemption(engine))
> +		return false;
> +
> +	return !timer_pending(&engine->execlists.timer);
>  }
>  
>  /*
> @@ -1395,7 +1442,10 @@ static void execlists_submission_tasklet(unsigned long data)
>  	unsigned long flags;
>  
>  	spin_lock_irqsave(&engine->active.lock, flags);
> -	__execlists_submission_tasklet(engine);
> +
> +	if (!__execlists_submission_tasklet(engine) && preempt_timeout(engine))
> +		preempt_reset(engine);
> +
>  	spin_unlock_irqrestore(&engine->active.lock, flags);
>  }
>  
> -- 
> 2.20.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 3/3] drm/i915/execlists: Force preemption
  2019-06-20 14:00   ` Mika Kuoppala
@ 2019-06-20 14:08     ` Chris Wilson
  2019-06-20 14:19       ` Mika Kuoppala
  0 siblings, 1 reply; 23+ messages in thread
From: Chris Wilson @ 2019-06-20 14:08 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx

Quoting Mika Kuoppala (2019-06-20 15:00:44)
> Chris Wilson <chris@chris-wilson.co.uk> writes:
> 
> > If the preempted context takes too long to relinquish control, e.g. it
> > is stuck inside a shader with arbitration disabled, evict that context
> > with an engine reset. This ensures that preemptions are reasonably
> > responsive, providing a tighter QoS for the more important context at
> > the cost of flagging unresponsive contexts more frequently (i.e. instead
> > of using an ~10s hangcheck, we now evict at ~10ms).  The challenge of
> > lies in picking a timeout that can be reasonably serviced by HW for
> > typical workloads, balancing the existing clients against the needs for
> > responsiveness.
> >
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> > Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> > ---
> >  drivers/gpu/drm/i915/Kconfig.profile | 12 ++++++
> >  drivers/gpu/drm/i915/gt/intel_lrc.c  | 56 ++++++++++++++++++++++++++--
> >  2 files changed, 65 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i915/Kconfig.profile b/drivers/gpu/drm/i915/Kconfig.profile
> > index 48df8889a88a..8273d3baafe4 100644
> > --- a/drivers/gpu/drm/i915/Kconfig.profile
> > +++ b/drivers/gpu/drm/i915/Kconfig.profile
> > @@ -25,3 +25,15 @@ config DRM_I915_SPIN_REQUEST
> >         May be 0 to disable the initial spin. In practice, we estimate
> >         the cost of enabling the interrupt (if currently disabled) to be
> >         a few microseconds.
> > +
> > +config DRM_I915_PREEMPT_TIMEOUT
> > +     int "Preempt timeout (ms)"
> > +     default 10 # milliseconds
> > +     help
> > +       How long to wait (in milliseconds) for a preemption event to occur
> > +       when submitting a new context via execlists. If the current context
> > +       does not hit an arbitration point and yield to HW before the timer
> > +       expires, the HW will be reset to allow the more important context
> > +       to execute.
> > +
> > +       May be 0 to disable the timeout.
> > diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
> > index fca79adb4aa3..e8d7deba3e49 100644
> > --- a/drivers/gpu/drm/i915/gt/intel_lrc.c
> > +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
> > @@ -889,6 +889,15 @@ enable_timeslice(struct intel_engine_cs *engine)
> >       return last && need_timeslice(engine, last);
> >  }
> >  
> > +static unsigned long preempt_expires(void)
> > +{
> > +     unsigned long timeout =
> 
> could be const
> 
> > +             msecs_to_jiffies_timeout(CONFIG_DRM_I915_PREEMPT_TIMEOUT);
> > +
> > +     barrier();
> 
> This needs a comment. I fail to connect the dots as jiffies
> is volatile by nature.

It's just crossing the 't' and dotting the 'i'. What I was thinking was
we don't want the compiler to load jiffies then compute the timeout. So
barrier() there says that timeout is always computed first. Now since it
is likely to be a function call (but I'm trying to find a way to let it
precompute the constant), it will always be precomputed, but who trusts
a compiler.

> > +     return jiffies + timeout;
> > +}
> > +
> >  static void execlists_dequeue(struct intel_engine_cs *engine)
> >  {
> >       struct intel_engine_execlists * const execlists = &engine->execlists;
> > @@ -1220,6 +1229,9 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
> >               *port = execlists_schedule_in(last, port - execlists->pending);
> >               memset(port + 1, 0, (last_port - port) * sizeof(*port));
> >               execlists_submit_ports(engine);
> > +
> > +             if (CONFIG_DRM_I915_PREEMPT_TIMEOUT)
> > +                     mod_timer(&execlists->timer, preempt_expires());
> >       }
> >  }
> >  
> > @@ -1376,13 +1388,48 @@ static void process_csb(struct intel_engine_cs *engine)
> >       invalidate_csb_entries(&buf[0], &buf[num_entries - 1]);
> >  }
> >  
> > -static void __execlists_submission_tasklet(struct intel_engine_cs *const engine)
> > +static bool __execlists_submission_tasklet(struct intel_engine_cs *const engine)
> >  {
> >       lockdep_assert_held(&engine->active.lock);
> >  
> >       process_csb(engine);
> > -     if (!engine->execlists.pending[0])
> > +     if (!engine->execlists.pending[0]) {
> >               execlists_dequeue(engine);
> > +             return true;
> > +     }
> > +
> > +     return false;
> > +}
> > +
> > +static void preempt_reset(struct intel_engine_cs *engine)
> > +{
> > +     const unsigned int bit = I915_RESET_ENGINE + engine->id;
> > +     unsigned long *lock = &engine->i915->gpu_error.flags;
> > +
> > +     if (test_and_set_bit(bit, lock))
> > +             return;
> > +
> > +     tasklet_disable_nosync(&engine->execlists.tasklet);
> > +     spin_unlock(&engine->active.lock);
> > +
> 
> Why do we need to drop the lock?

We take it again inside the reset, and I am far too lazy to lift it to
the caller :) Disabling the tasklet will prevent other threads from
submitting as we drop the lock.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/3] drm/i915/execlists: Minimalistic timeslicing
  2019-06-20 13:57     ` Chris Wilson
@ 2019-06-20 14:13       ` Mika Kuoppala
  0 siblings, 0 replies; 23+ messages in thread
From: Mika Kuoppala @ 2019-06-20 14:13 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx

Chris Wilson <chris@chris-wilson.co.uk> writes:

> Quoting Mika Kuoppala (2019-06-20 14:51:24)
>> > +static void
>> > +defer_request(struct i915_request * const rq, struct list_head * const pl)
>> > +{
>> > +     struct i915_dependency *p;
>> > +
>> > +     /*
>> > +      * We want to move the interrupted request to the back of
>> > +      * the round-robin list (i.e. its priority level), but
>> > +      * in doing so, we must then move all requests that were in
>> > +      * flight and were waiting for the interrupted request to
>> > +      * be run after it again.
>> > +      */
>> > +     list_move_tail(&rq->sched.link, pl);
>> > +
>> > +     list_for_each_entry(p, &rq->sched.waiters_list, wait_link) {
>> > +             struct i915_request *w =
>> > +                     container_of(p->waiter, typeof(*w), sched);
>> > +
>> > +             /* Leave semaphores spinning on the other engines */
>> > +             if (w->engine != rq->engine)
>> > +                     continue;
>> > +
>> > +             /* No waiter should start before the active request completed */
>> > +             GEM_BUG_ON(i915_request_started(w));
>> > +
>> > +             GEM_BUG_ON(rq_prio(w) > rq_prio(rq));
>> > +             if (rq_prio(w) < rq_prio(rq))
>> > +                     continue;
>> > +
>> > +             if (list_empty(&w->sched.link))
>> > +                     continue; /* Not yet submitted; unready */
>> > +
>> > +             /*
>> > +              * This should be very shallow as it is limited by the
>> > +              * number of requests that can fit in a ring (<64) and
>> 
>> s/and/or ?
>
> I think "and" works better as each context has their own ring, so it's a
> multiplicative effect.
>

I jumped. But got clarity on irc that this are the contexts in flight.

>> > +              * the number of contexts that can be in flight on this
>> > +              * engine.
>> > +              */
>> > +             defer_request(w, pl);
>> 
>> So the stack frame will be 64*(3*8 + preample/return) at worst case?
>> can be over 2k
>
> Ok, that makes it sound scary -- but we are well within the 8k irq
> limit. (Even interrupts now have 2 pages iirc, but even at 4k we are
> well within bounds.)
>

Should be safe.

>> > @@ -906,6 +982,27 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>> >                        */
>> >                       last->hw_context->lrc_desc |= CTX_DESC_FORCE_RESTORE;
>> >                       last = NULL;
>> > +             } else if (need_timeslice(engine, last) &&
>> > +                        !timer_pending(&engine->execlists.timer)) {
>> > +                     GEM_TRACE("%s: expired last=%llx:%lld, prio=%d, hint=%d\n",
>> > +                               engine->name,
>> > +                               last->fence.context,
>> > +                               last->fence.seqno,
>> > +                               last->sched.attr.priority,
>> > +                               execlists->queue_priority_hint);
>> > +
>> > +                     ring_pause(engine) = 1;
>> > +                     defer_active(engine);
>> > +
>> > +                     /*
>> > +                      * Unlike for preemption, if we rewind and continue
>> > +                      * executing the same context as previously active,
>> > +                      * the order of execution will remain the same and
>> > +                      * the tail will only advance. We do not need to
>> > +                      * force a full context restore, as a lite-restore
>> > +                      * is sufficient to resample the monotonic TAIL.
>> > +                      */
>> 
>> I would have asked about the force preemption without this fine comment.
>> 
>> But this is a similar as the other kind of preemption. So what happens
>> when the contexts are not the same?
>
> It's just a normal preemption event. The old ring regs are saved and we
> don't try and scribble over them. Any future use of the old context will
> have the same RING_TAIL as before or later (new request) so we will
> never try to program a backwards step.

Ok,

Reviewed-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>

> -Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 3/3] drm/i915/execlists: Force preemption
  2019-06-20 14:08     ` Chris Wilson
@ 2019-06-20 14:19       ` Mika Kuoppala
  0 siblings, 0 replies; 23+ messages in thread
From: Mika Kuoppala @ 2019-06-20 14:19 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx

Chris Wilson <chris@chris-wilson.co.uk> writes:

> Quoting Mika Kuoppala (2019-06-20 15:00:44)
>> Chris Wilson <chris@chris-wilson.co.uk> writes:
>> 
>> > If the preempted context takes too long to relinquish control, e.g. it
>> > is stuck inside a shader with arbitration disabled, evict that context
>> > with an engine reset. This ensures that preemptions are reasonably
>> > responsive, providing a tighter QoS for the more important context at
>> > the cost of flagging unresponsive contexts more frequently (i.e. instead
>> > of using an ~10s hangcheck, we now evict at ~10ms).  The challenge of
>> > lies in picking a timeout that can be reasonably serviced by HW for
>> > typical workloads, balancing the existing clients against the needs for
>> > responsiveness.
>> >
>> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>> > Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com>
>> > Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
>> > ---
>> >  drivers/gpu/drm/i915/Kconfig.profile | 12 ++++++
>> >  drivers/gpu/drm/i915/gt/intel_lrc.c  | 56 ++++++++++++++++++++++++++--
>> >  2 files changed, 65 insertions(+), 3 deletions(-)
>> >
>> > diff --git a/drivers/gpu/drm/i915/Kconfig.profile b/drivers/gpu/drm/i915/Kconfig.profile
>> > index 48df8889a88a..8273d3baafe4 100644
>> > --- a/drivers/gpu/drm/i915/Kconfig.profile
>> > +++ b/drivers/gpu/drm/i915/Kconfig.profile
>> > @@ -25,3 +25,15 @@ config DRM_I915_SPIN_REQUEST
>> >         May be 0 to disable the initial spin. In practice, we estimate
>> >         the cost of enabling the interrupt (if currently disabled) to be
>> >         a few microseconds.
>> > +
>> > +config DRM_I915_PREEMPT_TIMEOUT
>> > +     int "Preempt timeout (ms)"
>> > +     default 10 # milliseconds
>> > +     help
>> > +       How long to wait (in milliseconds) for a preemption event to occur
>> > +       when submitting a new context via execlists. If the current context
>> > +       does not hit an arbitration point and yield to HW before the timer
>> > +       expires, the HW will be reset to allow the more important context
>> > +       to execute.
>> > +
>> > +       May be 0 to disable the timeout.
>> > diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
>> > index fca79adb4aa3..e8d7deba3e49 100644
>> > --- a/drivers/gpu/drm/i915/gt/intel_lrc.c
>> > +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
>> > @@ -889,6 +889,15 @@ enable_timeslice(struct intel_engine_cs *engine)
>> >       return last && need_timeslice(engine, last);
>> >  }
>> >  
>> > +static unsigned long preempt_expires(void)
>> > +{
>> > +     unsigned long timeout =
>> 
>> could be const
>> 
>> > +             msecs_to_jiffies_timeout(CONFIG_DRM_I915_PREEMPT_TIMEOUT);
>> > +
>> > +     barrier();
>> 
>> This needs a comment. I fail to connect the dots as jiffies
>> is volatile by nature.
>
> It's just crossing the 't' and dotting the 'i'. What I was thinking was
> we don't want the compiler to load jiffies then compute the timeout. So
> barrier() there says that timeout is always computed first. Now since it
> is likely to be a function call (but I'm trying to find a way to let it
> precompute the constant), it will always be precomputed, but who trusts
> a compiler.
>
>> > +     return jiffies + timeout;
>> > +}
>> > +
>> >  static void execlists_dequeue(struct intel_engine_cs *engine)
>> >  {
>> >       struct intel_engine_execlists * const execlists = &engine->execlists;
>> > @@ -1220,6 +1229,9 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>> >               *port = execlists_schedule_in(last, port - execlists->pending);
>> >               memset(port + 1, 0, (last_port - port) * sizeof(*port));
>> >               execlists_submit_ports(engine);
>> > +
>> > +             if (CONFIG_DRM_I915_PREEMPT_TIMEOUT)
>> > +                     mod_timer(&execlists->timer, preempt_expires());
>> >       }
>> >  }
>> >  
>> > @@ -1376,13 +1388,48 @@ static void process_csb(struct intel_engine_cs *engine)
>> >       invalidate_csb_entries(&buf[0], &buf[num_entries - 1]);
>> >  }
>> >  
>> > -static void __execlists_submission_tasklet(struct intel_engine_cs *const engine)
>> > +static bool __execlists_submission_tasklet(struct intel_engine_cs *const engine)
>> >  {
>> >       lockdep_assert_held(&engine->active.lock);
>> >  
>> >       process_csb(engine);
>> > -     if (!engine->execlists.pending[0])
>> > +     if (!engine->execlists.pending[0]) {
>> >               execlists_dequeue(engine);
>> > +             return true;
>> > +     }
>> > +
>> > +     return false;
>> > +}
>> > +
>> > +static void preempt_reset(struct intel_engine_cs *engine)
>> > +{
>> > +     const unsigned int bit = I915_RESET_ENGINE + engine->id;
>> > +     unsigned long *lock = &engine->i915->gpu_error.flags;
>> > +
>> > +     if (test_and_set_bit(bit, lock))
>> > +             return;
>> > +
>> > +     tasklet_disable_nosync(&engine->execlists.tasklet);
>> > +     spin_unlock(&engine->active.lock);
>> > +
>> 
>> Why do we need to drop the lock?
>
> We take it again inside the reset, and I am far too lazy to lift it to
> the caller :) Disabling the tasklet will prevent other threads from
> submitting as we drop the lock.

With the barrier commented,

Reviewed-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>

Ok, the way you got to timeslicing with 2 last patches was
very elegant, surpricingly so.

Now lets hope I wasn't completely fooled by the first one.
There is atleast somewhat reassuring amount of CI cycles
behind these at this stage.
-Mika
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

* ✗ Fi.CI.IGT: failure for series starting with drm/i915/execlists: Preempt-to-busy (rev2)
  2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
                   ` (9 preceding siblings ...)
  2019-06-20 13:16 ` Patchwork
@ 2019-06-20 15:38 ` Patchwork
  10 siblings, 0 replies; 23+ messages in thread
From: Patchwork @ 2019-06-20 15:38 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with drm/i915/execlists: Preempt-to-busy (rev2)
URL   : https://patchwork.freedesktop.org/series/62431/
State : failure

== Summary ==

CI Bug Log - changes from CI_DRM_6312_full -> Patchwork_13361_full
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with Patchwork_13361_full absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in Patchwork_13361_full, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in Patchwork_13361_full:

### IGT changes ###

#### Possible regressions ####

  * igt@gem_exec_await@wide-contexts:
    - shard-kbl:          [PASS][1] -> [FAIL][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-kbl4/igt@gem_exec_await@wide-contexts.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-kbl1/igt@gem_exec_await@wide-contexts.html

  

### Piglit changes ###

#### Possible regressions ####

  * spec@arb_shader_image_load_store@shader-mem-barrier (NEW):
    - pig-glk-j5005:      NOTRUN -> [FAIL][3] +2 similar issues
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/pig-glk-j5005/spec@arb_shader_image_load_store@shader-mem-barrier.html
    - pig-skl-6260u:      NOTRUN -> [FAIL][4]
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/pig-skl-6260u/spec@arb_shader_image_load_store@shader-mem-barrier.html

  
New tests
---------

  New tests have been introduced between CI_DRM_6312_full and Patchwork_13361_full:

### New Piglit tests (3) ###

  * spec@arb_shader_image_load_store@shader-mem-barrier:
    - Statuses : 2 fail(s)
    - Exec time: [0.13, 0.18] s

  * spec@ext_transform_feedback@order arrays points:
    - Statuses : 1 fail(s)
    - Exec time: [0.11] s

  * spec@glsl-1.30@execution@fs-execution-ordering:
    - Statuses : 1 fail(s)
    - Exec time: [0.52] s

  

Known issues
------------

  Here are the changes found in Patchwork_13361_full that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_eio@unwedge-stress:
    - shard-snb:          [PASS][5] -> [FAIL][6] ([fdo#109661]) +1 similar issue
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-snb5/igt@gem_eio@unwedge-stress.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-snb5/igt@gem_eio@unwedge-stress.html

  * igt@gem_eio@wait-wedge-10ms:
    - shard-apl:          [PASS][7] -> [DMESG-WARN][8] ([fdo#110913 ]) +1 similar issue
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-apl7/igt@gem_eio@wait-wedge-10ms.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-apl3/igt@gem_eio@wait-wedge-10ms.html

  * igt@gem_exec_balancer@smoke:
    - shard-iclb:         [PASS][9] -> [SKIP][10] ([fdo#110854])
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb1/igt@gem_exec_balancer@smoke.html
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb5/igt@gem_exec_balancer@smoke.html

  * igt@gem_persistent_relocs@forked-faulting-reloc-thrashing:
    - shard-snb:          [PASS][11] -> [DMESG-WARN][12] ([fdo#110789] / [fdo#110913 ])
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-snb6/igt@gem_persistent_relocs@forked-faulting-reloc-thrashing.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-snb4/igt@gem_persistent_relocs@forked-faulting-reloc-thrashing.html

  * igt@gem_persistent_relocs@forked-interruptible-thrashing:
    - shard-kbl:          [PASS][13] -> [DMESG-WARN][14] ([fdo#110913 ])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-kbl1/igt@gem_persistent_relocs@forked-interruptible-thrashing.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-kbl1/igt@gem_persistent_relocs@forked-interruptible-thrashing.html

  * igt@gem_tiled_swapping@non-threaded:
    - shard-kbl:          [PASS][15] -> [DMESG-WARN][16] ([fdo#108686])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-kbl4/igt@gem_tiled_swapping@non-threaded.html
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-kbl3/igt@gem_tiled_swapping@non-threaded.html

  * igt@gem_userptr_blits@sync-unmap-cycles:
    - shard-snb:          [PASS][17] -> [DMESG-WARN][18] ([fdo#110913 ])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-snb2/igt@gem_userptr_blits@sync-unmap-cycles.html
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-snb7/igt@gem_userptr_blits@sync-unmap-cycles.html

  * igt@i915_selftest@live_hangcheck:
    - shard-snb:          [PASS][19] -> [INCOMPLETE][20] ([fdo#105411])
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-snb2/igt@i915_selftest@live_hangcheck.html
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-snb7/igt@i915_selftest@live_hangcheck.html

  * igt@kms_cursor_legacy@2x-long-cursor-vs-flip-legacy:
    - shard-hsw:          [PASS][21] -> [FAIL][22] ([fdo#105767])
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-hsw7/igt@kms_cursor_legacy@2x-long-cursor-vs-flip-legacy.html
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-hsw8/igt@kms_cursor_legacy@2x-long-cursor-vs-flip-legacy.html

  * igt@kms_flip@2x-flip-vs-expired-vblank:
    - shard-glk:          [PASS][23] -> [FAIL][24] ([fdo#105363])
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-glk8/igt@kms_flip@2x-flip-vs-expired-vblank.html
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-glk5/igt@kms_flip@2x-flip-vs-expired-vblank.html

  * igt@kms_flip@flip-vs-suspend:
    - shard-skl:          [PASS][25] -> [INCOMPLETE][26] ([fdo#109507])
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-skl8/igt@kms_flip@flip-vs-suspend.html
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-skl8/igt@kms_flip@flip-vs-suspend.html
    - shard-hsw:          [PASS][27] -> [INCOMPLETE][28] ([fdo#103540])
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-hsw5/igt@kms_flip@flip-vs-suspend.html
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-hsw1/igt@kms_flip@flip-vs-suspend.html
    - shard-glk:          [PASS][29] -> [INCOMPLETE][30] ([fdo#103359] / [k.org#198133])
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-glk9/igt@kms_flip@flip-vs-suspend.html
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-glk4/igt@kms_flip@flip-vs-suspend.html

  * igt@kms_frontbuffer_tracking@fbc-1p-offscren-pri-shrfb-draw-pwrite:
    - shard-iclb:         [PASS][31] -> [FAIL][32] ([fdo#103167]) +7 similar issues
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb4/igt@kms_frontbuffer_tracking@fbc-1p-offscren-pri-shrfb-draw-pwrite.html
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb6/igt@kms_frontbuffer_tracking@fbc-1p-offscren-pri-shrfb-draw-pwrite.html

  * igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-blt:
    - shard-hsw:          [PASS][33] -> [SKIP][34] ([fdo#109271]) +7 similar issues
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-hsw6/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-blt.html
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-hsw1/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-blt.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-mmap-wc:
    - shard-skl:          [PASS][35] -> [FAIL][36] ([fdo#103167])
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-skl3/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-mmap-wc.html
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-skl10/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-mmap-wc.html

  * igt@kms_pipe_crc_basic@hang-read-crc-pipe-c:
    - shard-skl:          [PASS][37] -> [FAIL][38] ([fdo#103191])
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-skl3/igt@kms_pipe_crc_basic@hang-read-crc-pipe-c.html
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-skl9/igt@kms_pipe_crc_basic@hang-read-crc-pipe-c.html

  * igt@kms_plane@plane-panning-bottom-right-suspend-pipe-a-planes:
    - shard-apl:          [PASS][39] -> [DMESG-WARN][40] ([fdo#108566]) +2 similar issues
   [39]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-apl6/igt@kms_plane@plane-panning-bottom-right-suspend-pipe-a-planes.html
   [40]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-apl5/igt@kms_plane@plane-panning-bottom-right-suspend-pipe-a-planes.html

  * igt@kms_plane_alpha_blend@pipe-c-constant-alpha-min:
    - shard-skl:          [PASS][41] -> [FAIL][42] ([fdo#108145]) +1 similar issue
   [41]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-skl3/igt@kms_plane_alpha_blend@pipe-c-constant-alpha-min.html
   [42]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-skl9/igt@kms_plane_alpha_blend@pipe-c-constant-alpha-min.html

  * igt@kms_psr2_su@frontbuffer:
    - shard-iclb:         [PASS][43] -> [SKIP][44] ([fdo#109642])
   [43]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb2/igt@kms_psr2_su@frontbuffer.html
   [44]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb7/igt@kms_psr2_su@frontbuffer.html

  * igt@kms_psr@psr2_cursor_blt:
    - shard-iclb:         [PASS][45] -> [SKIP][46] ([fdo#109441]) +1 similar issue
   [45]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb2/igt@kms_psr@psr2_cursor_blt.html
   [46]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb3/igt@kms_psr@psr2_cursor_blt.html

  
#### Possible fixes ####

  * igt@gem_eio@wait-10ms:
    - shard-apl:          [DMESG-WARN][47] ([fdo#110913 ]) -> [PASS][48] +1 similar issue
   [47]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-apl8/igt@gem_eio@wait-10ms.html
   [48]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-apl7/igt@gem_eio@wait-10ms.html

  * igt@gem_exec_schedule@semaphore-resolve:
    - shard-skl:          [FAIL][49] ([fdo#110519] / [fdo#110946]) -> [PASS][50]
   [49]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-skl6/igt@gem_exec_schedule@semaphore-resolve.html
   [50]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-skl7/igt@gem_exec_schedule@semaphore-resolve.html
    - shard-kbl:          [FAIL][51] ([fdo#110519] / [fdo#110946]) -> [PASS][52]
   [51]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-kbl2/igt@gem_exec_schedule@semaphore-resolve.html
   [52]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-kbl3/igt@gem_exec_schedule@semaphore-resolve.html
    - shard-apl:          [FAIL][53] ([fdo#110519]) -> [PASS][54]
   [53]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-apl1/igt@gem_exec_schedule@semaphore-resolve.html
   [54]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-apl1/igt@gem_exec_schedule@semaphore-resolve.html
    - shard-glk:          [FAIL][55] ([fdo#110519] / [fdo#110946]) -> [PASS][56]
   [55]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-glk7/igt@gem_exec_schedule@semaphore-resolve.html
   [56]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-glk9/igt@gem_exec_schedule@semaphore-resolve.html
    - shard-iclb:         [FAIL][57] ([fdo#110519] / [fdo#110946]) -> [PASS][58]
   [57]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb5/igt@gem_exec_schedule@semaphore-resolve.html
   [58]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb4/igt@gem_exec_schedule@semaphore-resolve.html

  * igt@gem_persistent_relocs@forked-interruptible-thrashing:
    - shard-snb:          [DMESG-WARN][59] ([fdo#110789] / [fdo#110913 ]) -> [PASS][60]
   [59]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-snb4/igt@gem_persistent_relocs@forked-interruptible-thrashing.html
   [60]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-snb6/igt@gem_persistent_relocs@forked-interruptible-thrashing.html

  * igt@i915_selftest@live_hangcheck:
    - shard-iclb:         [INCOMPLETE][61] ([fdo#107713] / [fdo#108569]) -> [PASS][62]
   [61]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb1/igt@i915_selftest@live_hangcheck.html
   [62]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb5/igt@i915_selftest@live_hangcheck.html

  * igt@kms_cursor_edge_walk@pipe-b-128x128-top-edge:
    - shard-snb:          [SKIP][63] ([fdo#109271] / [fdo#109278]) -> [PASS][64]
   [63]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-snb2/igt@kms_cursor_edge_walk@pipe-b-128x128-top-edge.html
   [64]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-snb5/igt@kms_cursor_edge_walk@pipe-b-128x128-top-edge.html

  * igt@kms_cursor_legacy@2x-long-flip-vs-cursor-atomic:
    - shard-glk:          [FAIL][65] ([fdo#104873]) -> [PASS][66]
   [65]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-glk8/igt@kms_cursor_legacy@2x-long-flip-vs-cursor-atomic.html
   [66]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-glk1/igt@kms_cursor_legacy@2x-long-flip-vs-cursor-atomic.html

  * igt@kms_dp_dsc@basic-dsc-enable-edp:
    - shard-iclb:         [SKIP][67] ([fdo#109349]) -> [PASS][68]
   [67]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb7/igt@kms_dp_dsc@basic-dsc-enable-edp.html
   [68]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb2/igt@kms_dp_dsc@basic-dsc-enable-edp.html

  * igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-pwrite:
    - shard-hsw:          [SKIP][69] ([fdo#109271]) -> [PASS][70] +33 similar issues
   [69]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-hsw1/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-pwrite.html
   [70]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-hsw4/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-pwrite.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-render:
    - shard-iclb:         [FAIL][71] ([fdo#103167]) -> [PASS][72] +6 similar issues
   [71]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb5/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-render.html
   [72]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb3/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-render.html

  * igt@kms_pipe_crc_basic@suspend-read-crc-pipe-a:
    - shard-apl:          [DMESG-WARN][73] ([fdo#108566]) -> [PASS][74]
   [73]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-apl5/igt@kms_pipe_crc_basic@suspend-read-crc-pipe-a.html
   [74]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-apl6/igt@kms_pipe_crc_basic@suspend-read-crc-pipe-a.html

  * igt@kms_pipe_crc_basic@suspend-read-crc-pipe-c:
    - shard-skl:          [INCOMPLETE][75] ([fdo#104108]) -> [PASS][76]
   [75]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-skl4/igt@kms_pipe_crc_basic@suspend-read-crc-pipe-c.html
   [76]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-skl8/igt@kms_pipe_crc_basic@suspend-read-crc-pipe-c.html

  * igt@kms_plane@plane-panning-bottom-right-pipe-b-planes:
    - shard-snb:          [SKIP][77] ([fdo#109271]) -> [PASS][78] +1 similar issue
   [77]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-snb2/igt@kms_plane@plane-panning-bottom-right-pipe-b-planes.html
   [78]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-snb5/igt@kms_plane@plane-panning-bottom-right-pipe-b-planes.html

  * igt@kms_psr@no_drrs:
    - shard-iclb:         [FAIL][79] ([fdo#108341]) -> [PASS][80]
   [79]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb1/igt@kms_psr@no_drrs.html
   [80]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb5/igt@kms_psr@no_drrs.html

  * igt@kms_psr@psr2_sprite_plane_move:
    - shard-iclb:         [SKIP][81] ([fdo#109441]) -> [PASS][82] +1 similar issue
   [81]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb6/igt@kms_psr@psr2_sprite_plane_move.html
   [82]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb2/igt@kms_psr@psr2_sprite_plane_move.html

  * igt@kms_setmode@basic:
    - shard-kbl:          [FAIL][83] ([fdo#99912]) -> [PASS][84]
   [83]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-kbl1/igt@kms_setmode@basic.html
   [84]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-kbl1/igt@kms_setmode@basic.html

  * igt@kms_sysfs_edid_timing:
    - shard-iclb:         [FAIL][85] ([fdo#100047]) -> [PASS][86]
   [85]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6312/shard-iclb2/igt@kms_sysfs_edid_timing.html
   [86]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/shard-iclb4/igt@kms_sysfs_edid_timing.html

  
  [fdo#100047]: https://bugs.freedesktop.org/show_bug.cgi?id=100047
  [fdo#103167]: https://bugs.freedesktop.org/show_bug.cgi?id=103167
  [fdo#103191]: https://bugs.freedesktop.org/show_bug.cgi?id=103191
  [fdo#103359]: https://bugs.freedesktop.org/show_bug.cgi?id=103359
  [fdo#103540]: https://bugs.freedesktop.org/show_bug.cgi?id=103540
  [fdo#104108]: https://bugs.freedesktop.org/show_bug.cgi?id=104108
  [fdo#104873]: https://bugs.freedesktop.org/show_bug.cgi?id=104873
  [fdo#105363]: https://bugs.freedesktop.org/show_bug.cgi?id=105363
  [fdo#105411]: https://bugs.freedesktop.org/show_bug.cgi?id=105411
  [fdo#105767]: https://bugs.freedesktop.org/show_bug.cgi?id=105767
  [fdo#107713]: https://bugs.freedesktop.org/show_bug.cgi?id=107713
  [fdo#108145]: https://bugs.freedesktop.org/show_bug.cgi?id=108145
  [fdo#108341]: https://bugs.freedesktop.org/show_bug.cgi?id=108341
  [fdo#108566]: https://bugs.freedesktop.org/show_bug.cgi?id=108566
  [fdo#108569]: https://bugs.freedesktop.org/show_bug.cgi?id=108569
  [fdo#108686]: https://bugs.freedesktop.org/show_bug.cgi?id=108686
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109278]: https://bugs.freedesktop.org/show_bug.cgi?id=109278
  [fdo#109349]: https://bugs.freedesktop.org/show_bug.cgi?id=109349
  [fdo#109441]: https://bugs.freedesktop.org/show_bug.cgi?id=109441
  [fdo#109507]: https://bugs.freedesktop.org/show_bug.cgi?id=109507
  [fdo#109642]: https://bugs.freedesktop.org/show_bug.cgi?id=109642
  [fdo#109661]: https://bugs.freedesktop.org/show_bug.cgi?id=109661
  [fdo#110519]: https://bugs.freedesktop.org/show_bug.cgi?id=110519
  [fdo#110789]: https://bugs.freedesktop.org/show_bug.cgi?id=110789
  [fdo#110854]: https://bugs.freedesktop.org/show_bug.cgi?id=110854
  [fdo#110913 ]: https://bugs.freedesktop.org/show_bug.cgi?id=110913 
  [fdo#110946]: https://bugs.freedesktop.org/show_bug.cgi?id=110946
  [fdo#99912]: https://bugs.freedesktop.org/show_bug.cgi?id=99912
  [k.org#198133]: https://bugzilla.kernel.org/show_bug.cgi?id=198133


Participating hosts (10 -> 10)
------------------------------

  No changes in participating hosts


Build changes
-------------

  * Linux: CI_DRM_6312 -> Patchwork_13361

  CI_DRM_6312: 034e3ac6a2d180d188da927388b60c7e62c5655b @ git://anongit.freedesktop.org/gfx-ci/linux
  IGT_5061: c88ced79a7b71aec58f1d9c5c599ac2f431bcf7a @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
  Patchwork_13361: bfa0c571526c8b843afc7e42fe124e39ff9d81d3 @ git://anongit.freedesktop.org/gfx-ci/linux
  piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_13361/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2019-06-20 15:38 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-06-20  7:05 [PATCH 1/3] drm/i915/execlists: Preempt-to-busy Chris Wilson
2019-06-20  7:05 ` [PATCH 2/3] drm/i915/execlists: Minimalistic timeslicing Chris Wilson
2019-06-20 13:51   ` Mika Kuoppala
2019-06-20 13:57     ` Chris Wilson
2019-06-20 14:13       ` Mika Kuoppala
2019-06-20  7:05 ` [PATCH 3/3] drm/i915/execlists: Force preemption Chris Wilson
2019-06-20 14:00   ` Mika Kuoppala
2019-06-20 14:08     ` Chris Wilson
2019-06-20 14:19       ` Mika Kuoppala
2019-06-20  7:46 ` ✗ Fi.CI.CHECKPATCH: warning for series starting with [1/3] drm/i915/execlists: Preempt-to-busy Patchwork
2019-06-20  7:48 ` ✗ Fi.CI.SPARSE: " Patchwork
2019-06-20  8:09 ` ✗ Fi.CI.BAT: failure " Patchwork
2019-06-20  8:13   ` Chris Wilson
2019-06-20  8:15     ` Chris Wilson
2019-06-20  8:24 ` [PATCH] " Chris Wilson
2019-06-20 12:41   ` Mika Kuoppala
2019-06-20 13:01     ` Chris Wilson
2019-06-20 13:23       ` Mika Kuoppala
2019-06-20  9:23 ` ✗ Fi.CI.CHECKPATCH: warning for series starting with drm/i915/execlists: Preempt-to-busy (rev2) Patchwork
2019-06-20  9:25 ` ✗ Fi.CI.SPARSE: " Patchwork
2019-06-20 12:52 ` ✓ Fi.CI.BAT: success " Patchwork
2019-06-20 13:16 ` Patchwork
2019-06-20 15:38 ` ✗ Fi.CI.IGT: failure " Patchwork

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.