[PATCH 43/43] drm/i915/gt: Move execlists submission state to the scheduling backend

Chris Wilson chris at chris-wilson.co.uk
Tue Feb 9 21:26:39 UTC 2021


Rather than embedded the execlists state into the common
intel_engine_cs, move it to the scheduler state local the scheduling
backend.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gt/intel_engine.h        |  27 -
 drivers/gpu/drm/i915/gt/intel_engine_cs.c     |  16 -
 drivers/gpu/drm/i915/gt/intel_engine_types.h  | 142 +--
 .../drm/i915/gt/intel_execlists_submission.c  | 926 ++++++++++--------
 .../gpu/drm/i915/gt/intel_ring_scheduler.c    | 211 ++--
 drivers/gpu/drm/i915/gt/selftest_execlists.c  |  30 +-
 drivers/gpu/drm/i915/gt/selftest_lrc.c        |   2 +-
 .../gpu/drm/i915/gt/uc/intel_guc_submission.c | 123 +--
 drivers/gpu/drm/i915/i915_gpu_error.c         |  60 --
 drivers/gpu/drm/i915/i915_gpu_error.h         |   3 -
 10 files changed, 683 insertions(+), 857 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/intel_engine.h b/drivers/gpu/drm/i915/gt/intel_engine.h
index 734f43d5d3dd..582f3d3a7042 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine.h
+++ b/drivers/gpu/drm/i915/gt/intel_engine.h
@@ -99,31 +99,6 @@ struct intel_gt;
  * do the writes, and that must have qw aligned offsets, simply pretend it's 8b.
  */
 
-static inline unsigned int
-execlists_num_ports(const struct intel_engine_execlists * const execlists)
-{
-	return execlists->port_mask + 1;
-}
-
-static inline struct i915_request *
-execlists_active(const struct intel_engine_execlists *execlists)
-{
-	struct i915_request * const *cur, * const *old, *active;
-
-	cur = READ_ONCE(execlists->active);
-	smp_rmb(); /* pairs with overwrite protection in process_csb() */
-	do {
-		old = cur;
-
-		active = READ_ONCE(*cur);
-		cur = READ_ONCE(execlists->active);
-
-		smp_rmb(); /* and complete the seqlock retry */
-	} while (unlikely(cur != old));
-
-	return active;
-}
-
 static inline u32
 intel_read_status_page(const struct intel_engine_cs *engine, int reg)
 {
@@ -204,8 +179,6 @@ u64 intel_engine_get_last_batch_head(const struct intel_engine_cs *engine);
 void intel_engine_get_instdone(const struct intel_engine_cs *engine,
 			       struct intel_instdone *instdone);
 
-void intel_engine_init_execlists(struct intel_engine_cs *engine);
-
 static inline void __intel_engine_reset(struct intel_engine_cs *engine,
 					bool stalled)
 {
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
index bd5ee06e1d30..5d2803b86e83 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
@@ -568,19 +568,6 @@ int intel_engines_init_mmio(struct intel_gt *gt)
 	return err;
 }
 
-void intel_engine_init_execlists(struct intel_engine_cs *engine)
-{
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-
-	execlists->port_mask = 1;
-	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));
-}
-
 static void cleanup_status_page(struct intel_engine_cs *engine)
 {
 	struct i915_vma *vma;
@@ -1418,9 +1405,6 @@ void intel_engine_dump(struct intel_engine_cs *engine,
 		drm_printf(m, "Runtime: %llums\n",
 			   ktime_to_ms(intel_engine_get_busy_time(engine,
 								  &dummy)));
-	drm_printf(m, "Forcewake: %x domains, %d active\n",
-		   engine->fw_domain, READ_ONCE(engine->fw_active));
-
 	rcu_read_lock();
 	rq = READ_ONCE(engine->heartbeat.systole);
 	if (rq)
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_types.h b/drivers/gpu/drm/i915/gt/intel_engine_types.h
index 5ebf64832680..bfc6e9f96f63 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_types.h
+++ b/drivers/gpu/drm/i915/gt/intel_engine_types.h
@@ -20,7 +20,6 @@
 #include "i915_gem.h"
 #include "i915_pmu.h"
 #include "i915_priolist_types.h"
-#include "i915_scheduler_types.h"
 #include "i915_selftest.h"
 #include "intel_breadcrumbs_types.h"
 #include "intel_sseu.h"
@@ -59,6 +58,7 @@ struct drm_i915_gem_object;
 struct drm_i915_reg_table;
 struct i915_gem_context;
 struct i915_request;
+struct i915_sched;
 struct i915_sched_attr;
 struct intel_gt;
 struct intel_ring;
@@ -126,132 +126,6 @@ enum intel_engine_id {
 /* A simple estimator for the round-trip latency of an engine */
 DECLARE_EWMA(_engine_latency, 6, 4)
 
-struct st_preempt_hang {
-	struct completion completion;
-	unsigned int count;
-};
-
-/**
- * struct intel_engine_execlists - execlist submission queue and port state
- *
- * The struct intel_engine_execlists represents the combined logical state of
- * driver and the hardware state for execlist mode of submission.
- */
-struct intel_engine_execlists {
-	/**
-	 * @timer: kick the current context if its timeslice expires
-	 */
-	struct timer_list timer;
-
-	/**
-	 * @preempt: reset the current context if it fails to give way
-	 */
-	struct timer_list preempt;
-
-	/**
-	 * @ccid: identifier for contexts submitted to this engine
-	 */
-	u32 ccid;
-
-	/**
-	 * @yield: CCID at the time of the last semaphore-wait interrupt.
-	 *
-	 * Instead of leaving a semaphore busy-spinning on an engine, we would
-	 * like to switch to another ready context, i.e. yielding the semaphore
-	 * timeslice.
-	 */
-	u32 yield;
-
-	/**
-	 * @error_interrupt: CS Master EIR
-	 *
-	 * The CS generates an interrupt when it detects an error. We capture
-	 * the first error interrupt, record the EIR and schedule the tasklet.
-	 * In the tasklet, we process the pending CS events to ensure we have
-	 * the guilty request, and then reset the engine.
-	 *
-	 * Low 16b are used by HW, with the upper 16b used as the enabling mask.
-	 * Reserve the upper 16b for tracking internal errors.
-	 */
-	u32 error_interrupt;
-#define ERROR_CSB	BIT(31)
-#define ERROR_PREEMPT	BIT(30)
-
-	/**
-	 * @reset_ccid: Active CCID [EXECLISTS_STATUS_HI] at the time of reset
-	 */
-	u32 reset_ccid;
-
-	/**
-	 * @submit_reg: gen-specific execlist submission register
-	 * set to the ExecList Submission Port (elsp) register pre-Gen11 and to
-	 * the ExecList Submission Queue Contents register array for Gen11+
-	 */
-	u32 __iomem *submit_reg;
-
-	/**
-	 * @ctrl_reg: the enhanced execlists control register, used to load the
-	 * submit queue on the HW and to request preemptions to idle
-	 */
-	u32 __iomem *ctrl_reg;
-
-#define EXECLIST_MAX_PORTS 2
-	/**
-	 * @active: the currently known context executing on HW
-	 */
-	struct i915_request * const *active;
-	/**
-	 * @inflight: the set of contexts submitted and acknowleged by HW
-	 *
-	 * The set of inflight contexts is managed by reading CS events
-	 * from the HW. On a context-switch event (not preemption), we
-	 * know the HW has transitioned from port0 to port1, and we
-	 * advance our inflight/active tracking accordingly.
-	 */
-	struct i915_request *inflight[EXECLIST_MAX_PORTS + 1 /* sentinel */];
-	/**
-	 * @pending: the next set of contexts submitted to ELSP
-	 *
-	 * We store the array of contexts that we submit to HW (via ELSP) and
-	 * promote them to the inflight array once HW has signaled the
-	 * preemption or idle-to-active event.
-	 */
-	struct i915_request *pending[EXECLIST_MAX_PORTS + 1];
-
-	/**
-	 * @port_mask: number of execlist ports - 1
-	 */
-	unsigned int port_mask;
-
-	struct rb_root_cached virtual;
-
-	/**
-	 * @csb_write: control register for Context Switch buffer
-	 *
-	 * Note this register may be either mmio or HWSP shadow.
-	 */
-	u32 *csb_write;
-
-	/**
-	 * @csb_status: status array for Context Switch buffer
-	 *
-	 * Note these register may be either mmio or HWSP shadow.
-	 */
-	u64 *csb_status;
-
-	/**
-	 * @csb_size: context status buffer FIFO size
-	 */
-	u8 csb_size;
-
-	/**
-	 * @csb_head: context status buffer head
-	 */
-	u8 csb_head;
-
-	I915_SELFTEST_DECLARE(struct st_preempt_hang preempt_hang;)
-};
-
 #define INTEL_ENGINE_CS_MAX_NAME 8
 
 struct intel_engine_cs {
@@ -280,18 +154,6 @@ struct intel_engine_cs {
 	u32 context_size;
 	u32 mmio_base;
 
-	/*
-	 * Some w/a require forcewake to be held (which prevents RC6) while
-	 * a particular engine is active. If so, we set fw_domain to which
-	 * domains need to be held for the duration of request activity,
-	 * and 0 if none. We try to limit the duration of the hold as much
-	 * as possible.
-	 */
-	enum forcewake_domains fw_domain;
-	unsigned int fw_active;
-
-	unsigned long context_tag;
-
 	struct rb_node uabi_node;
 
 	struct intel_sseu sseu;
@@ -413,8 +275,6 @@ struct intel_engine_cs {
 
 	void		(*release)(struct intel_engine_cs *engine);
 
-	struct intel_engine_execlists execlists;
-
 	/*
 	 * Keep track of completed timelines on this engine for early
 	 * retirement with the goal of quickly enabling powersaving as
diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
index 45a00ec29916..7b097aad3782 100644
--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
+++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
@@ -157,6 +157,109 @@
 /* Typical size of the average request (2 pipecontrols and a MI_BB) */
 #define EXECLISTS_REQUEST_SIZE 64 /* bytes */
 
+struct st_preempt_hang {
+	struct completion completion;
+	unsigned int count;
+};
+
+struct intel_execlists {
+	struct i915_sched sched;
+
+	unsigned long flags;
+#define GEN12_CSB_PARSE BIT(0)
+
+	u32 *pause;
+
+	u32 *csb_write;
+	u64 *csb_status;
+	u8 csb_size;
+	u8 csb_head;
+	u8 port_mask;
+	u8 id;
+
+	/* tagging for contexts submitted to the engine */
+	unsigned long ccid_tags;
+	u32 ccid_engine;
+
+	/*
+	 * @yield: CCID at the time of the last semaphore-wait interrupt.
+	 *
+	 * Instead of leaving a semaphore busy-spinning on an engine, we would
+	 * like to switch to another ready context, i.e. yielding the semaphore
+	 * timeslice.
+	 */
+	u32 yield;
+
+	/*
+	 * @error_interrupt: CS Master EIR
+	 *
+	 * The CS generates an interrupt when it detects an error. We capture
+	 * the first error interrupt, record the EIR and schedule the tasklet.
+	 * In the tasklet, we process the pending CS events to ensure we have
+	 * the guilty request, and then reset the engine.
+	 *
+	 * Low 16b are used by HW, with the upper 16b used as the enabling mask.
+	 * Reserve the upper 16b for tracking internal errors.
+	 */
+	u32 error_interrupt;
+#define ERROR_CSB	BIT(31)
+#define ERROR_PREEMPT	BIT(30)
+
+	/**
+	 * @reset_ccid: Active CCID [EXECLISTS_STATUS_HI] at the time of reset
+	 */
+	u32 reset_ccid;
+
+	u32 __iomem *submit_reg;
+	u32 __iomem *ctrl_reg;
+
+	/*
+	 * Some w/a require forcewake to be held (which prevents RC6) while
+	 * a particular engine is active. If so, we set fw_domain to which
+	 * domains need to be held for the duration of request activity,
+	 * and 0 if none. We try to limit the duration of the hold as much
+	 * as possible.
+	 */
+	enum forcewake_domains fw_domain;
+	unsigned int fw_active;
+
+#define EXECLIST_MAX_PORTS 2
+	/*
+	 * @active: the currently known context executing on HW
+	 */
+	struct i915_request * const *active;
+	/*
+	 * @inflight: the set of contexts submitted and acknowleged by HW
+	 *
+	 * The set of inflight contexts is managed by reading CS events
+	 * from the HW. On a context-switch event (not preemption), we
+	 * know the HW has transitioned from port0 to port1, and we
+	 * advance our inflight/active tracking accordingly.
+	 */
+	struct i915_request *inflight[EXECLIST_MAX_PORTS + 1 /* sentinel */];
+	/*
+	 * @pending: the next set of contexts submitted to ELSP
+	 *
+	 * We store the array of contexts that we submit to HW (via ELSP) and
+	 * promote them to the inflight array once HW has signaled the
+	 * preemption or idle-to-active event.
+	 */
+	struct i915_request *pending[EXECLIST_MAX_PORTS + 1];
+
+	struct rb_root_cached virtual;
+
+	struct timer_list timer;
+	struct timer_list preempt;
+
+	I915_SELFTEST_DECLARE(struct st_preempt_hang preempt_hang;)
+};
+
+static inline unsigned int
+num_ports(const struct intel_execlists * const el)
+{
+	return el->port_mask + 1;
+}
+
 struct virtual_engine {
 	struct intel_engine_cs base;
 	struct intel_context context;
@@ -235,7 +338,7 @@ active_request(const struct intel_timeline * const tl, struct i915_request *rq)
 	return __active_request(tl, rq, 0);
 }
 
-static void ring_set_paused(const struct intel_engine_cs *engine, int state)
+static void ring_set_paused(const struct intel_execlists *el, int state)
 {
 	/*
 	 * We inspect HWS_PREEMPT with a semaphore inside
@@ -243,7 +346,7 @@ static void ring_set_paused(const struct intel_engine_cs *engine, int state)
 	 * the ring is paused as the semaphore will busywait
 	 * until the dword is false.
 	 */
-	engine->status_page.addr[I915_GEM_HWS_PREEMPT] = state;
+	WRITE_ONCE(*el->pause, state);
 	if (state)
 		wmb();
 }
@@ -273,19 +376,19 @@ static struct i915_request *first_request(const struct i915_sched *se)
 }
 
 static struct virtual_engine *
-first_virtual_engine(const struct intel_engine_cs *engine)
+first_virtual_engine(const struct intel_execlists *el)
 {
-	return rb_entry_safe(rb_first_cached(&engine->execlists.virtual),
+	return rb_entry_safe(rb_first_cached(&el->virtual),
 			     struct virtual_engine,
-			     nodes[engine->id].rb);
+			     nodes[el->id].rb);
 }
 
 static const struct i915_request *
-first_virtual(const struct intel_engine_cs *engine)
+first_virtual(const struct intel_execlists *el)
 {
 	struct virtual_engine *ve;
 
-	ve = first_virtual_engine(engine);
+	ve = first_virtual_engine(el);
 	if (!ve)
 		return NULL;
 
@@ -307,14 +410,13 @@ dl_before(const struct i915_request *next, const struct i915_request *prev)
 	return !prev || (next && rq_deadline(next) < rq_deadline(prev));
 }
 
-static bool need_preempt(const struct intel_engine_cs *engine,
+static bool need_preempt(const struct intel_execlists *el,
 			 const struct i915_request *rq)
 {
-	const struct i915_sched *se = engine->sched;
 	const struct i915_request *first = NULL;
 	const struct i915_request *next;
 
-	if (!i915_sched_use_busywait(se))
+	if (!i915_sched_use_busywait(&el->sched))
 		return false;
 
 	/*
@@ -332,7 +434,7 @@ static bool need_preempt(const struct intel_engine_cs *engine,
 	 * Check against the first request in ELSP[1], it will, thanks to the
 	 * power of PI, be the highest priority of that context.
 	 */
-	next = next_elsp_request(se, rq);
+	next = next_elsp_request(&el->sched, rq);
 	if (dl_before(next, first))
 		first = next;
 
@@ -346,11 +448,11 @@ static bool need_preempt(const struct intel_engine_cs *engine,
 	 * ELSP[0] or ELSP[1] as, thanks again to PI, if it was the same
 	 * context, it's priority would not exceed ELSP[0] aka last_prio.
 	 */
-	next = first_request(se);
+	next = first_request(&el->sched);
 	if (dl_before(next, first))
 		first = next;
 
-	next = first_virtual(engine);
+	next = first_virtual(el);
 	if (dl_before(next, first))
 		first = next;
 
@@ -364,7 +466,7 @@ static bool need_preempt(const struct intel_engine_cs *engine,
 	 * switching between contexts is noticeable, so we try to keep
 	 * the deadline shuffling only to timeslice boundaries.
 	 */
-	ENGINE_TRACE(engine,
+	ENGINE_TRACE(el->sched.priv,
 		     "preempt for first=%llx:%llu, dl=%llu, prio=%d?\n",
 		     first->fence.context,
 		     first->fence.seqno,
@@ -374,7 +476,7 @@ static bool need_preempt(const struct intel_engine_cs *engine,
 }
 
 __maybe_unused static bool
-assert_priority_queue(const struct intel_engine_cs *engine,
+assert_priority_queue(const struct intel_execlists *el,
 		      const struct i915_request *prev,
 		      const struct i915_request *next)
 {
@@ -391,7 +493,7 @@ assert_priority_queue(const struct intel_engine_cs *engine,
 	if (rq_deadline(prev) <= rq_deadline(next))
 		return true;
 
-	ENGINE_TRACE(engine,
+	ENGINE_TRACE(el->sched.priv,
 		     "next %llx:%lld dl %lld is before prev %llx:%lld dl %lld\n",
 		     next->fence.context, next->fence.seqno, rq_deadline(next),
 		     prev->fence.context, prev->fence.seqno, rq_deadline(prev));
@@ -415,9 +517,9 @@ execlists_context_status_change(struct intel_engine_cs *engine,
 				   status, rq);
 }
 
-static void reset_active(struct i915_request *rq,
-			 struct intel_engine_cs *engine)
+static void reset_active(struct i915_request *rq, struct intel_execlists *el)
 {
+	struct intel_engine_cs *engine = el->sched.priv;
 	struct intel_context * const ce = rq->context;
 	u32 head;
 
@@ -454,8 +556,9 @@ static void reset_active(struct i915_request *rq,
 }
 
 static struct intel_engine_cs *
-__execlists_schedule_in(struct intel_engine_cs *engine, struct i915_request *rq)
+__execlists_schedule_in(struct intel_execlists *el, struct i915_request *rq)
 {
+	struct intel_engine_cs *engine = el->sched.priv;
 	struct intel_context * const ce = rq->context;
 
 	intel_context_get(ce);
@@ -465,7 +568,7 @@ __execlists_schedule_in(struct intel_engine_cs *engine, struct i915_request *rq)
 		intel_context_set_banned(ce);
 
 	if (unlikely(intel_context_is_banned(ce)))
-		reset_active(rq, engine);
+		reset_active(rq, el);
 
 	if (IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM))
 		lrc_check_regs(ce, engine, "before");
@@ -476,20 +579,20 @@ __execlists_schedule_in(struct intel_engine_cs *engine, struct i915_request *rq)
 		ce->lrc.ccid = ce->tag;
 	} else {
 		/* We don't need a strict matching tag, just different values */
-		unsigned int tag = __ffs(engine->context_tag);
+		unsigned int tag = __ffs(el->ccid_tags);
 
 		GEM_BUG_ON(tag >= BITS_PER_LONG);
-		__clear_bit(tag, &engine->context_tag);
+		__clear_bit(tag, &el->ccid_tags);
 		ce->lrc.ccid = (1 + tag) << (GEN11_SW_CTX_ID_SHIFT - 32);
 
 		BUILD_BUG_ON(BITS_PER_LONG > GEN12_MAX_CONTEXT_HW_ID);
 	}
 
-	ce->lrc.ccid |= engine->execlists.ccid;
+	ce->lrc.ccid |= el->ccid_engine;
 
 	__intel_gt_pm_get(engine->gt);
-	if (engine->fw_domain && !engine->fw_active++)
-		intel_uncore_forcewake_get(engine->uncore, engine->fw_domain);
+	if (el->fw_domain && !el->fw_active++)
+		intel_uncore_forcewake_get(engine->uncore, el->fw_domain);
 	execlists_context_status_change(engine, rq, INTEL_CONTEXT_SCHEDULE_IN);
 	intel_engine_context_in(engine);
 
@@ -499,33 +602,34 @@ __execlists_schedule_in(struct intel_engine_cs *engine, struct i915_request *rq)
 }
 
 static void
-execlists_schedule_in(struct intel_engine_cs *engine,
+execlists_schedule_in(struct intel_execlists *el,
 		      struct i915_request *rq,
 		      int idx)
 {
 	struct intel_context * const ce = rq->context;
 	struct intel_engine_cs *old;
 
-	GEM_BUG_ON(!intel_engine_pm_is_awake(engine));
+	GEM_BUG_ON(!intel_engine_pm_is_awake(el->sched.priv));
 	trace_i915_request_in(rq, idx);
 
 	old = ce->inflight;
 	if (!__intel_context_inflight_count(old))
-		old = __execlists_schedule_in(engine, rq);
+		old = __execlists_schedule_in(el, rq);
 	WRITE_ONCE(ce->inflight, ptr_inc(old));
 
-	GEM_BUG_ON(intel_context_inflight(ce) != engine);
+	GEM_BUG_ON(intel_context_inflight(ce) != el->sched.priv);
 }
 
 static void
-resubmit_virtual_request(struct i915_request *rq, struct virtual_engine *ve)
+resubmit_virtual_request(struct intel_execlists *el,
+			 struct i915_request *rq,
+			 struct virtual_engine *ve)
 {
 	struct i915_sched *se = intel_engine_get_scheduler(&ve->base);
-	struct i915_sched *pv = i915_request_get_scheduler(rq);
 	struct i915_request *pos = rq;
 	struct intel_timeline *tl;
 
-	spin_lock_irq(&pv->lock);
+	spin_lock_irq(&el->sched.lock);
 
 	if (__i915_request_is_complete(rq))
 		goto unlock;
@@ -551,14 +655,14 @@ resubmit_virtual_request(struct i915_request *rq, struct virtual_engine *ve)
 	spin_unlock(&se->lock);
 
 unlock:
-	spin_unlock_irq(&pv->lock);
+	spin_unlock_irq(&el->sched.lock);
 }
 
-static void kick_siblings(struct intel_engine_cs *engine,
+static void kick_siblings(struct intel_execlists *el,
 			  struct i915_request *rq,
 			  struct intel_context *ce)
 {
-	struct virtual_engine *ve = container_of(ce, typeof(*ve), context);
+	struct virtual_engine *ve = to_virtual_context(ce);
 
 	/*
 	 * This engine is now too busy to run this virtual request, so
@@ -567,17 +671,18 @@ static void kick_siblings(struct intel_engine_cs *engine,
 	 * same as other native request.
 	 */
 	if (i915_request_in_priority_queue(rq) &&
-	    rq->execution_mask != engine->mask)
-		resubmit_virtual_request(rq, ve);
+	    rq->execution_mask != el->sched.mask)
+		resubmit_virtual_request(el, rq, ve);
 
 	if (!i915_sched_is_idle(ve->base.sched))
 		i915_sched_kick(ve->base.sched);
 }
 
-static void __execlists_schedule_out(struct intel_engine_cs *engine,
+static void __execlists_schedule_out(struct intel_execlists *el,
 				     struct i915_request * const rq,
 				     struct intel_context * const ce)
 {
+	struct intel_engine_cs *engine = el->sched.priv;
 	unsigned int ccid;
 
 	/*
@@ -620,13 +725,13 @@ static void __execlists_schedule_out(struct intel_engine_cs *engine,
 	ccid &= GEN12_MAX_CONTEXT_HW_ID;
 	if (ccid < BITS_PER_LONG) {
 		GEM_BUG_ON(ccid == 0);
-		GEM_BUG_ON(test_bit(ccid - 1, &engine->context_tag));
-		__set_bit(ccid - 1, &engine->context_tag);
+		GEM_BUG_ON(test_bit(ccid - 1, &el->ccid_tags));
+		__set_bit(ccid - 1, &el->ccid_tags);
 	}
 	intel_engine_context_out(engine);
 	execlists_context_status_change(engine, rq, INTEL_CONTEXT_SCHEDULE_OUT);
-	if (engine->fw_domain && !--engine->fw_active)
-		intel_uncore_forcewake_put(engine->uncore, engine->fw_domain);
+	if (el->fw_domain && !--el->fw_active)
+		intel_uncore_forcewake_put(engine->uncore, el->fw_domain);
 	intel_gt_pm_put_async(engine->gt);
 
 	/*
@@ -639,14 +744,14 @@ static void __execlists_schedule_out(struct intel_engine_cs *engine,
 	 * each virtual tree and kick everyone again.
 	 */
 	if (ce->engine != engine)
-		kick_siblings(engine, rq, ce);
+		kick_siblings(el, rq, ce);
 
 	WRITE_ONCE(ce->inflight, NULL);
 	intel_context_put(ce);
 }
 
 static inline void
-execlists_schedule_out(struct intel_engine_cs *engine, struct i915_request *rq)
+execlists_schedule_out(struct intel_execlists *el, struct i915_request *rq)
 {
 	struct intel_context * const ce = rq->context;
 
@@ -655,7 +760,7 @@ execlists_schedule_out(struct intel_engine_cs *engine, struct i915_request *rq)
 	GEM_BUG_ON(!ce->inflight);
 	ce->inflight = ptr_dec(ce->inflight);
 	if (!__intel_context_inflight_count(ce->inflight))
-		__execlists_schedule_out(engine, rq, ce);
+		__execlists_schedule_out(el, rq, ce);
 
 	i915_request_put(rq);
 }
@@ -707,14 +812,14 @@ static u64 execlists_update_context(struct i915_request *rq)
 	return desc;
 }
 
-static void write_desc(struct intel_engine_execlists *execlists, u64 desc, u32 port)
+static void write_desc(struct intel_execlists *el, u64 desc, u32 port)
 {
-	if (execlists->ctrl_reg) {
-		writel(lower_32_bits(desc), execlists->submit_reg + port * 2);
-		writel(upper_32_bits(desc), execlists->submit_reg + port * 2 + 1);
+	if (el->ctrl_reg) {
+		writel(lower_32_bits(desc), el->submit_reg + port * 2);
+		writel(upper_32_bits(desc), el->submit_reg + port * 2 + 1);
 	} else {
-		writel(upper_32_bits(desc), execlists->submit_reg);
-		writel(lower_32_bits(desc), execlists->submit_reg);
+		writel(upper_32_bits(desc), el->submit_reg);
+		writel(lower_32_bits(desc), el->submit_reg);
 	}
 }
 
@@ -737,52 +842,48 @@ dump_port(char *buf, int buflen, const char *prefix, struct i915_request *rq)
 }
 
 static __maybe_unused noinline void
-trace_ports(const struct intel_engine_execlists *execlists,
+trace_ports(const struct intel_execlists *el,
 	    const char *msg,
 	    struct i915_request * const *ports)
 {
-	const struct intel_engine_cs *engine =
-		container_of(execlists, typeof(*engine), execlists);
 	char __maybe_unused p0[40], p1[40];
 
 	if (!ports[0])
 		return;
 
-	ENGINE_TRACE(engine, "%s { %s%s }\n", msg,
+	ENGINE_TRACE(el->sched.priv, "%s { %s%s }\n", msg,
 		     dump_port(p0, sizeof(p0), "", ports[0]),
 		     dump_port(p1, sizeof(p1), ", ", ports[1]));
 }
 
 static __maybe_unused noinline bool
-assert_pending_valid(const struct intel_engine_execlists *execlists,
+assert_pending_valid(const struct intel_execlists *el,
 		     const char *msg)
 {
-	struct intel_engine_cs *engine =
-		container_of(execlists, typeof(*engine), execlists);
 	struct i915_request * const *port, *rq;
 	struct intel_context *ce = NULL;
 	bool sentinel = false;
 	u32 ccid = -1;
 
-	trace_ports(execlists, msg, execlists->pending);
+	trace_ports(el, msg, el->pending);
 
 	/* We may be messing around with the lists during reset, lalala */
-	if (__i915_sched_tasklet_is_disabled(intel_engine_get_scheduler(engine)))
+	if (__i915_sched_tasklet_is_disabled(&el->sched))
 		return true;
 
-	if (!execlists->pending[0]) {
+	if (!el->pending[0]) {
 		GEM_TRACE_ERR("%s: Nothing pending for promotion!\n",
-			      engine->name);
+			      el->sched.dbg.name);
 		return false;
 	}
 
-	if (execlists->pending[execlists_num_ports(execlists)]) {
+	if (el->pending[num_ports(el)]) {
 		GEM_TRACE_ERR("%s: Excess pending[%d] for promotion!\n",
-			      engine->name, execlists_num_ports(execlists));
+			      el->sched.dbg.name, num_ports(el));
 		return false;
 	}
 
-	for (port = execlists->pending; (rq = *port); port++) {
+	for (port = el->pending; (rq = *port); port++) {
 		unsigned long flags;
 		bool ok = true;
 
@@ -791,18 +892,18 @@ assert_pending_valid(const struct intel_engine_execlists *execlists,
 
 		if (ce == rq->context) {
 			GEM_TRACE_ERR("%s: Dup context:%llx in pending[%zd]\n",
-				      engine->name,
+				      el->sched.dbg.name,
 				      ce->timeline->fence_context,
-				      port - execlists->pending);
+				      port - el->pending);
 			return false;
 		}
 		ce = rq->context;
 
 		if (ccid == ce->lrc.ccid) {
 			GEM_TRACE_ERR("%s: Dup ccid:%x context:%llx in pending[%zd]\n",
-				      engine->name,
+				      el->sched.dbg.name,
 				      ccid, ce->timeline->fence_context,
-				      port - execlists->pending);
+				      port - el->pending);
 			return false;
 		}
 		ccid = ce->lrc.ccid;
@@ -814,9 +915,9 @@ assert_pending_valid(const struct intel_engine_execlists *execlists,
 		 */
 		if (sentinel) {
 			GEM_TRACE_ERR("%s: context:%llx after sentinel in pending[%zd]\n",
-				      engine->name,
+				      el->sched.dbg.name,
 				      ce->timeline->fence_context,
-				      port - execlists->pending);
+				      port - el->pending);
 			return false;
 		}
 		sentinel = i915_request_has_sentinel(rq);
@@ -826,12 +927,12 @@ assert_pending_valid(const struct intel_engine_execlists *execlists,
 		 * that they are never stuck behind a hog and can be immediately
 		 * transferred onto the next idle engine.
 		 */
-		if (rq->execution_mask != engine->mask &&
-		    port != execlists->pending) {
+		if (rq->execution_mask != el->sched.mask &&
+		    port != el->pending) {
 			GEM_TRACE_ERR("%s: virtual engine:%llx not in prime position[%zd]\n",
-				      engine->name,
+				      el->sched.dbg.name,
 				      ce->timeline->fence_context,
-				      port - execlists->pending);
+				      port - el->pending);
 			return false;
 		}
 
@@ -845,27 +946,27 @@ assert_pending_valid(const struct intel_engine_execlists *execlists,
 		if (i915_active_is_idle(&ce->active) &&
 		    !intel_context_is_barrier(ce)) {
 			GEM_TRACE_ERR("%s: Inactive context:%llx in pending[%zd]\n",
-				      engine->name,
+				      el->sched.dbg.name,
 				      ce->timeline->fence_context,
-				      port - execlists->pending);
+				      port - el->pending);
 			ok = false;
 			goto unlock;
 		}
 
 		if (!i915_vma_is_pinned(ce->state)) {
 			GEM_TRACE_ERR("%s: Unpinned context:%llx in pending[%zd]\n",
-				      engine->name,
+				      el->sched.dbg.name,
 				      ce->timeline->fence_context,
-				      port - execlists->pending);
+				      port - el->pending);
 			ok = false;
 			goto unlock;
 		}
 
 		if (!i915_vma_is_pinned(ce->ring->vma)) {
 			GEM_TRACE_ERR("%s: Unpinned ring:%llx in pending[%zd]\n",
-				      engine->name,
+				      el->sched.dbg.name,
 				      ce->timeline->fence_context,
-				      port - execlists->pending);
+				      port - el->pending);
 			ok = false;
 			goto unlock;
 		}
@@ -879,12 +980,11 @@ assert_pending_valid(const struct intel_engine_execlists *execlists,
 	return ce;
 }
 
-static void execlists_submit_ports(struct intel_engine_cs *engine)
+static void execlists_submit_ports(struct intel_execlists *el)
 {
-	struct intel_engine_execlists *execlists = &engine->execlists;
 	unsigned int n;
 
-	GEM_BUG_ON(!assert_pending_valid(execlists, "submit"));
+	GEM_BUG_ON(!assert_pending_valid(el, "submit"));
 
 	/*
 	 * We can skip acquiring intel_runtime_pm_get() here as it was taken
@@ -894,7 +994,7 @@ static void execlists_submit_ports(struct intel_engine_cs *engine)
 	 * that all ELSP are drained i.e. we have processed the CSB,
 	 * before allowing ourselves to idle and calling intel_runtime_pm_put().
 	 */
-	GEM_BUG_ON(!intel_engine_pm_is_awake(engine));
+	GEM_BUG_ON(!intel_engine_pm_is_awake(el->sched.priv));
 
 	/*
 	 * ELSQ note: the submit queue is not cleared after being submitted
@@ -902,17 +1002,15 @@ static void execlists_submit_ports(struct intel_engine_cs *engine)
 	 * currently ensured by the fact that we always write the same number
 	 * of elsq entries, keep this in mind before changing the loop below.
 	 */
-	for (n = execlists_num_ports(execlists); n--; ) {
-		struct i915_request *rq = execlists->pending[n];
+	for (n = num_ports(el); n--; ) {
+		struct i915_request *rq = el->pending[n];
 
-		write_desc(execlists,
-			   rq ? execlists_update_context(rq) : 0,
-			   n);
+		write_desc(el, 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);
+	if (el->ctrl_reg)
+		writel(EL_CTRL_LOAD, el->ctrl_reg);
 }
 
 static bool ctx_single_port_submission(const struct intel_context *ce)
@@ -944,12 +1042,12 @@ static unsigned long i915_request_flags(const struct i915_request *rq)
 	return READ_ONCE(rq->fence.flags);
 }
 
-static bool can_merge_rq(const struct intel_engine_cs *engine,
+static bool can_merge_rq(const struct intel_execlists *el,
 			 const struct i915_request *prev,
 			 const struct i915_request *next)
 {
 	GEM_BUG_ON(prev == next);
-	GEM_BUG_ON(!assert_priority_queue(engine, prev, next));
+	GEM_BUG_ON(!assert_priority_queue(el, prev, next));
 
 	/*
 	 * We do not submit known completed requests. Therefore if the next
@@ -976,14 +1074,14 @@ static bool can_merge_rq(const struct intel_engine_cs *engine,
 
 static bool virtual_matches(const struct virtual_engine *ve,
 			    const struct i915_request *rq,
-			    const struct intel_engine_cs *engine)
+			    const struct intel_execlists *el)
 {
 	const struct intel_engine_cs *inflight;
 
 	if (!rq)
 		return false;
 
-	if (!(rq->execution_mask & engine->mask)) /* We peeked too soon! */
+	if (!(rq->execution_mask & el->sched.mask)) /* We peeked too soon! */
 		return false;
 
 	/*
@@ -996,7 +1094,7 @@ static bool virtual_matches(const struct virtual_engine *ve,
 	 * hystersis on the greedy seelction algorithm.
 	 */
 	inflight = intel_context_inflight(&ve->context);
-	if (inflight && inflight != engine)
+	if (inflight && inflight != el->sched.priv)
 		return false;
 
 	return true;
@@ -1034,7 +1132,7 @@ static void virtual_xfer_context(struct virtual_engine *ve,
 }
 
 static bool
-timeslice_yield(const struct intel_engine_execlists *el,
+timeslice_yield(const struct intel_execlists *el,
 		const struct i915_request *rq)
 {
 	/*
@@ -1052,12 +1150,10 @@ timeslice_yield(const struct intel_engine_execlists *el,
 	return rq->context->lrc.ccid == READ_ONCE(el->yield);
 }
 
-static bool needs_timeslice(const struct intel_engine_cs *engine,
+static bool needs_timeslice(const struct intel_execlists *el,
 			    const struct i915_request *rq)
 {
-	const struct i915_sched *se = engine->sched;
-
-	if (!i915_sched_has_timeslices(se))
+	if (!i915_sched_has_timeslices(&el->sched))
 		return false;
 
 	/* If not currently active, or about to switch, wait for next event */
@@ -1065,23 +1161,24 @@ static bool needs_timeslice(const struct intel_engine_cs *engine,
 		return false;
 
 	/* We do not need to start the timeslice until after the ACK */
-	if (READ_ONCE(engine->execlists.pending[0]))
+	if (READ_ONCE(el->pending[0]))
 		return false;
 
 	/* If ELSP[1] is occupied, always check to see if worth slicing */
-	if (!i915_sched_is_last_request(se, rq)) {
-		ENGINE_TRACE(engine, "timeslice required for second inflight context\n");
+	if (!i915_sched_is_last_request(&el->sched, rq)) {
+		ENGINE_TRACE(el->sched.priv,
+			     "timeslice required for second inflight context\n");
 		return true;
 	}
 
 	/* Otherwise, ELSP[0] is by itself, but may be waiting in the queue */
-	if (!i915_sched_is_idle(se)) {
-		ENGINE_TRACE(engine, "timeslice required for queue\n");
+	if (!i915_sched_is_idle(&el->sched)) {
+		ENGINE_TRACE(el->sched.priv, "timeslice required for queue\n");
 		return true;
 	}
 
-	if (!RB_EMPTY_ROOT(&engine->execlists.virtual.rb_root)) {
-		ENGINE_TRACE(engine, "timeslice required for virtual\n");
+	if (!RB_EMPTY_ROOT(&el->virtual.rb_root)) {
+		ENGINE_TRACE(el->sched.priv, "timeslice required for virtual\n");
 		return true;
 	}
 
@@ -1089,14 +1186,12 @@ static bool needs_timeslice(const struct intel_engine_cs *engine,
 }
 
 static bool
-timeslice_expired(struct intel_engine_cs *engine, const struct i915_request *rq)
+timeslice_expired(struct intel_execlists *el, const struct i915_request *rq)
 {
-	const struct intel_engine_execlists *el = &engine->execlists;
-
 	if (i915_request_has_nopreempt(rq) && __i915_request_has_started(rq))
 		return false;
 
-	if (!needs_timeslice(engine, rq))
+	if (!needs_timeslice(el, rq))
 		return false;
 
 	return timer_expired(&el->timer) || timeslice_yield(el, rq);
@@ -1107,14 +1202,13 @@ static unsigned long timeslice(const struct intel_engine_cs *engine)
 	return READ_ONCE(engine->props.timeslice_duration_ms);
 }
 
-static void start_timeslice(struct intel_engine_cs *engine)
+static void start_timeslice(struct intel_execlists *el)
 {
-	struct intel_engine_execlists *el = &engine->execlists;
 	unsigned long duration;
 
 	/* Disable the timer if there is nothing to switch to */
 	duration = 0;
-	if (needs_timeslice(engine, *el->active)) {
+	if (needs_timeslice(el, *el->active)) {
 		/* Avoid continually prolonging an active timeslice */
 		if (timer_active(&el->timer)) {
 			/*
@@ -1123,19 +1217,19 @@ static void start_timeslice(struct intel_engine_cs *engine)
 			 * its timeslice, so recheck.
 			 */
 			if (!timer_pending(&el->timer))
-				intel_engine_kick_scheduler(engine);
+				i915_sched_kick(&el->sched);
 			return;
 		}
 
-		duration = timeslice(engine);
+		duration = timeslice(el->sched.priv);
 	}
 
 	set_timer_ms(&el->timer, duration);
 }
 
-static void record_preemption(struct intel_engine_execlists *execlists)
+static void record_preemption(struct intel_execlists *el)
 {
-	(void)I915_SELFTEST_ONLY(execlists->preempt_hang.count++);
+	(void)I915_SELFTEST_ONLY(el->preempt_hang.count++);
 }
 
 static unsigned long active_preempt_timeout(struct intel_engine_cs *engine,
@@ -1151,14 +1245,13 @@ static unsigned long active_preempt_timeout(struct intel_engine_cs *engine,
 	return READ_ONCE(engine->props.preempt_timeout_ms);
 }
 
-static void set_preempt_timeout(struct intel_engine_cs *engine,
+static void set_preempt_timeout(struct intel_execlists *el,
 				const struct i915_request *rq)
 {
-	if (!intel_engine_has_preempt_reset(engine))
+	if (!intel_engine_has_preempt_reset(el->sched.priv))
 		return;
 
-	set_timer_ms(&engine->execlists.preempt,
-		     active_preempt_timeout(engine, rq));
+	set_timer_ms(&el->preempt, active_preempt_timeout(el->sched.priv, rq));
 }
 
 static bool completed(const struct i915_request *rq)
@@ -1169,25 +1262,33 @@ static bool completed(const struct i915_request *rq)
 	return __i915_request_is_complete(rq);
 }
 
-static void __virtual_dequeue(struct virtual_engine *ve,
-			      struct intel_engine_cs *sibling)
+#define as_execlists(se) \
+	container_of(se, const struct intel_execlists, sched)
+
+static struct intel_execlists *to_execlists(struct intel_engine_cs *e)
 {
-	struct ve_node * const node = &ve->nodes[sibling->id];
+	return container_of(e->sched, struct intel_execlists, sched);
+}
+
+static void __virtual_dequeue(struct virtual_engine *ve,
+			      struct intel_execlists *el)
+{
+	struct ve_node * const node = &ve->nodes[el->id];
 	struct rb_node **parent, *rb;
 	struct i915_request *rq;
 	u64 deadline;
 	bool first;
 
-	rb_erase_cached(&node->rb, &sibling->execlists.virtual);
+	rb_erase_cached(&node->rb, &el->virtual);
 	RB_CLEAR_NODE(&node->rb);
 
 	rq = first_request(ve->base.sched);
-	if (!virtual_matches(ve, rq, sibling))
+	if (!virtual_matches(ve, rq, el))
 		return;
 
 	rb = NULL;
 	first = true;
-	parent = &sibling->execlists.virtual.rb_root.rb_node;
+	parent = &el->virtual.rb_root.rb_node;
 	deadline = rq_deadline(rq);
 	while (*parent) {
 		struct ve_node *other;
@@ -1203,24 +1304,23 @@ static void __virtual_dequeue(struct virtual_engine *ve,
 	}
 
 	rb_link_node(&node->rb, rb, parent);
-	rb_insert_color_cached(&node->rb, &sibling->execlists.virtual, first);
+	rb_insert_color_cached(&node->rb, &el->virtual, first);
 }
 
-static void virtual_requeue(struct intel_engine_cs *engine,
+static void virtual_requeue(struct intel_execlists *el,
 			    struct i915_request *last)
 {
-	const struct i915_request * const first =
-		first_request(intel_engine_get_scheduler(engine));
+	const struct i915_request * const first = first_request(&el->sched);
 	struct virtual_engine *ve;
 
-	while ((ve = first_virtual_engine(engine))) {
+	while ((ve = first_virtual_engine(el))) {
 		struct i915_sched *se = intel_engine_get_scheduler(&ve->base);
 		struct i915_request *rq;
 
 		spin_lock(&se->lock);
 
 		rq = first_request(se);
-		if (unlikely(!virtual_matches(ve, rq, engine)))
+		if (unlikely(!virtual_matches(ve, rq, el)))
 			/* lost the race to a sibling */
 			goto unlock;
 
@@ -1236,7 +1336,7 @@ static void virtual_requeue(struct intel_engine_cs *engine,
 			return;
 		}
 
-		ENGINE_TRACE(engine,
+		ENGINE_TRACE(el->sched.priv,
 			     "virtual rq=%llx:%lld%s, dl %lld, new engine? %s\n",
 			     rq->fence.context,
 			     rq->fence.seqno,
@@ -1244,10 +1344,10 @@ static void virtual_requeue(struct intel_engine_cs *engine,
 			     __i915_request_has_started(rq) ? "*" :
 			     "",
 			     rq_deadline(rq),
-			     yesno(engine != ve->siblings[0]));
+			     yesno(el->sched.priv != ve->siblings[0]));
 
-		GEM_BUG_ON(!(rq->execution_mask & engine->mask));
-		if (__i915_request_requeue(rq, engine->sched)) {
+		GEM_BUG_ON(!(rq->execution_mask & el->sched.mask));
+		if (__i915_request_requeue(rq, &el->sched)) {
 			/*
 			 * Only after we confirm that we will submit
 			 * this request (i.e. it has not already
@@ -1261,30 +1361,29 @@ static void virtual_requeue(struct intel_engine_cs *engine,
 			 * we may be using ve->siblings[] in
 			 * virtual_context_enter / virtual_context_exit.
 			 */
-			virtual_xfer_context(ve, engine);
+			virtual_xfer_context(ve, el->sched.priv);
 
 			/* Bind this ve before we release the lock */
 			if (!ve->context.inflight)
-				WRITE_ONCE(ve->context.inflight, engine);
+				WRITE_ONCE(ve->context.inflight,
+					   el->sched.priv);
 
-			GEM_BUG_ON(ve->siblings[0] != engine);
-			GEM_BUG_ON(intel_context_inflight(rq->context) != engine);
+			GEM_BUG_ON(ve->siblings[0] != el->sched.priv);
+			GEM_BUG_ON(intel_context_inflight(rq->context) != el->sched.priv);
 
 			last = rq;
 		}
 
 unlock:
-		__virtual_dequeue(ve, engine);
+		__virtual_dequeue(ve, el);
 		spin_unlock(&se->lock);
 	}
 }
 
-static void execlists_dequeue(struct intel_engine_cs *engine)
+static void execlists_dequeue(struct intel_execlists *el)
 {
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct i915_sched *se = intel_engine_get_scheduler(engine);
-	struct i915_request **port = execlists->pending;
-	struct i915_request ** const last_port = port + execlists->port_mask;
+	struct i915_request **port = el->pending;
+	struct i915_request ** const last_port = port + el->port_mask;
 	struct i915_request *last, * const *active;
 	struct i915_request *rq, *rn;
 	struct i915_priolist *pl;
@@ -1321,28 +1420,28 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 	 * of trouble.
 	 *
 	 */
-	active = execlists->active;
+	active = el->active;
 	while ((last = *active) && completed(last))
 		active++;
 
 	if (last) {
-		if (need_preempt(engine, last)) {
-			ENGINE_TRACE(engine,
+		if (need_preempt(el, last)) {
+			ENGINE_TRACE(el->sched.priv,
 				     "preempting last=%llx:%llu, dl=%llu, prio=%d\n",
 				     last->fence.context,
 				     last->fence.seqno,
 				     rq_deadline(last),
 				     rq_prio(last));
-			record_preemption(execlists);
+			record_preemption(el);
 			last = (void *)1;
-		} else if (timeslice_expired(engine, last)) {
-			ENGINE_TRACE(engine,
+		} else if (timeslice_expired(el, last)) {
+			ENGINE_TRACE(el->sched.priv,
 				     "expired:%s last=%llx:%llu, deadline=%llu, now=%llu, yield?=%s\n",
-				     yesno(timer_expired(&execlists->timer)),
+				     yesno(timer_expired(&el->timer)),
 				     last->fence.context, last->fence.seqno,
 				     rq_deadline(last),
 				     i915_sched_to_ticks(ktime_get()),
-				     yesno(timeslice_yield(execlists, last)));
+				     yesno(timeslice_yield(el, last)));
 
 			/*
 			 * Consume this timeslice; ensure we start a new one.
@@ -1360,7 +1459,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 			 * consumption of this timeslice, if we submit the
 			 * same context again, grant it a full timeslice.
 			 */
-			cancel_timer(&execlists->timer);
+			cancel_timer(&el->timer);
 
 			/*
 			 * Unlike for preemption, if we rewind and continue
@@ -1397,7 +1496,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 	}
 
 	local_irq_disable(); /* irq remains off until after ELSP write */
-	spin_lock(&se->lock);
+	spin_lock(&el->sched.lock);
 
 	if ((unsigned long)last & 1) {
 		bool defer = (unsigned long)last & 2;
@@ -1407,7 +1506,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 		 * as we unwind (and until we resubmit) so that we do
 		 * not accidentally tell it to go backwards.
 		 */
-		ring_set_paused(engine, (unsigned long)last);
+		ring_set_paused(el, (unsigned long)last);
 
 		/*
 		 * Note that we have not stopped the GPU at this point,
@@ -1416,7 +1515,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 		 * the preemption, some of the unwound requests may
 		 * complete!
 		 */
-		last = __i915_sched_rewind_requests(se);
+		last = __i915_sched_rewind_requests(&el->sched);
 
 		/*
 		 * We want to move the interrupted request to the back of
@@ -1426,18 +1525,18 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 		 * be run after it again.
 		 */
 		if (last && defer)
-			__i915_sched_defer_request(se, last);
+			__i915_sched_defer_request(&el->sched, last);
 
 		last = NULL;
 	}
 
-	if (!RB_EMPTY_ROOT(&execlists->virtual.rb_root))
-		virtual_requeue(engine, last);
+	if (!RB_EMPTY_ROOT(&el->virtual.rb_root))
+		virtual_requeue(el, last);
 
-	i915_sched_dequeue(se, pl, rq, rn) {
+	i915_sched_dequeue(&el->sched, pl, rq, rn) {
 		bool merge = true;
 
-		GEM_BUG_ON(i915_request_get_scheduler(rq) != se);
+		GEM_BUG_ON(i915_request_get_scheduler(rq) != &el->sched);
 
 		/*
 		 * Can we combine this request with the current port?
@@ -1450,7 +1549,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 		 * second request, and so we never need to tell the
 		 * hardware about the first.
 		 */
-		if (last && !can_merge_rq(engine, last, rq)) {
+		if (last && !can_merge_rq(el, last, rq)) {
 			/*
 			 * If we are on the second port and cannot
 			 * combine this request with the last, then we
@@ -1476,10 +1575,10 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 			 * the request immediately to another engine
 			 * rather than wait for the primary request.
 			 */
-			if (rq->execution_mask != engine->mask)
+			if (rq->execution_mask != el->sched.mask)
 				goto done;
 
-			if (unlikely(dl_before(first_virtual(engine), rq)))
+			if (unlikely(dl_before(first_virtual(el), rq)))
 				goto done;
 
 			/*
@@ -1496,7 +1595,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 			merge = false;
 		}
 
-		if (__i915_request_submit(rq, engine)) {
+		if (__i915_request_submit(rq, el->sched.priv)) {
 			if (!merge) {
 				*port++ = i915_request_get(last);
 				last = NULL;
@@ -1515,7 +1614,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 	}
 done:
 	*port++ = i915_request_get(last);
-	spin_unlock(&se->lock);
+	spin_unlock(&el->sched.lock);
 
 	/*
 	 * We can skip poking the HW if we ended up with exactly the same set
@@ -1524,21 +1623,20 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 	 */
 	if (submit &&
 	    memcmp(active,
-		   execlists->pending,
-		   (port - execlists->pending) * sizeof(*port))) {
+		   el->pending,
+		   (port - el->pending) * sizeof(*port))) {
 		*port = NULL;
-		while (port-- != execlists->pending)
-			execlists_schedule_in(engine, *port,
-					      port - execlists->pending);
+		while (port-- != el->pending)
+			execlists_schedule_in(el, *port, port - el->pending);
 
-		WRITE_ONCE(execlists->yield, -1);
-		set_preempt_timeout(engine, *active);
-		execlists_submit_ports(engine);
+		WRITE_ONCE(el->yield, -1);
+		set_preempt_timeout(el, *active);
+		execlists_submit_ports(el);
 	} else {
-		ring_set_paused(engine, 0);
-		while (port-- != execlists->pending)
+		ring_set_paused(el, 0);
+		while (port-- != el->pending)
 			i915_request_put(*port);
-		*execlists->pending = NULL;
+		*el->pending = NULL;
 	}
 
 	local_irq_enable();
@@ -1558,27 +1656,27 @@ copy_ports(struct i915_request **dst, struct i915_request **src, int count)
 }
 
 static struct i915_request **
-cancel_port_requests(struct intel_engine_execlists * const execlists,
+cancel_port_requests(struct intel_execlists * const el,
 		     struct i915_request **inactive)
 {
 	struct i915_request * const *port;
 
-	for (port = execlists->pending; *port; port++)
+	for (port = el->pending; *port; port++)
 		*inactive++ = *port;
-	clear_ports(execlists->pending, ARRAY_SIZE(execlists->pending));
+	clear_ports(el->pending, ARRAY_SIZE(el->pending));
 
 	/* Mark the end of active before we overwrite *active */
-	for (port = xchg(&execlists->active, execlists->pending); *port; port++)
+	for (port = xchg(&el->active, el->pending); *port; port++)
 		*inactive++ = *port;
-	clear_ports(execlists->inflight, ARRAY_SIZE(execlists->inflight));
+	clear_ports(el->inflight, ARRAY_SIZE(el->inflight));
 
 	smp_wmb(); /* complete the seqlock for execlists_active() */
-	WRITE_ONCE(execlists->active, execlists->inflight);
+	WRITE_ONCE(el->active, el->inflight);
 
 	/* Having cancelled all outstanding process_csb(), stop their timers */
-	GEM_BUG_ON(execlists->pending[0]);
-	cancel_timer(&execlists->timer);
-	cancel_timer(&execlists->preempt);
+	GEM_BUG_ON(el->pending[0]);
+	cancel_timer(&el->timer);
+	cancel_timer(&el->preempt);
 
 	return inactive;
 }
@@ -1648,7 +1746,7 @@ static bool gen8_csb_parse(const u64 csb)
 }
 
 static noinline u64
-wa_csb_read(const struct intel_engine_cs *engine, u64 * const csb)
+wa_csb_read(const struct intel_execlists *el, u64 * const csb)
 {
 	u64 entry;
 
@@ -1663,7 +1761,8 @@ wa_csb_read(const struct intel_engine_cs *engine, u64 * const csb)
 	 */
 	preempt_disable();
 	if (wait_for_atomic_us((entry = READ_ONCE(*csb)) != -1, 10)) {
-		int idx = csb - engine->execlists.csb_status;
+		struct intel_engine_cs *engine = el->sched.priv;
+		int idx = csb - el->csb_status;
 		int status;
 
 		status = GEN8_EXECLISTS_STATUS_BUF;
@@ -1681,7 +1780,7 @@ wa_csb_read(const struct intel_engine_cs *engine, u64 * const csb)
 	return entry;
 }
 
-static u64 csb_read(const struct intel_engine_cs *engine, u64 * const csb)
+static u64 csb_read(const struct intel_execlists *el, u64 * const csb)
 {
 	u64 entry = READ_ONCE(*csb);
 
@@ -1697,7 +1796,7 @@ static u64 csb_read(const struct intel_engine_cs *engine, u64 * const csb)
 	 * tgl:HSDES#22011248461
 	 */
 	if (unlikely(entry == -1))
-		entry = wa_csb_read(engine, csb);
+		entry = wa_csb_read(el, csb);
 
 	/* Consume this entry so that we can spot its future reuse. */
 	WRITE_ONCE(*csb, -1);
@@ -1706,18 +1805,22 @@ static u64 csb_read(const struct intel_engine_cs *engine, u64 * const csb)
 	return entry;
 }
 
-static void new_timeslice(struct intel_engine_execlists *el)
+static void new_timeslice(struct intel_execlists *el)
 {
 	/* By cancelling, we will start afresh in start_timeslice() */
 	cancel_timer(&el->timer);
 }
 
-static struct i915_request **
-process_csb(struct intel_engine_cs *engine, struct i915_request **inactive)
+static void process_csb_delay(struct intel_engine_cs *engine)
 {
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-	u64 * const buf = execlists->csb_status;
-	const u8 num_entries = execlists->csb_size;
+	ENGINE_POSTING_READ(engine, RING_CONTEXT_STATUS_PTR);
+}
+
+static struct i915_request **
+process_csb(struct intel_execlists *el, struct i915_request **inactive)
+{
+	u64 * const buf = el->csb_status;
+	const u8 num_entries = el->csb_size;
 	struct i915_request **prev;
 	u8 head, tail;
 
@@ -1731,8 +1834,8 @@ process_csb(struct intel_engine_cs *engine, struct i915_request **inactive)
 	 * to use explicit shifting and masking, and probably bifurcating
 	 * the code to handle the legacy mmio read).
 	 */
-	head = execlists->csb_head;
-	tail = READ_ONCE(*execlists->csb_write);
+	head = el->csb_head;
+	tail = READ_ONCE(*el->csb_write);
 	if (unlikely(head == tail))
 		return inactive;
 
@@ -1752,8 +1855,8 @@ process_csb(struct intel_engine_cs *engine, struct i915_request **inactive)
 	 * simplest way is by stop processing the event queue and force the
 	 * engine to reset.
 	 */
-	execlists->csb_head = tail;
-	ENGINE_TRACE(engine, "cs-irq head=%d, tail=%d\n", head, tail);
+	el->csb_head = tail;
+	ENGINE_TRACE(el->sched.priv, "cs-irq head=%d, tail=%d\n", head, tail);
 
 	/*
 	 * Hopefully paired with a wmb() in HW!
@@ -1794,53 +1897,51 @@ process_csb(struct intel_engine_cs *engine, struct i915_request **inactive)
 		 * status notifier.
 		 */
 
-		csb = csb_read(engine, buf + head);
-		ENGINE_TRACE(engine, "csb[%d]: status=0x%08x:0x%08x\n",
+		csb = csb_read(el, buf + head);
+		ENGINE_TRACE(el->sched.priv, "csb[%d]: status=0x%08x:0x%08x\n",
 			     head, upper_32_bits(csb), lower_32_bits(csb));
 
-		if (INTEL_GEN(engine->i915) >= 12)
+		if (el->flags & GEN12_CSB_PARSE)
 			promote = gen12_csb_parse(csb);
 		else
 			promote = gen8_csb_parse(csb);
 		if (promote) {
-			struct i915_request * const *old = execlists->active;
+			struct i915_request * const *old = el->active;
 
-			if (GEM_WARN_ON(!*execlists->pending)) {
-				execlists->error_interrupt |= ERROR_CSB;
+			if (GEM_WARN_ON(!*el->pending)) {
+				el->error_interrupt |= ERROR_CSB;
 				break;
 			}
 
-			ring_set_paused(engine, 0);
+			ring_set_paused(el, 0);
 
 			/* Point active to the new ELSP; prevent overwriting */
-			WRITE_ONCE(execlists->active, execlists->pending);
+			WRITE_ONCE(el->active, el->pending);
 			smp_wmb(); /* notify execlists_active() */
 
 			/* cancel old inflight, prepare for switch */
-			trace_ports(execlists, "preempted", old);
+			trace_ports(el, "preempted", old);
 			while (*old)
 				*inactive++ = *old++;
 
 			/* switch pending to inflight */
-			GEM_BUG_ON(!assert_pending_valid(execlists, "promote"));
-			copy_ports(execlists->inflight,
-				   execlists->pending,
-				   execlists_num_ports(execlists));
+			GEM_BUG_ON(!assert_pending_valid(el, "promote"));
+			copy_ports(el->inflight, el->pending, num_ports(el));
 			smp_wmb(); /* complete the seqlock */
-			WRITE_ONCE(execlists->active, execlists->inflight);
+			WRITE_ONCE(el->active, el->inflight);
 
 			/* XXX Magic delay for tgl */
-			ENGINE_POSTING_READ(engine, RING_CONTEXT_STATUS_PTR);
+			process_csb_delay(el->sched.priv);
 
-			WRITE_ONCE(execlists->pending[0], NULL);
+			WRITE_ONCE(el->pending[0], NULL);
 		} else {
-			if (GEM_WARN_ON(!*execlists->active)) {
-				execlists->error_interrupt |= ERROR_CSB;
+			if (GEM_WARN_ON(!*el->active)) {
+				el->error_interrupt |= ERROR_CSB;
 				break;
 			}
 
 			/* port0 completed, advanced to port1 */
-			trace_ports(execlists, "completed", execlists->active);
+			trace_ports(el, "completed", el->active);
 
 			/*
 			 * We rely on the hardware being strongly
@@ -1853,10 +1954,12 @@ process_csb(struct intel_engine_cs *engine, struct i915_request **inactive)
 			 * itself...
 			 */
 			if (GEM_SHOW_DEBUG() &&
-			    !__i915_request_is_complete(*execlists->active)) {
-				struct i915_request *rq = *execlists->active;
+			    !__i915_request_is_complete(*el->active)) {
+				struct i915_request *rq = *el->active;
 				const u32 *regs __maybe_unused =
 					rq->context->lrc_reg_state;
+				struct intel_engine_cs *engine __maybe_unused =
+					el->sched.priv;
 
 				ENGINE_TRACE(engine,
 					     "context completed before request!\n");
@@ -1881,10 +1984,9 @@ process_csb(struct intel_engine_cs *engine, struct i915_request **inactive)
 					     regs[CTX_RING_TAIL]);
 			}
 
-			*inactive++ = *execlists->active++;
+			*inactive++ = *el->active++;
 
-			GEM_BUG_ON(execlists->active - execlists->inflight >
-				   execlists_num_ports(execlists));
+			GEM_BUG_ON(el->active - el->inflight > num_ports(el));
 		}
 	} while (head != tail);
 
@@ -1906,7 +2008,7 @@ process_csb(struct intel_engine_cs *engine, struct i915_request **inactive)
 	 * and merits a fresh timeslice. We reinstall the timer after
 	 * inspecting the queue to see if we need to resumbit.
 	 */
-	if (*prev != *execlists->active) { /* elide lite-restores */
+	if (*prev != *el->active) { /* elide lite-restores */
 		/*
 		 * Note the inherent discrepancy between the HW runtime,
 		 * recorded as part of the context switch, and the CPU
@@ -1919,20 +2021,20 @@ process_csb(struct intel_engine_cs *engine, struct i915_request **inactive)
 		 */
 		if (*prev)
 			lrc_runtime_stop((*prev)->context);
-		if (*execlists->active)
-			lrc_runtime_start((*execlists->active)->context);
-		new_timeslice(execlists);
+		if (*el->active)
+			lrc_runtime_start((*el->active)->context);
+		new_timeslice(el);
 	}
 
 	return inactive;
 }
 
-static void post_process_csb(struct intel_engine_cs *engine,
+static void post_process_csb(struct intel_execlists *el,
 			     struct i915_request **port,
 			     struct i915_request **last)
 {
 	while (port != last)
-		execlists_schedule_out(engine, *port++);
+		execlists_schedule_out(el, *port++);
 }
 
 struct execlists_capture {
@@ -2007,9 +2109,8 @@ static struct execlists_capture *capture_regs(struct intel_engine_cs *engine)
 }
 
 static struct i915_request *
-active_context(struct intel_engine_cs *engine, u32 ccid)
+active_context(struct intel_execlists *el, u32 ccid)
 {
-	const struct intel_engine_execlists * const el = &engine->execlists;
 	struct i915_request * const *port, *rq;
 
 	/*
@@ -2020,7 +2121,7 @@ active_context(struct intel_engine_cs *engine, u32 ccid)
 
 	for (port = el->active; (rq = *port); port++) {
 		if (rq->context->lrc.ccid == ccid) {
-			ENGINE_TRACE(engine,
+			ENGINE_TRACE(el->sched.priv,
 				     "ccid:%x found at active:%zd\n",
 				     ccid, port - el->active);
 			return rq;
@@ -2029,14 +2130,14 @@ active_context(struct intel_engine_cs *engine, u32 ccid)
 
 	for (port = el->pending; (rq = *port); port++) {
 		if (rq->context->lrc.ccid == ccid) {
-			ENGINE_TRACE(engine,
+			ENGINE_TRACE(el->sched.priv,
 				     "ccid:%x found at pending:%zd\n",
 				     ccid, port - el->pending);
 			return rq;
 		}
 	}
 
-	ENGINE_TRACE(engine, "ccid:%x not found\n", ccid);
+	ENGINE_TRACE(el->sched.priv, "ccid:%x not found\n", ccid);
 	return NULL;
 }
 
@@ -2045,7 +2146,7 @@ static u32 active_ccid(struct intel_engine_cs *engine)
 	return ENGINE_READ_FW(engine, RING_EXECLIST_STATUS_HI);
 }
 
-static void execlists_capture(struct intel_engine_cs *engine)
+static void execlists_capture(struct intel_execlists *el)
 {
 	struct execlists_capture *cap;
 	struct i915_request *rq;
@@ -2053,7 +2154,7 @@ static void execlists_capture(struct intel_engine_cs *engine)
 	if (!IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR))
 		return;
 
-	rq = active_context(engine, active_ccid(engine));
+	rq = active_context(el, active_ccid(el->sched.priv));
 
 	/*
 	 * If the context is closed or already banned, assume no one is
@@ -2071,7 +2172,7 @@ static void execlists_capture(struct intel_engine_cs *engine)
 	 * We are inside an atomic section (softirq) here and we are delaying
 	 * the forced preemption event.
 	 */
-	cap = capture_regs(engine);
+	cap = capture_regs(el->sched.priv);
 	if (!cap)
 		return;
 
@@ -2100,7 +2201,7 @@ static void execlists_capture(struct intel_engine_cs *engine)
 	 * simply hold that request accountable for being non-preemptible
 	 * long enough to force the reset.
 	 */
-	if (!i915_sched_suspend_request(engine->sched, cap->rq))
+	if (!i915_sched_suspend_request(&el->sched, cap->rq))
 		goto err_rq;
 
 	INIT_WORK(&cap->work, execlists_capture_work);
@@ -2114,12 +2215,12 @@ static void execlists_capture(struct intel_engine_cs *engine)
 	kfree(cap);
 }
 
-static noinline void execlists_reset(struct intel_engine_cs *engine)
+static noinline void execlists_reset(struct intel_execlists *el)
 {
-	struct i915_sched *se = intel_engine_get_scheduler(engine);
+	struct intel_engine_cs *engine = el->sched.priv;
 	const unsigned int bit = I915_RESET_ENGINE + engine->id;
 	unsigned long *lock = &engine->gt->reset.flags;
-	unsigned long eir = fetch_and_zero(&engine->execlists.error_interrupt);
+	unsigned long eir = fetch_and_zero(&el->error_interrupt);
 	const char *msg;
 
 	if (!intel_has_reset_engine(engine->gt))
@@ -2140,19 +2241,19 @@ static noinline void execlists_reset(struct intel_engine_cs *engine)
 	ENGINE_TRACE(engine, "reset for %s\n", msg);
 
 	/* Mark this tasklet as disabled to avoid waiting for it to complete */
-	tasklet_disable_nosync(&se->tasklet);
+	tasklet_disable_nosync(&el->sched.tasklet);
 
-	ring_set_paused(engine, 1); /* Freeze the current request in place */
-	execlists_capture(engine);
-	intel_engine_reset(engine, msg);
+	ring_set_paused(el, 1); /* Freeze the current request in place */
+	execlists_capture(el);
+	intel_engine_reset(el->sched.priv, msg);
 
-	tasklet_enable(&se->tasklet);
+	tasklet_enable(&el->sched.tasklet);
 	clear_and_wake_up_bit(bit, lock);
 }
 
-static bool preempt_timeout(const struct intel_engine_cs *const engine)
+static bool preempt_timeout(const struct intel_execlists *const el)
 {
-	const struct timer_list *t = &engine->execlists.preempt;
+	const struct timer_list *t = &el->preempt;
 
 	if (!CONFIG_DRM_I915_PREEMPT_TIMEOUT)
 		return false;
@@ -2160,7 +2261,7 @@ static bool preempt_timeout(const struct intel_engine_cs *const engine)
 	if (!timer_expired(t))
 		return false;
 
-	return engine->execlists.pending[0];
+	return el->pending[0];
 }
 
 /*
@@ -2169,8 +2270,7 @@ static bool preempt_timeout(const struct intel_engine_cs *const engine)
  */
 static void execlists_submission_tasklet(struct tasklet_struct *t)
 {
-	struct i915_sched * const se = from_tasklet(se, t, tasklet);
-	struct intel_engine_cs *engine = se->priv;
+	struct intel_execlists *el = from_tasklet(el, t, sched.tasklet);
 	struct i915_request *post[2 * EXECLIST_MAX_PORTS];
 	struct i915_request **inactive;
 
@@ -2181,28 +2281,29 @@ static void execlists_submission_tasklet(struct tasklet_struct *t)
 	 */
 
 	rcu_read_lock();
-	inactive = process_csb(engine, post);
+	inactive = process_csb(el, post);
 	GEM_BUG_ON(inactive - post > ARRAY_SIZE(post));
 
-	if (unlikely(preempt_timeout(engine))) {
-		cancel_timer(&engine->execlists.preempt);
-		engine->execlists.error_interrupt |= ERROR_PREEMPT;
+	if (unlikely(preempt_timeout(el))) {
+		cancel_timer(&el->preempt);
+		el->error_interrupt |= ERROR_PREEMPT;
 	}
 
-	if (unlikely(READ_ONCE(engine->execlists.error_interrupt)))
-		execlists_reset(engine);
+	if (unlikely(READ_ONCE(el->error_interrupt)))
+		execlists_reset(el);
 
-	if (!engine->execlists.pending[0]) {
-		execlists_dequeue(engine);
-		start_timeslice(engine);
+	if (!el->pending[0]) {
+		execlists_dequeue(el);
+		start_timeslice(el);
 	}
 
-	post_process_csb(engine, post, inactive);
+	post_process_csb(el, post, inactive);
 	rcu_read_unlock();
 }
 
 static void execlists_irq_handler(struct intel_engine_cs *engine, u16 iir)
 {
+	struct intel_execlists *el = to_execlists(engine);
 	bool tasklet = false;
 
 	if (unlikely(iir & GT_CS_MASTER_ERROR_INTERRUPT)) {
@@ -2216,17 +2317,16 @@ static void execlists_irq_handler(struct intel_engine_cs *engine, u16 iir)
 		if (likely(eir)) {
 			ENGINE_WRITE(engine, RING_EMR, ~0u);
 			ENGINE_WRITE(engine, RING_EIR, eir);
-			WRITE_ONCE(engine->execlists.error_interrupt, eir);
+			WRITE_ONCE(el->error_interrupt, eir);
 			tasklet = true;
 		}
 	}
 
 	if (iir & GT_WAIT_SEMAPHORE_INTERRUPT) {
-		WRITE_ONCE(engine->execlists.yield,
+		WRITE_ONCE(el->yield,
 			   ENGINE_READ_FW(engine, RING_EXECLIST_STATUS_HI));
-		ENGINE_TRACE(engine, "semaphore yield: %08x\n",
-			     engine->execlists.yield);
-		if (del_timer(&engine->execlists.timer))
+		ENGINE_TRACE(engine, "semaphore yield: %08x\n", el->yield);
+		if (del_timer(&el->timer))
 			tasklet = true;
 	}
 
@@ -2237,19 +2337,17 @@ static void execlists_irq_handler(struct intel_engine_cs *engine, u16 iir)
 		intel_engine_signal_breadcrumbs(engine);
 
 	if (tasklet)
-		intel_engine_kick_scheduler(engine);
+		i915_sched_kick(&el->sched);
 }
 
-static void __execlists_kick(struct intel_engine_execlists *execlists)
-{
-	struct intel_engine_cs *engine =
-		container_of(execlists, typeof(*engine), execlists);
 
-	intel_engine_kick_scheduler(engine);
+static void __execlists_kick(struct intel_execlists *el)
+{
+	i915_sched_kick(&el->sched);
 }
 
 #define execlists_kick(t, member) \
-	__execlists_kick(container_of(t, struct intel_engine_execlists, member))
+	__execlists_kick(container_of(t, struct intel_execlists, member))
 
 static void execlists_timeslice(struct timer_list *timer)
 {
@@ -2391,12 +2489,12 @@ static const struct intel_context_ops execlists_context_ops = {
 	.destroy = lrc_destroy,
 };
 
-static void reset_csb_pointers(struct intel_engine_cs *engine)
+static void reset_csb_pointers(struct intel_execlists *el)
 {
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-	const unsigned int reset_value = execlists->csb_size - 1;
+	struct intel_engine_cs *engine = el->sched.priv;
+	const unsigned int reset_value = el->csb_size - 1;
 
-	ring_set_paused(engine, 0);
+	ring_set_paused(el, 0);
 
 	/*
 	 * Sometimes Icelake forgets to reset its pointers on a GPU reset.
@@ -2415,21 +2513,21 @@ static void reset_csb_pointers(struct intel_engine_cs *engine)
 	 * inline comparison of our cached head position against the last HW
 	 * write works even before the first interrupt.
 	 */
-	execlists->csb_head = reset_value;
-	WRITE_ONCE(*execlists->csb_write, reset_value);
+	el->csb_head = reset_value;
+	WRITE_ONCE(*el->csb_write, reset_value);
 	wmb(); /* Make sure this is visible to HW (paranoia?) */
 
 	/* Check that the GPU does indeed update the CSB entries! */
-	memset(execlists->csb_status, -1, (reset_value + 1) * sizeof(u64));
-	invalidate_csb_entries(&execlists->csb_status[0],
-			       &execlists->csb_status[reset_value]);
+	memset(el->csb_status, -1, (reset_value + 1) * sizeof(u64));
+	invalidate_csb_entries(&el->csb_status[0],
+			       &el->csb_status[reset_value]);
 
 	/* Once more for luck and our trusty paranoia */
 	ENGINE_WRITE(engine, RING_CONTEXT_STATUS_PTR,
 		     0xffff << 16 | reset_value << 8 | reset_value);
 	ENGINE_POSTING_READ(engine, RING_CONTEXT_STATUS_PTR);
 
-	GEM_BUG_ON(READ_ONCE(*execlists->csb_write) != reset_value);
+	GEM_BUG_ON(READ_ONCE(*el->csb_write) != reset_value);
 }
 
 static void sanitize_hwsp(struct intel_engine_cs *engine)
@@ -2442,7 +2540,7 @@ static void sanitize_hwsp(struct intel_engine_cs *engine)
 
 static void execlists_sanitize(struct intel_engine_cs *engine)
 {
-	GEM_BUG_ON(*engine->execlists.active);
+	GEM_BUG_ON(*to_execlists(engine)->active);
 
 	/*
 	 * Poison residual state on resume, in case the suspend didn't!
@@ -2456,7 +2554,7 @@ static void execlists_sanitize(struct intel_engine_cs *engine)
 	if (IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM))
 		memset(engine->status_page.addr, POISON_INUSE, PAGE_SIZE);
 
-	reset_csb_pointers(engine);
+	reset_csb_pointers(to_execlists(engine));
 
 	/*
 	 * The kernel_context HWSP is stored in the status_page. As above,
@@ -2475,7 +2573,7 @@ static void enable_error_interrupt(struct intel_engine_cs *engine)
 
 	/* Flush ongoing GT interrupts before touching interrupt state */
 	synchronize_hardirq(engine->i915->drm.irq);
-	engine->execlists.error_interrupt = 0;
+	to_execlists(engine)->error_interrupt = 0;
 
 	ENGINE_WRITE(engine, RING_EMR, ~0u);
 	ENGINE_WRITE(engine, RING_EIR, ~0u); /* clear all existing errors */
@@ -2551,6 +2649,8 @@ static int execlists_resume(struct intel_engine_cs *engine)
 
 static void execlists_reset_prepare(struct intel_engine_cs *engine)
 {
+	struct intel_execlists *el = to_execlists(engine);
+
 	/*
 	 * Prevent request submission to the hardware until we have
 	 * completed the reset in i915_gem_reset_finish(). If a request
@@ -2560,7 +2660,7 @@ static void execlists_reset_prepare(struct intel_engine_cs *engine)
 	 * Turning off the execlists->tasklet until the reset is over
 	 * prevents the race.
 	 */
-	i915_sched_disable_tasklet(intel_engine_get_scheduler(engine));
+	i915_sched_disable_tasklet(&el->sched);
 
 	/*
 	 * We stop engines, otherwise we might get failed reset and a
@@ -2574,31 +2674,29 @@ static void execlists_reset_prepare(struct intel_engine_cs *engine)
 	 *
 	 * FIXME: Wa for more modern gens needs to be validated
 	 */
-	ring_set_paused(engine, 1);
+	ring_set_paused(el, 1);
 	intel_engine_stop_cs(engine);
 
-	engine->execlists.reset_ccid = active_ccid(engine);
+	el->reset_ccid = active_ccid(engine);
 }
 
 static struct i915_request **
-reset_csb(struct intel_engine_cs *engine, struct i915_request **inactive)
+reset_csb(struct intel_execlists *el, struct i915_request **inactive)
 {
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-
 	mb(); /* paranoia: read the CSB pointers from after the reset */
-	clflush(execlists->csb_write);
+	clflush(el->csb_write);
 	mb();
 
-	inactive = process_csb(engine, inactive); /* drain preemption events */
+	inactive = process_csb(el, inactive); /* drain preemption events */
 
 	/* Following the reset, we need to reload the CSB read/write pointers */
-	reset_csb_pointers(engine);
+	reset_csb_pointers(el);
 
 	return inactive;
 }
 
 static void
-execlists_reset_active(struct intel_engine_cs *engine, bool stalled)
+execlists_reset_active(struct intel_execlists *el, bool stalled)
 {
 	struct intel_context *ce;
 	struct i915_request *rq;
@@ -2609,7 +2707,7 @@ execlists_reset_active(struct intel_engine_cs *engine, bool stalled)
 	 * its request, it was still running at the time of the
 	 * reset and will have been clobbered.
 	 */
-	rq = active_context(engine, engine->execlists.reset_ccid);
+	rq = active_context(el, el->reset_ccid);
 	if (!rq)
 		return;
 
@@ -2623,7 +2721,7 @@ execlists_reset_active(struct intel_engine_cs *engine, bool stalled)
 	}
 
 	/* We still have requests in-flight; the engine should be active */
-	GEM_BUG_ON(!intel_engine_pm_is_awake(engine));
+	GEM_BUG_ON(!intel_engine_pm_is_awake(el->sched.priv));
 
 	/* Context has requests still in-flight; it should not be idle! */
 	GEM_BUG_ON(i915_active_is_idle(&ce->active));
@@ -2669,50 +2767,48 @@ execlists_reset_active(struct intel_engine_cs *engine, bool stalled)
 	 * to recreate its own state.
 	 */
 out_replay:
-	ENGINE_TRACE(engine, "replay {head:%04x, tail:%04x}\n",
+	ENGINE_TRACE(el->sched.priv, "replay {head:%04x, tail:%04x}\n",
 		     head, ce->ring->tail);
-	lrc_reset_regs(ce, engine);
-	ce->lrc.lrca = lrc_update_regs(ce, engine, head);
+	lrc_reset_regs(ce, el->sched.priv);
+	ce->lrc.lrca = lrc_update_regs(ce, el->sched.priv, head);
 }
 
-static void execlists_reset_csb(struct intel_engine_cs *engine, bool stalled)
+static void execlists_reset_csb(struct intel_execlists *el, bool stalled)
 {
-	struct intel_engine_execlists * const execlists = &engine->execlists;
 	struct i915_request *post[2 * EXECLIST_MAX_PORTS];
 	struct i915_request **inactive;
 
 	rcu_read_lock();
-	inactive = reset_csb(engine, post);
+	inactive = reset_csb(el, post);
 
-	execlists_reset_active(engine, true);
+	execlists_reset_active(el, true);
 
-	inactive = cancel_port_requests(execlists, inactive);
-	post_process_csb(engine, post, inactive);
+	inactive = cancel_port_requests(el, inactive);
+	post_process_csb(el, post, inactive);
 	rcu_read_unlock();
 }
 
 static void execlists_reset_rewind(struct intel_engine_cs *engine, bool stalled)
 {
-	struct i915_sched *se = intel_engine_get_scheduler(engine);
+	struct intel_execlists *el = to_execlists(engine);
 	unsigned long flags;
 
 	ENGINE_TRACE(engine, "\n");
 
 	/* Process the csb, find the guilty context and throw away */
-	execlists_reset_csb(engine, stalled);
+	execlists_reset_csb(el, stalled);
 
 	/* Push back any incomplete requests for replay after the reset. */
 	rcu_read_lock();
-	spin_lock_irqsave(&se->lock, flags);
-	__i915_sched_rewind_requests(se);
-	spin_unlock_irqrestore(&se->lock, flags);
+	spin_lock_irqsave(&el->sched.lock, flags);
+	__i915_sched_rewind_requests(&el->sched);
+	spin_unlock_irqrestore(&el->sched.lock, flags);
 	rcu_read_unlock();
 }
 
 static void execlists_reset_cancel(struct intel_engine_cs *engine)
 {
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct i915_sched *se = intel_engine_get_scheduler(engine);
+	struct intel_execlists * const el = to_execlists(engine);
 	unsigned long flags;
 	struct rb_node *rb;
 
@@ -2732,19 +2828,19 @@ static void execlists_reset_cancel(struct intel_engine_cs *engine)
 	 * submission's irq state, we also wish to remind ourselves that
 	 * it is irq state.)
 	 */
-	execlists_reset_csb(engine, true);
+	execlists_reset_csb(el, true);
 
 	rcu_read_lock();
-	spin_lock_irqsave(&se->lock, flags);
+	spin_lock_irqsave(&el->sched.lock, flags);
 
-	__i915_sched_cancel_queue(se);
+	__i915_sched_cancel_queue(&el->sched);
 
 	/* Cancel all attached virtual engines */
-	while ((rb = rb_first_cached(&execlists->virtual))) {
+	while ((rb = rb_first_cached(&el->virtual))) {
 		struct virtual_engine *ve =
 			rb_entry(rb, typeof(*ve), nodes[engine->id].rb);
 
-		rb_erase_cached(rb, &execlists->virtual);
+		rb_erase_cached(rb, &el->virtual);
 		RB_CLEAR_NODE(rb);
 
 		spin_lock(&ve->base.sched->lock);
@@ -2752,7 +2848,7 @@ static void execlists_reset_cancel(struct intel_engine_cs *engine)
 		spin_unlock(&ve->base.sched->lock);
 	}
 
-	spin_unlock_irqrestore(&se->lock, flags);
+	spin_unlock_irqrestore(&el->sched.lock, flags);
 	rcu_read_unlock();
 
 	intel_engine_signal_breadcrumbs(engine);
@@ -2788,17 +2884,38 @@ static void gen8_logical_ring_disable_irq(struct intel_engine_cs *engine)
 
 static void execlists_park(struct intel_engine_cs *engine)
 {
-	cancel_timer(&engine->execlists.timer);
-	cancel_timer(&engine->execlists.preempt);
+	struct intel_execlists *el = to_execlists(engine);
+
+	cancel_timer(&el->timer);
+	cancel_timer(&el->preempt);
+}
+
+static inline struct i915_request *
+execlists_active(const struct intel_execlists *el)
+{
+	struct i915_request * const *cur, * const *old, *active;
+
+	cur = READ_ONCE(el->active);
+	smp_rmb(); /* pairs with overwrite protection in process_csb() */
+	do {
+		old = cur;
+
+		active = READ_ONCE(*cur);
+		cur = READ_ONCE(el->active);
+
+		smp_rmb(); /* and complete the seqlock retry */
+	} while (unlikely(cur != old));
+
+	return active;
 }
 
 static struct i915_request *
 execlists_active_request(const struct i915_sched *se)
 {
-	const struct intel_engine_cs *engine = se->priv;
+	const struct intel_execlists *el = as_execlists(se);
 	struct i915_request *rq;
 
-	rq = execlists_active(&engine->execlists);
+	rq = execlists_active(el);
 	if (rq)
 		rq = active_request(rq->context->timeline, rq);
 
@@ -2863,18 +2980,18 @@ static void execlists_set_default_submission(struct intel_engine_cs *engine)
 	engine->sched->submit_request = i915_request_enqueue;
 }
 
-static void execlists_shutdown(struct intel_engine_cs *engine)
+static void execlists_shutdown(struct intel_execlists *el)
 {
 	/* Synchronise with residual timers and any softirq they raise */
-	del_timer_sync(&engine->execlists.timer);
-	del_timer_sync(&engine->execlists.preempt);
+	del_timer_sync(&el->timer);
+	del_timer_sync(&el->preempt);
 }
 
 static void execlists_release(struct intel_engine_cs *engine)
 {
 	engine->sanitize = NULL; /* no longer in control, nothing to sanitize */
 
-	execlists_shutdown(engine);
+	execlists_shutdown(to_execlists(engine));
 
 	intel_engine_cleanup_common(engine);
 	lrc_fini_wa_ctx(engine);
@@ -2973,71 +3090,85 @@ static void rcs_submission_override(struct intel_engine_cs *engine)
 	}
 }
 
-static int init_execlists(struct intel_engine_cs *engine)
+static struct i915_sched *init_execlists(struct intel_engine_cs *engine)
 {
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct drm_i915_private *i915 = engine->i915;
 	struct intel_uncore *uncore = engine->uncore;
+	struct drm_i915_private *i915 = engine->i915;
 	u32 base = engine->mmio_base;
+	struct intel_execlists *el;
 
-	engine->sched =
-		i915_sched_create(i915->drm.dev,
-				  engine->name,
-				  engine->mask,
-				  execlists_submission_tasklet, engine,
-				  ENGINE_PHYSICAL);
-	if (!engine->sched)
-		return -ENOMEM;
+	el = kzalloc(sizeof(*el), GFP_KERNEL);
+	if (!el)
+		return NULL;
 
-	engine->sched->submit_request = i915_request_enqueue;
-	engine->sched->active_request = execlists_active_request;
-	engine->sched->revoke_context = execlists_revoke_context;
-	engine->sched->show = execlists_show;
+	i915_sched_init(&el->sched,
+			i915->drm.dev,
+			engine->name,
+			engine->mask,
+			execlists_submission_tasklet, engine,
+			ENGINE_PHYSICAL);
 
-	i915_sched_select_mode(engine->sched, I915_SCHED_MODE_DEADLINE);
+	el->sched.submit_request = i915_request_enqueue;
+	el->sched.active_request = execlists_active_request;
+	el->sched.revoke_context = execlists_revoke_context;
+	el->sched.show = execlists_show;
 
-	intel_engine_init_execlists(engine);
-
-	if (IS_ACTIVE(CONFIG_DRM_I915_TIMESLICE_DURATION) &&
-	    intel_engine_has_preemption(engine))
-		__set_bit(I915_SCHED_TIMESLICE_BIT, &engine->sched->flags);
+	i915_sched_select_mode(&el->sched, I915_SCHED_MODE_DEADLINE);
 
 	if (intel_engine_has_preemption(engine)) {
-		__set_bit(I915_SCHED_BUSYWAIT_BIT, &engine->sched->flags);
-		__set_bit(I915_SCHED_PREEMPT_RESET_BIT, &engine->sched->flags);
+		__set_bit(I915_SCHED_BUSYWAIT_BIT, &el->sched.flags);
+
+		if (IS_ACTIVE(CONFIG_DRM_I915_TIMESLICE_DURATION))
+			__set_bit(I915_SCHED_TIMESLICE_BIT,
+				  &el->sched.flags);
+
+		if (IS_ACTIVE(CONFIG_DRM_I915_PREEMPT_TIMEOUT))
+			__set_bit(I915_SCHED_PREEMPT_RESET_BIT,
+				  &el->sched.flags);
 	}
 
-	timer_setup(&engine->execlists.timer, execlists_timeslice, 0);
-	timer_setup(&engine->execlists.preempt, execlists_preempt, 0);
+	if (INTEL_GEN(i915) >= 12)
+		el->flags |= GEN12_CSB_PARSE;
+
+	el->id = engine->id;
+	el->pause = &engine->status_page.addr[I915_GEM_HWS_PREEMPT];
+
+	el->port_mask = 1;
+	GEM_BUG_ON(!is_power_of_2(num_ports(el)));
+	GEM_BUG_ON(num_ports(el) > EXECLIST_MAX_PORTS);
+	el->active = el->inflight;
+
+	timer_setup(&el->timer, execlists_timeslice, 0);
+	timer_setup(&el->preempt, execlists_preempt, 0);
 
 	if (HAS_LOGICAL_RING_ELSQ(i915)) {
-		execlists->submit_reg = uncore->regs +
+		el->submit_reg = uncore->regs +
 			i915_mmio_reg_offset(RING_EXECLIST_SQ_CONTENTS(base));
-		execlists->ctrl_reg = uncore->regs +
+		el->ctrl_reg = uncore->regs +
 			i915_mmio_reg_offset(RING_EXECLIST_CONTROL(base));
 	} else {
-		execlists->submit_reg = uncore->regs +
+		el->submit_reg = uncore->regs +
 			i915_mmio_reg_offset(RING_ELSP(base));
 	}
 
-	execlists->csb_status =
+	el->csb_status =
 		(u64 *)&engine->status_page.addr[I915_HWS_CSB_BUF0_INDEX];
 
-	execlists->csb_write =
+	el->csb_write =
 		&engine->status_page.addr[intel_hws_csb_write_index(i915)];
 
 	if (INTEL_GEN(i915) < 11)
-		execlists->csb_size = GEN8_CSB_ENTRIES;
+		el->csb_size = GEN8_CSB_ENTRIES;
 	else
-		execlists->csb_size = GEN11_CSB_ENTRIES;
+		el->csb_size = GEN11_CSB_ENTRIES;
 
-	engine->context_tag = GENMASK(BITS_PER_LONG - 2, 0);
-	if (INTEL_GEN(i915) >= 11) {
-		execlists->ccid |= engine->instance << (GEN11_ENGINE_INSTANCE_SHIFT - 32);
-		execlists->ccid |= engine->class << (GEN11_ENGINE_CLASS_SHIFT - 32);
-	}
+	el->ccid_tags = GENMASK(BITS_PER_LONG - 2, 0);
+	if (INTEL_GEN(i915) >= 11)
+		el->ccid_engine =
+			engine->instance << (GEN11_ENGINE_INSTANCE_SHIFT - 32) |
+			engine->class << (GEN11_ENGINE_CLASS_SHIFT - 32);
 
-	return 0;
+	return &el->sched;
 }
 
 int intel_execlists_submission_setup(struct intel_engine_cs *engine)
@@ -3048,7 +3179,8 @@ int intel_execlists_submission_setup(struct intel_engine_cs *engine)
 	if (engine->class == RENDER_CLASS)
 		rcs_submission_override(engine);
 
-	if (init_execlists(engine))
+	engine->sched = init_execlists(engine);
+	if (!engine->sched)
 		return -ENOMEM;
 
 	lrc_init_wa_ctx(engine);
@@ -3062,8 +3194,7 @@ int intel_execlists_submission_setup(struct intel_engine_cs *engine)
 
 static void rcu_virtual_context_destroy(struct work_struct *wrk)
 {
-	struct virtual_engine *ve =
-		container_of(wrk, typeof(*ve), rcu.work);
+	struct virtual_engine *ve = container_of(wrk, typeof(*ve), rcu.work);
 	struct i915_sched *se = intel_engine_get_scheduler(&ve->base);
 	unsigned int n;
 
@@ -3099,7 +3230,7 @@ static void rcu_virtual_context_destroy(struct work_struct *wrk)
 
 		/* Detachment is lazily performed in the execlists tasklet */
 		if (!RB_EMPTY_NODE(node))
-			rb_erase_cached(node, &sibling->execlists.virtual);
+			rb_erase_cached(node, &to_execlists(sibling)->virtual);
 
 		spin_unlock_irq(&sibling->sched->lock);
 	}
@@ -3161,7 +3292,7 @@ virtual_engine_initial_hint(struct virtual_engine *ve)
 
 static int virtual_context_alloc(struct intel_context *ce)
 {
-	struct virtual_engine *ve = container_of(ce, typeof(*ve), context);
+	struct virtual_engine *ve = to_virtual_context(ce);
 
 	return lrc_alloc(ce, ve->siblings[0]);
 }
@@ -3170,7 +3301,7 @@ static int virtual_context_pre_pin(struct intel_context *ce,
 				   struct i915_gem_ww_ctx *ww,
 				   void **vaddr)
 {
-	struct virtual_engine *ve = container_of(ce, typeof(*ve), context);
+	struct virtual_engine *ve = to_virtual_context(ce);
 
 	/* Note: we must use a real engine class for setting up reg state */
 	return lrc_pre_pin(ce, ve->siblings[0], ww, vaddr);
@@ -3178,14 +3309,14 @@ static int virtual_context_pre_pin(struct intel_context *ce,
 
 static int virtual_context_pin(struct intel_context *ce, void *vaddr)
 {
-	struct virtual_engine *ve = container_of(ce, typeof(*ve), context);
+	struct virtual_engine *ve = to_virtual_context(ce);
 
 	return lrc_pin(ce, ve->siblings[0], vaddr);
 }
 
 static void virtual_context_enter(struct intel_context *ce)
 {
-	struct virtual_engine *ve = container_of(ce, typeof(*ve), context);
+	struct virtual_engine *ve = to_virtual_context(ce);
 	unsigned int n;
 
 	for (n = 0; n < ve->num_siblings; n++)
@@ -3196,7 +3327,7 @@ static void virtual_context_enter(struct intel_context *ce)
 
 static void virtual_context_exit(struct intel_context *ce)
 {
-	struct virtual_engine *ve = container_of(ce, typeof(*ve), context);
+	struct virtual_engine *ve = to_virtual_context(ce);
 	unsigned int n;
 
 	intel_timeline_exit(ce->timeline);
@@ -3264,17 +3395,16 @@ static void virtual_submission_tasklet(struct tasklet_struct *t)
 
 	for (n = 0; n < ve->num_siblings; n++) {
 		struct intel_engine_cs *sibling = READ_ONCE(ve->siblings[n]);
-		struct i915_sched *se = intel_engine_get_scheduler(sibling);
+		struct intel_execlists *el = to_execlists(sibling);
 		struct ve_node * const node = &ve->nodes[sibling->id];
 		struct rb_node **parent, *rb;
 		bool first;
 
-		spin_lock_irq(&se->lock);
+		spin_lock_irq(&el->sched.lock);
 
-		if (unlikely(!virtual_matches(ve, rq, sibling))) {
+		if (unlikely(!virtual_matches(ve, rq, el))) {
 			if (!RB_EMPTY_NODE(&node->rb)) {
-				rb_erase_cached(&node->rb,
-						&sibling->execlists.virtual);
+				rb_erase_cached(&node->rb, &el->virtual);
 				RB_CLEAR_NODE(&node->rb);
 			}
 
@@ -3286,18 +3416,17 @@ static void virtual_submission_tasklet(struct tasklet_struct *t)
 			 * Cheat and avoid rebalancing the tree if we can
 			 * reuse this node in situ.
 			 */
-			first = rb_first_cached(&sibling->execlists.virtual) ==
-				&node->rb;
+			first = rb_first_cached(&el->virtual) == &node->rb;
 			if (deadline == node->deadline ||
 			    (deadline < node->deadline && first))
 				goto submit_engine;
 
-			rb_erase_cached(&node->rb, &sibling->execlists.virtual);
+			rb_erase_cached(&node->rb, &el->virtual);
 		}
 
 		rb = NULL;
 		first = true;
-		parent = &sibling->execlists.virtual.rb_root.rb_node;
+		parent = &el->virtual.rb_root.rb_node;
 		while (*parent) {
 			struct ve_node *other;
 
@@ -3313,17 +3442,17 @@ static void virtual_submission_tasklet(struct tasklet_struct *t)
 
 		rb_link_node(&node->rb, rb, parent);
 		rb_insert_color_cached(&node->rb,
-				       &sibling->execlists.virtual,
+				       &to_execlists(sibling)->virtual,
 				       first);
 
 submit_engine:
 		GEM_BUG_ON(RB_EMPTY_NODE(&node->rb));
 		node->deadline = deadline;
 		if (first)
-			i915_sched_kick(se);
+			i915_sched_kick(&el->sched);
 
 unlock_engine:
-		spin_unlock_irq(&se->lock);
+		spin_unlock_irq(&el->sched.lock);
 
 		if (intel_context_inflight(&ve->context))
 			break;
@@ -3399,8 +3528,6 @@ intel_execlists_create_virtual(struct intel_engine_cs **siblings,
 
 	snprintf(ve->base.name, sizeof(ve->base.name), "virtual");
 
-	intel_engine_init_execlists(&ve->base);
-
 	ve->base.cops = &virtual_context_ops;
 
 	ve->base.bond_execute = virtual_bond_execute;
@@ -3605,8 +3732,8 @@ static void execlists_show(struct drm_printer *m,
 						int indent),
 			   unsigned int max)
 {
+	const struct intel_execlists *el = as_execlists(se);
 	const struct intel_engine_cs *engine = se->priv;
-	const struct intel_engine_execlists *el = &engine->execlists;
 	const u8 num_entries = el->csb_size;
 	const u64 *hws = el->csb_status;
 	struct i915_request * const *port;
@@ -3624,7 +3751,7 @@ static void execlists_show(struct drm_printer *m,
 	count = 0;
 	for (rb = rb_first_cached(&el->virtual); rb; rb = rb_next(rb)) {
 		struct virtual_engine *ve =
-			rb_entry(rb, typeof(*ve), nodes[engine->id].rb);
+			rb_entry(rb, typeof(*ve), nodes[el->id].rb);
 		struct i915_request *rq;
 
 		rq = first_request(ve->base.sched);
@@ -3701,6 +3828,9 @@ static void execlists_show(struct drm_printer *m,
 		   repr_timer(&el->preempt),
 		   repr_timer(&el->timer));
 
+	drm_printf(m, "Forcewake: %x domains, %d active\n",
+		   el->fw_domain, READ_ONCE(el->fw_active));
+
 	rcu_read_unlock();
 	intel_runtime_pm_put(engine->uncore->rpm, wakeref);
 }
diff --git a/drivers/gpu/drm/i915/gt/intel_ring_scheduler.c b/drivers/gpu/drm/i915/gt/intel_ring_scheduler.c
index d4c54ebdf13b..cf6207ea3b4e 100644
--- a/drivers/gpu/drm/i915/gt/intel_ring_scheduler.c
+++ b/drivers/gpu/drm/i915/gt/intel_ring_scheduler.c
@@ -31,6 +31,41 @@
  */
 #define LEGACY_REQUEST_SIZE 200
 
+struct ring_sched {
+	struct i915_sched sched;
+
+#define MAX_PORTS 2
+	/**
+	 * @active: the currently known context executing on HW
+	 */
+	struct i915_request * const *active;
+	/**
+	 * @inflight: the set of contexts submitted and acknowleged by HW
+	 *
+	 * The set of inflight contexts is managed by reading CS events
+	 * from the HW. On a context-switch event (not preemption), we
+	 * know the HW has transitioned from port0 to port1, and we
+	 * advance our inflight/active tracking accordingly.
+	 */
+	struct i915_request *inflight[MAX_PORTS + 1 /* sentinel */];
+	/**
+	 * @pending: the next set of contexts submitted to ELSP
+	 *
+	 * We store the array of contexts that we submit to HW (via ELSP) and
+	 * promote them to the inflight array once HW has signaled the
+	 * preemption or idle-to-active event.
+	 */
+	struct i915_request *pending[MAX_PORTS + 1];
+
+	enum forcewake_domains fw_domain;
+	unsigned int fw_active;
+};
+
+static inline struct ring_sched *to_ring_sched(struct intel_engine_cs *engine)
+{
+	return container_of(engine->sched, struct ring_sched, sched);
+}
+
 static void
 set_current_context(struct intel_context **ptr, struct intel_context *ce)
 {
@@ -72,15 +107,15 @@ static inline void runtime_stop(struct intel_context *ce)
 }
 
 static struct intel_engine_cs *
-__schedule_in(struct intel_engine_cs *engine, struct i915_request *rq)
+__schedule_in(struct ring_sched *rs, struct intel_context *ce)
 {
-	struct intel_context *ce = rq->context;
+	struct intel_engine_cs *engine = rs->sched.priv;
 
 	intel_context_get(ce);
 
 	__intel_gt_pm_get(engine->gt);
-	if (engine->fw_domain && !engine->fw_active++)
-		intel_uncore_forcewake_get(engine->uncore, engine->fw_domain);
+	if (rs->fw_domain && !rs->fw_active++)
+		intel_uncore_forcewake_get(engine->uncore, rs->fw_domain);
 
 	intel_engine_context_in(engine);
 
@@ -89,25 +124,26 @@ __schedule_in(struct intel_engine_cs *engine, struct i915_request *rq)
 	return engine;
 }
 
-static void schedule_in(struct intel_engine_cs *engine, struct i915_request *rq)
+static void schedule_in(struct ring_sched *rs, struct i915_request *rq)
 {
 	struct intel_context * const ce = rq->context;
 	struct intel_engine_cs *old;
 
-	GEM_BUG_ON(!intel_engine_pm_is_awake(engine));
+	GEM_BUG_ON(!intel_engine_pm_is_awake(rs->sched.priv));
 
 	old = ce->inflight;
 	if (!old)
-		old = __schedule_in(engine, rq);
+		old = __schedule_in(rs, ce);
 	WRITE_ONCE(ce->inflight, ptr_inc(old));
 
-	GEM_BUG_ON(intel_context_inflight(ce) != engine);
+	GEM_BUG_ON(intel_context_inflight(ce) != rs->sched.priv);
 	GEM_BUG_ON(!intel_context_inflight_count(ce));
 }
 
 static void
-__schedule_out(struct intel_engine_cs *engine, struct i915_request *rq)
+__schedule_out(struct ring_sched *rs, struct i915_request *rq)
 {
+	struct intel_engine_cs *engine = rs->sched.priv;
 	struct intel_context *ce = rq->context;
 
 	CE_TRACE(ce, "schedule-out\n");
@@ -119,21 +155,21 @@ __schedule_out(struct intel_engine_cs *engine, struct i915_request *rq)
 
 	intel_engine_context_out(engine);
 
-	if (engine->fw_domain && !--engine->fw_active)
-		intel_uncore_forcewake_put(engine->uncore, engine->fw_domain);
+	if (rs->fw_domain && !--rs->fw_active)
+		intel_uncore_forcewake_put(engine->uncore, rs->fw_domain);
 	intel_gt_pm_put_async(engine->gt);
 }
 
 static void
-schedule_out(struct intel_engine_cs *engine, struct i915_request *rq)
+schedule_out(struct ring_sched *rs, struct i915_request *rq)
 {
 	struct intel_context *ce = rq->context;
 
 	GEM_BUG_ON(!ce->inflight);
 	ce->inflight = ptr_dec(ce->inflight);
 	if (!intel_context_inflight_count(ce)) {
-		GEM_BUG_ON(ce->inflight != engine);
-		__schedule_out(engine, rq);
+		GEM_BUG_ON(ce->inflight != rs->sched.priv);
+		__schedule_out(rs, rq);
 		WRITE_ONCE(ce->inflight, NULL);
 		intel_context_put(ce);
 	}
@@ -522,24 +558,23 @@ static void wa_write_tail(const struct intel_engine_cs *engine)
 			      _MASKED_BIT_DISABLE(PSMI_SLEEP_MSG_DISABLE));
 }
 
-static inline void write_tail(const struct intel_engine_cs *engine)
+static inline void write_tail(const struct ring_sched *rs)
 {
 	wmb(); /* paranoid flush of WCB before RING_TAIL write */
-	if (!engine->fw_active)
-		__write_tail(engine);
+	if (!rs->fw_active)
+		__write_tail(rs->sched.priv);
 	else
-		wa_write_tail(engine);
+		wa_write_tail(rs->sched.priv);
 }
 
-static void dequeue(struct i915_sched *se, struct intel_engine_cs *engine)
+static void dequeue(struct ring_sched *rs)
 {
-	struct intel_engine_execlists * const el = &engine->execlists;
-	struct i915_request ** const last_port = el->pending + el->port_mask;
+	struct i915_request ** const last_port = rs->pending + MAX_PORTS - 1;
 	struct i915_request **port, **first, *last;
 	struct i915_request *rq, *rn;
 	struct i915_priolist *pl;
 
-	first = copy_active(el->pending, el->active);
+	first = copy_active(rs->pending, rs->active);
 	if (first > last_port)
 		return;
 
@@ -547,8 +582,8 @@ static void dequeue(struct i915_sched *se, struct intel_engine_cs *engine)
 
 	last = NULL;
 	port = first;
-	spin_lock(&se->lock);
-	i915_sched_dequeue(se, pl, rq, rn) {
+	spin_lock(&rs->sched.lock);
+	i915_sched_dequeue(&rs->sched, pl, rq, rn) {
 		if (last && rq->context != last->context) {
 			if (port == last_port)
 				goto done;
@@ -556,55 +591,55 @@ static void dequeue(struct i915_sched *se, struct intel_engine_cs *engine)
 			*port++ = i915_request_get(last);
 		}
 
-		last = ring_submit(engine, rq);
+		last = ring_submit(rs->sched.priv, rq);
 	}
 done:
-	spin_unlock(&se->lock);
+	spin_unlock(&rs->sched.lock);
 
 	if (last) {
 		*port++ = i915_request_get(last);
 		*port = NULL;
 
-		if (!*el->active)
-			runtime_start((*el->pending)->context);
-		WRITE_ONCE(el->active, el->pending);
+		if (!*rs->active)
+			runtime_start((*rs->pending)->context);
+		WRITE_ONCE(rs->active, rs->pending);
 
-		copy_ports(el->inflight, el->pending, port - el->pending + 1);
+		copy_ports(rs->inflight, rs->pending, port - rs->pending + 1);
 		while (port-- != first)
-			schedule_in(engine, *port);
+			schedule_in(rs, *port);
 
-		write_tail(engine);
+		write_tail(rs);
 
-		WRITE_ONCE(el->active, el->inflight);
-		GEM_BUG_ON(!*el->active);
+		WRITE_ONCE(rs->active, rs->inflight);
+		GEM_BUG_ON(!*rs->active);
 	}
 
-	WRITE_ONCE(el->pending[0], NULL);
+	WRITE_ONCE(rs->pending[0], NULL);
 
 	local_irq_enable(); /* flush irq_work *after* RING_TAIL write */
 }
 
-static void post_process_csb(struct intel_engine_cs *engine,
+static void post_process_csb(struct ring_sched *rs,
 			     struct i915_request **port,
 			     struct i915_request **last)
 {
 	while (port != last)
-		schedule_out(engine, *port++);
+		schedule_out(rs, *port++);
 }
 
 static struct i915_request **
-process_csb(struct intel_engine_execlists *el, struct i915_request **inactive)
+process_csb(struct ring_sched *rs, struct i915_request **inactive)
 {
 	struct i915_request *rq;
 
-	while ((rq = *el->active)) {
+	while ((rq = *rs->active)) {
 		if (!__i915_request_is_complete(rq)) {
 			runtime_start(rq->context);
 			break;
 		}
 
 		*inactive++ = rq;
-		el->active++;
+		rs->active++;
 
 		runtime_stop(rq->context);
 	}
@@ -614,19 +649,18 @@ process_csb(struct intel_engine_execlists *el, struct i915_request **inactive)
 
 static void submission_tasklet(struct tasklet_struct *t)
 {
-	struct i915_sched *se = from_tasklet(se, t, tasklet);
-	struct intel_engine_cs *engine = se->priv;
-	struct i915_request *post[2 * EXECLIST_MAX_PORTS];
+	struct ring_sched *rs = from_tasklet(rs, t, sched.tasklet);
+	struct i915_request *post[2 * MAX_PORTS];
 	struct i915_request **inactive;
 
 	rcu_read_lock();
-	inactive = process_csb(&engine->execlists, post);
+	inactive = process_csb(rs, post);
 	GEM_BUG_ON(inactive - post > ARRAY_SIZE(post));
 
-	if (!i915_sched_is_idle(se))
-		dequeue(se, engine);
+	if (!i915_sched_is_idle(&rs->sched))
+		dequeue(rs);
 
-	post_process_csb(engine, post, inactive);
+	post_process_csb(rs, post, inactive);
 	rcu_read_unlock();
 }
 
@@ -642,71 +676,72 @@ static inline void clear_ports(struct i915_request **ports, int count)
 }
 
 static struct i915_request **
-cancel_port_requests(struct intel_engine_execlists * const el,
+cancel_port_requests(struct ring_sched * const rs,
 		     struct i915_request **inactive)
 {
 	struct i915_request * const *port;
 
-	clear_ports(el->pending, ARRAY_SIZE(el->pending));
+	clear_ports(rs->pending, ARRAY_SIZE(rs->pending));
 
 	/* Mark the end of active before we overwrite *active */
-	for (port = xchg(&el->active, el->pending); *port; port++)
+	for (port = xchg(&rs->active, rs->pending); *port; port++)
 		*inactive++ = *port;
-	clear_ports(el->inflight, ARRAY_SIZE(el->inflight));
+	clear_ports(rs->inflight, ARRAY_SIZE(rs->inflight));
 
-	smp_wmb(); /* complete the seqlock for execlists_active() */
-	WRITE_ONCE(el->active, el->inflight);
+	WRITE_ONCE(rs->active, rs->inflight);
 
 	return inactive;
 }
 
-static void __ring_rewind(struct i915_sched *se, bool stalled)
+static void __ring_rewind(struct ring_sched *rs, bool stalled)
 {
 	struct i915_request *rq;
 	unsigned long flags;
 
 	rcu_read_lock();
-	spin_lock_irqsave(&se->lock, flags);
-	rq = __i915_sched_rewind_requests(se);
-	spin_unlock_irqrestore(&se->lock, flags);
+	spin_lock_irqsave(&rs->sched.lock, flags);
+	rq = __i915_sched_rewind_requests(&rs->sched);
+	spin_unlock_irqrestore(&rs->sched.lock, flags);
 	if (rq && __i915_request_has_started(rq))
 		__i915_request_reset(rq, stalled);
 	rcu_read_unlock();
 }
 
-static void ring_reset_csb(struct intel_engine_cs *engine)
+static void ring_reset_csb(struct ring_sched *rs)
 {
-	struct intel_engine_execlists * const el = &engine->execlists;
-	struct i915_request *post[2 * EXECLIST_MAX_PORTS];
+	struct intel_engine_cs *engine = rs->sched.priv;
+	struct i915_request *post[2 * MAX_PORTS];
 	struct i915_request **inactive;
 
 	rcu_read_lock();
-	inactive = cancel_port_requests(el, post);
+	inactive = cancel_port_requests(rs, post);
 
 	/* Clear the global submission state, we will submit from scratch */
 	intel_ring_reset(engine->legacy.ring, 0);
 	set_current_context(&engine->legacy.context, NULL);
 
-	post_process_csb(engine, post, inactive);
+	post_process_csb(rs, post, inactive);
 	rcu_read_unlock();
 }
 
 static void ring_reset_rewind(struct intel_engine_cs *engine, bool stalled)
 {
-	ring_reset_csb(engine);
-	__ring_rewind(engine->sched, stalled);
+	struct ring_sched *rs = to_ring_sched(engine);
+
+	ring_reset_csb(rs);
+	__ring_rewind(rs, stalled);
 }
 
 static void ring_reset_cancel(struct intel_engine_cs *engine)
 {
-	struct i915_sched *se = intel_engine_get_scheduler(engine);
+	struct ring_sched *rs = to_ring_sched(engine);
 	unsigned long flags;
 
-	ring_reset_csb(engine);
+	ring_reset_csb(rs);
 
-	spin_lock_irqsave(&se->lock, flags);
-	__i915_sched_cancel_queue(se);
-	spin_unlock_irqrestore(&se->lock, flags);
+	spin_lock_irqsave(&rs->sched.lock, flags);
+	__i915_sched_cancel_queue(&rs->sched);
+	spin_unlock_irqrestore(&rs->sched.lock, flags);
 
 	intel_engine_signal_breadcrumbs(engine);
 }
@@ -1052,8 +1087,6 @@ static void setup_rcs(struct intel_engine_cs *engine)
 static void setup_vcs(struct intel_engine_cs *engine)
 {
 	if (INTEL_GEN(engine->i915) >= 6) {
-		if (IS_GEN(engine->i915, 6))
-			engine->fw_domain = FORCEWAKE_ALL;
 		engine->emit_flush = gen6_emit_flush_vcs;
 		engine->irq_enable_mask = GT_BSD_USER_INTERRUPT;
 	} else if (INTEL_GEN(engine->i915) >= 5) {
@@ -1086,7 +1119,7 @@ static void setup_vecs(struct intel_engine_cs *engine)
 static unsigned int global_ring_size(void)
 {
 	/* Enough space to hold 2 clients and the context switch */
-	return roundup_pow_of_two(EXECLIST_MAX_PORTS * SZ_16K + SZ_4K);
+	return roundup_pow_of_two(MAX_PORTS * SZ_16K + SZ_4K);
 }
 
 static int gen7_ctx_switch_bb_init(struct intel_engine_cs *engine)
@@ -1141,6 +1174,31 @@ static int gen7_ctx_switch_bb_init(struct intel_engine_cs *engine)
 	return err;
 }
 
+static struct i915_sched *create_ring_sched(struct intel_engine_cs *engine)
+{
+	struct ring_sched *rs;
+
+	rs = kzalloc(sizeof(*rs), GFP_KERNEL);
+	if (!rs)
+		return NULL;
+
+	i915_sched_init(&rs->sched,
+			engine->i915->drm.dev,
+			engine->name,
+			engine->mask,
+			submission_tasklet, engine,
+			ENGINE_PHYSICAL);
+
+	i915_sched_select_mode(&rs->sched, I915_SCHED_MODE_DEADLINE);
+
+	rs->active = rs->inflight;
+
+	if (IS_GEN(engine->i915, 6) && engine->class == VIDEO_DECODE_CLASS)
+		rs->fw_domain = FORCEWAKE_ALL;
+
+	return &rs->sched;
+}
+
 int intel_ring_scheduler_setup(struct intel_engine_cs *engine)
 {
 	struct intel_ring *ring;
@@ -1168,19 +1226,12 @@ int intel_ring_scheduler_setup(struct intel_engine_cs *engine)
 		return -ENODEV;
 	}
 
-	engine->sched =
-		i915_sched_create(engine->i915->drm.dev,
-				  engine->name,
-				  engine->mask,
-				  submission_tasklet, engine,
-				  ENGINE_PHYSICAL);
+	engine->sched = create_ring_sched(engine);
 	if (!engine->sched) {
 		err = -ENOMEM;
 		goto err;
 	}
 
-	i915_sched_select_mode(engine->sched, I915_SCHED_MODE_DEADLINE);
-
 	ring = intel_engine_create_ring(engine, global_ring_size());
 	if (IS_ERR(ring)) {
 		err = PTR_ERR(ring);
diff --git a/drivers/gpu/drm/i915/gt/selftest_execlists.c b/drivers/gpu/drm/i915/gt/selftest_execlists.c
index 1fe716d9f344..f07842637121 100644
--- a/drivers/gpu/drm/i915/gt/selftest_execlists.c
+++ b/drivers/gpu/drm/i915/gt/selftest_execlists.c
@@ -54,7 +54,8 @@ static int wait_for_submit(struct intel_engine_cs *engine,
 
 		/* Wait until the HW has acknowleged the submission (or err) */
 		intel_engine_flush_scheduler(engine);
-		if (!READ_ONCE(engine->execlists.pending[0]) && is_active(rq))
+		if (!READ_ONCE(to_execlists(engine)->pending[0]) &&
+		    is_active(rq))
 			return 0;
 
 		if (done)
@@ -74,7 +75,7 @@ static int wait_for_reset(struct intel_engine_cs *engine,
 		cond_resched();
 		intel_engine_flush_scheduler(engine);
 
-		if (READ_ONCE(engine->execlists.pending[0]))
+		if (READ_ONCE(to_execlists(engine)->pending[0]))
 			continue;
 
 		if (i915_request_completed(rq))
@@ -606,7 +607,6 @@ static int live_hold_reset(void *arg)
 		tasklet_disable(&se->tasklet);
 
 		se->tasklet.callback(&se->tasklet);
-		GEM_BUG_ON(execlists_active(&engine->execlists) != rq);
 
 		i915_request_get(rq);
 		i915_sched_suspend_request(se, rq);
@@ -1180,7 +1180,7 @@ static int live_timeslice_rewind(void *arg)
 		ENGINE_TRACE(engine, "forcing tasklet for rewind\n");
 		while (i915_request_is_active(rq[A2])) { /* semaphore yield! */
 			/* Wait for the timeslice to kick in */
-			del_timer(&engine->execlists.timer);
+			del_timer(&to_execlists(engine)->timer);
 			intel_engine_kick_scheduler(engine);
 			intel_engine_flush_scheduler(engine);
 		}
@@ -1356,7 +1356,7 @@ static int live_timeslice_queue(void *arg)
 		do {
 			cond_resched();
 			intel_engine_flush_scheduler(engine);
-		} while (READ_ONCE(engine->execlists.pending[0]));
+		} while (READ_ONCE(to_execlists(engine)->pending[0]));
 
 		/* Timeslice every jiffy, so within 2 we should signal */
 		if (i915_request_wait(rq, 0, slice_timeout(engine)) < 0) {
@@ -1952,7 +1952,7 @@ static int live_nopreempt(void *arg)
 		if (!intel_engine_has_preemption(engine))
 			continue;
 
-		engine->execlists.preempt_hang.count = 0;
+		to_execlists(engine)->preempt_hang.count = 0;
 
 		rq_a = spinner_create_request(&a.spin,
 					      a.ctx, engine,
@@ -1999,9 +1999,9 @@ static int live_nopreempt(void *arg)
 
 		igt_spinner_end(&b.spin);
 
-		if (engine->execlists.preempt_hang.count) {
+		if (to_execlists(engine)->preempt_hang.count) {
 			pr_err("Preemption recorded x%d; should have been suppressed!\n",
-			       engine->execlists.preempt_hang.count);
+			       to_execlists(engine)->preempt_hang.count);
 			err = -EINVAL;
 			goto err_wedged;
 		}
@@ -2326,9 +2326,9 @@ static int __cancel_fail(struct live_preempt_cancel *arg)
 	force_reset_timeout(engine);
 
 	/* force preempt reset [failure] */
-	while (!engine->execlists.pending[0])
+	while (!to_execlists(engine)->pending[0])
 		intel_engine_flush_scheduler(engine);
-	del_timer_sync(&engine->execlists.preempt);
+	del_timer_sync(&to_execlists(engine)->preempt);
 	intel_engine_flush_scheduler(engine);
 
 	engine->props.preempt_timeout_ms = 0;
@@ -2447,7 +2447,7 @@ static int live_suppress_self_preempt(void *arg)
 			goto err_wedged;
 
 		st_engine_heartbeat_disable(engine);
-		engine->execlists.preempt_hang.count = 0;
+		to_execlists(engine)->preempt_hang.count = 0;
 
 		rq_a = spinner_create_request(&a.spin,
 					      a.ctx, engine,
@@ -2466,7 +2466,7 @@ static int live_suppress_self_preempt(void *arg)
 		}
 
 		/* Keep postponing the timer to avoid premature slicing */
-		mod_timer(&engine->execlists.timer, jiffies + HZ);
+		mod_timer(&to_execlists(engine)->timer, jiffies + HZ);
 		for (depth = 0; depth < 8; depth++) {
 			rq_b = spinner_create_request(&b.spin,
 						      b.ctx, engine,
@@ -2493,10 +2493,10 @@ static int live_suppress_self_preempt(void *arg)
 		}
 		igt_spinner_end(&a.spin);
 
-		if (engine->execlists.preempt_hang.count) {
+		if (to_execlists(engine)->preempt_hang.count) {
 			pr_err("Preemption on %s recorded x%d, depth %d; should have been suppressed!\n",
 			       engine->name,
-			       engine->execlists.preempt_hang.count,
+			       to_execlists(engine)->preempt_hang.count,
 			       depth);
 			st_engine_heartbeat_enable(engine);
 			err = -EINVAL;
@@ -3403,7 +3403,7 @@ static int live_preempt_timeout(void *arg)
 		}
 
 		/* Flush the previous CS ack before changing timeouts */
-		while (READ_ONCE(engine->execlists.pending[0]))
+		while (READ_ONCE(to_execlists(engine)->pending[0]))
 			cpu_relax();
 
 		saved_timeout = engine->props.preempt_timeout_ms;
diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c b/drivers/gpu/drm/i915/gt/selftest_lrc.c
index 391a14cc135f..fa7e7bf3cf09 100644
--- a/drivers/gpu/drm/i915/gt/selftest_lrc.c
+++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c
@@ -60,7 +60,7 @@ static int wait_for_submit(struct intel_engine_cs *engine,
 
 		/* Wait until the HW has acknowleged the submission (or err) */
 		intel_engine_flush_scheduler(engine);
-		if (!READ_ONCE(engine->execlists.pending[0]) && is_active(rq))
+		if (is_active(rq))
 			return 0;
 
 		if (done)
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
index ac3c20a51b1a..c1ede6e8af12 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
@@ -132,126 +132,17 @@ static void flush_ggtt_writes(struct i915_vma *vma)
 					     GUC_STATUS);
 }
 
-static void guc_submit(struct intel_engine_cs *engine,
-		       struct i915_request **out,
-		       struct i915_request **end)
-{
-	struct intel_guc *guc = &engine->gt->uc.guc;
-
-	do {
-		struct i915_request *rq = *out++;
-
-		flush_ggtt_writes(rq->ring->vma);
-		guc_add_request(guc, rq);
-	} while (out != end);
-}
-
-static inline int rq_prio(const struct i915_request *rq)
-{
-	return rq->sched.attr.priority;
-}
-
-static struct i915_request *
-schedule_in(struct intel_engine_cs *engine, struct i915_request *rq, int idx)
-{
-	trace_i915_request_in(rq, idx);
-
-	/*
-	 * Currently we are not tracking the rq->context being inflight
-	 * (ce->inflight = rq->engine). It is only used by the execlists
-	 * backend at the moment, a similar counting strategy would be
-	 * required if we generalise the inflight tracking.
-	 */
-
-	__intel_gt_pm_get(engine->gt);
-	return i915_request_get(rq);
-}
-
-static void
-schedule_out(struct intel_engine_cs *engine, struct i915_request *rq)
-{
-	trace_i915_request_out(rq);
-
-	i915_request_put(rq);
-	intel_gt_pm_put_async(engine->gt);
-}
-
-static void __guc_dequeue(struct intel_engine_cs *engine)
-{
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct i915_sched *se = intel_engine_get_scheduler(engine);
-	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 *rq, *rn;
-	struct i915_request **port;
-	struct i915_priolist *pl;
-	bool submit = false;
-
-	lockdep_assert_held(&se->lock);
-
-	if (last) {
-		if (*++first)
-			return;
-
-		last = NULL;
-	}
-
-	/*
-	 * We write directly into the execlists->inflight queue and don't use
-	 * the execlists->pending queue, as we don't have a distinct switch
-	 * event.
-	 */
-	port = first;
-	i915_sched_dequeue(se, pl, rq, rn) {
-		if (last && rq->context != last->context) {
-			if (port == last_port)
-				goto done;
-
-			*port = schedule_in(engine, last,
-					    port - execlists->inflight);
-			port++;
-		}
-
-		list_del_init(&rq->sched.link);
-		__i915_request_submit(rq, engine);
-		submit = true;
-		last = rq;
-	}
-done:
-	if (submit) {
-		*port = schedule_in(engine, last, port - execlists->inflight);
-		*++port = NULL;
-		guc_submit(engine, first, port);
-	}
-	execlists->active = execlists->inflight;
-}
-
 static void guc_submission_tasklet(struct tasklet_struct *t)
 {
-	struct i915_sched * const se = from_tasklet(se, t, tasklet);
-	struct intel_engine_cs *engine = se->priv;
-	struct intel_engine_execlists * const execlists = &engine->execlists;
-	struct i915_request **port, *rq;
-	unsigned long flags;
+	struct i915_sched *se = from_tasklet(se, t, tasklet);
+	struct i915_request *rq, *rn;
+	struct i915_priolist *pl;
 
-	spin_lock_irqsave(&se->lock, flags);
-
-	for (port = execlists->inflight; (rq = *port); port++) {
-		if (!i915_request_completed(rq))
-			break;
-
-		schedule_out(engine, rq);
+	i915_sched_dequeue(se, pl, rq, rn) {
+		__i915_request_submit(rq, rq->context->engine);
+		flush_ggtt_writes(rq->context->state);
+		guc_add_request(se->priv, rq);
 	}
-	if (port != execlists->inflight) {
-		int idx = port - execlists->inflight;
-		int rem = ARRAY_SIZE(execlists->inflight) - idx;
-		memmove(execlists->inflight, port, rem * sizeof(*port));
-	}
-
-	__guc_dequeue(engine);
-
-	spin_unlock_irqrestore(&se->lock, flags);
 }
 
 static void cs_irq_handler(struct intel_engine_cs *engine, u16 iir)
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 66c81f1b3bec..3122e46f0713 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -464,23 +464,6 @@ static void error_print_instdone(struct drm_i915_error_state_buf *m,
 		   ee->instdone.slice_common_extra[1]);
 }
 
-static void error_print_request(struct drm_i915_error_state_buf *m,
-				const char *prefix,
-				const struct i915_request_coredump *erq)
-{
-	if (!erq->seqno)
-		return;
-
-	err_printf(m, "%s pid %d, seqno %8x:%08x%s%s, prio %d, head %08x, tail %08x\n",
-		   prefix, erq->pid, erq->context, erq->seqno,
-		   test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
-			    &erq->flags) ? "!" : "",
-		   test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
-			    &erq->flags) ? "+" : "",
-		   erq->sched_attr.priority,
-		   erq->head, erq->tail);
-}
-
 static void error_print_context(struct drm_i915_error_state_buf *m,
 				const char *header,
 				const struct i915_gem_context_coredump *ctx)
@@ -513,7 +496,6 @@ static void error_print_engine(struct drm_i915_error_state_buf *m,
 			       const struct intel_engine_coredump *ee)
 {
 	struct i915_vma_coredump *batch;
-	int n;
 
 	err_printf(m, "%s command stream:\n", ee->engine->name);
 	err_printf(m, "  CCID:  0x%08x\n", ee->ccid);
@@ -572,11 +554,6 @@ static void error_print_engine(struct drm_i915_error_state_buf *m,
 	err_printf(m, "  Scheduler: %s\n",
 		   i915_sched_repr_mode(intel_engine_get_scheduler(ee->engine)));
 
-	for (n = 0; n < ee->num_ports; n++) {
-		err_printf(m, "  ELSP[%d]:", n);
-		error_print_request(m, " ", &ee->execlist[n]);
-	}
-
 	error_print_context(m, "  Active context: ", &ee->context);
 }
 
@@ -1222,42 +1199,6 @@ static void engine_record_registers(struct intel_engine_coredump *ee)
 	}
 }
 
-static void record_request(const struct i915_request *request,
-			   struct i915_request_coredump *erq)
-{
-	erq->flags = request->fence.flags;
-	erq->context = request->fence.context;
-	erq->seqno = request->fence.seqno;
-	erq->sched_attr = request->sched.attr;
-	erq->head = request->head;
-	erq->tail = request->tail;
-
-	erq->pid = 0;
-	rcu_read_lock();
-	if (!intel_context_is_closed(request->context)) {
-		const struct i915_gem_context *ctx;
-
-		ctx = rcu_dereference(request->context->gem_context);
-		if (ctx)
-			erq->pid = I915_SELFTEST_ONLY(!ctx->client) ?
-				   0 :
-				   pid_nr(i915_drm_client_pid(ctx->client));
-	}
-	rcu_read_unlock();
-}
-
-static void engine_record_execlists(struct intel_engine_coredump *ee)
-{
-	const struct intel_engine_execlists * const el = &ee->engine->execlists;
-	struct i915_request * const *port = el->active;
-	unsigned int n = 0;
-
-	while (*port)
-		record_request(*port++, &ee->execlist[n++]);
-
-	ee->num_ports = n;
-}
-
 static bool record_context(struct i915_gem_context_coredump *e,
 			   const struct i915_request *rq)
 {
@@ -1357,7 +1298,6 @@ intel_engine_coredump_alloc(struct intel_engine_cs *engine, gfp_t gfp)
 	ee->engine = engine;
 
 	engine_record_registers(ee);
-	engine_record_execlists(ee);
 
 	return ee;
 }
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.h b/drivers/gpu/drm/i915/i915_gpu_error.h
index 2d8debabfe28..c31955407d18 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.h
+++ b/drivers/gpu/drm/i915/i915_gpu_error.h
@@ -101,9 +101,6 @@ struct intel_engine_coredump {
 
 	struct i915_vma_coredump *vma;
 
-	struct i915_request_coredump execlist[EXECLIST_MAX_PORTS];
-	unsigned int num_ports;
-
 	struct {
 		u32 gfx_mode;
 		union {
-- 
2.20.1



More information about the Intel-gfx-trybot mailing list