In order to avoid preempting ourselves, we currently refuse to schedule
the tasklet if we reschedule an inflight context. However, this glosses
over a few issues such as what happens after a CS completion event and
we then preempt the newly executing context with itself, or if something
else causes a tasklet_schedule triggering the same evaluation to
preempt the active context with itself.

To avoid the extra complications, after deciding that we have
potentially queued a request with higher priority than the currently
executing request, inspect the head of the queue to see if it is indeed
higher priority from another context.

v2: We can simplify a bunch of tests based on the knowledge that PI will
ensure that earlier requests along the same context will have the highest
priority.

References: a2bf92e8cc16 ("drm/i915/execlists: Avoid kicking priority on the 
current context")
Signed-off-by: Chris Wilson <ch...@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursu...@intel.com>
---
 drivers/gpu/drm/i915/i915_scheduler.c | 20 ++++--
 drivers/gpu/drm/i915/intel_lrc.c      | 91 ++++++++++++++++++++++++---
 2 files changed, 100 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_scheduler.c 
b/drivers/gpu/drm/i915/i915_scheduler.c
index 0da718ceab43..7db1255665a8 100644
--- a/drivers/gpu/drm/i915/i915_scheduler.c
+++ b/drivers/gpu/drm/i915/i915_scheduler.c
@@ -238,6 +238,18 @@ sched_lock_engine(struct i915_sched_node *node, struct 
intel_engine_cs *locked)
        return engine;
 }
 
+static bool inflight(const struct i915_request *rq,
+                    const struct intel_engine_cs *engine)
+{
+       const struct i915_request *active;
+
+       if (!rq->global_seqno)
+               return false;
+
+       active = port_request(engine->execlists.port);
+       return active->hw_context == rq->hw_context;
+}
+
 static void __i915_schedule(struct i915_request *rq,
                            const struct i915_sched_attr *attr)
 {
@@ -327,6 +339,7 @@ static void __i915_schedule(struct i915_request *rq,
                INIT_LIST_HEAD(&dep->dfs_link);
 
                engine = sched_lock_engine(node, engine);
+               lockdep_assert_held(&engine->timeline.lock);
 
                /* Recheck after acquiring the engine->timeline.lock */
                if (prio <= node->attr.priority || node_signaled(node))
@@ -355,17 +368,16 @@ static void __i915_schedule(struct i915_request *rq,
                if (prio <= engine->execlists.preempt_priority_hint)
                        continue;
 
+               engine->execlists.preempt_priority_hint = prio;
+
                /*
                 * If we are already the currently executing context, don't
                 * bother evaluating if we should preempt ourselves.
                 */
-               if (node_to_request(node)->global_seqno &&
-                   
i915_seqno_passed(port_request(engine->execlists.port)->global_seqno,
-                                     node_to_request(node)->global_seqno))
+               if (inflight(node_to_request(node), engine))
                        continue;
 
                /* Defer (tasklet) submission until after all of our updates. */
-               engine->execlists.preempt_priority_hint = prio;
                tasklet_hi_schedule(&engine->execlists.tasklet);
        }
 
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 71006b031f54..b44db7d49584 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -182,13 +182,89 @@ static inline int rq_prio(const struct i915_request *rq)
        return rq->sched.attr.priority;
 }
 
+static int queue_prio(const struct intel_engine_execlists *execlists)
+{
+       struct i915_priolist *p;
+       struct rb_node *rb;
+
+       rb = rb_first_cached(&execlists->queue);
+       if (!rb)
+               return INT_MIN;
+
+       /*
+        * As the priolist[] are inverted, with the highest priority in [0],
+        * we have to flip the index value to become priority.
+        */
+       p = to_priolist(rb);
+       return ((p->priority + 1) << I915_USER_PRIORITY_SHIFT) - ffs(p->used);
+}
+
 static inline bool need_preempt(const struct intel_engine_cs *engine,
-                               const struct i915_request *last,
-                               int prio)
+                               const struct i915_request *rq,
+                               int hint)
 {
-       return (intel_engine_has_preemption(engine) &&
-               __execlists_need_preempt(prio, rq_prio(last)) &&
-               !i915_request_completed(last));
+       const struct intel_context *ctx = rq->hw_context;
+       const int last_prio = rq_prio(rq);
+
+       if (!intel_engine_has_preemption(engine))
+               return false;
+
+       if (i915_request_completed(rq))
+               return false;
+
+       /*
+        * Check if the current priority hint merits a preemption attempt.
+        *
+        * However, the priority hint is a mere hint that we may need to
+        * preempt. If that hint is stale or we may be trying to preempt
+        * ourselves, ignore the request.
+        */
+       if (!__execlists_need_preempt(hint, last_prio))
+               return false;
+
+       /*
+        * Check against the first request in ELSP[1], it will, thanks to the
+        * power of PI, be the highest priority of that context.
+        */
+       if (!list_is_last(&rq->link, &engine->timeline.requests)) {
+               rq = list_next_entry(rq, link);
+               GEM_BUG_ON(rq->hw_context == ctx);
+               if (rq_prio(rq) > last_prio)
+                       return true;
+       }
+
+       /*
+        * If the inflight context did not trigger the preemption, then maybe
+        * it was the set of queued requests? Pick the highest priority in
+        * the queue (the first active priolist) and see if it deserves to be
+        * running instead of ELSP[0].
+        *
+        * The highest priority request in the queue can not be either
+        * 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.
+        */
+       return queue_prio(&engine->execlists) > last_prio;
+}
+
+__maybe_unused static inline bool
+assert_priority_queue(const struct intel_engine_execlists *execlists,
+                     const struct i915_request *prev,
+                     const struct i915_request *next)
+{
+       if (!prev)
+               return true;
+
+       /*
+        * Without preemption, the prev may refer to the still active element
+        * which we refuse to let go.
+        *
+        * Even with preemption, there are times when we think it is better not
+        * to preempt and leave an ostensibly lower priority request in flight.
+        */
+       if (port_request(execlists->port) == prev)
+               return true;
+
+       return rq_prio(prev) >= rq_prio(next);
 }
 
 /*
@@ -630,8 +706,7 @@ static void execlists_dequeue(struct intel_engine_cs 
*engine)
                int i;
 
                priolist_for_each_request_consume(rq, rn, p, i) {
-                       GEM_BUG_ON(last &&
-                                  need_preempt(engine, last, rq_prio(rq)));
+                       GEM_BUG_ON(!assert_priority_queue(execlists, last, rq));
 
                        /*
                         * Can we combine this request with the current port?
@@ -876,6 +951,8 @@ static void process_csb(struct intel_engine_cs *engine)
        const u32 * const buf = execlists->csb_status;
        u8 head, tail;
 
+       lockdep_assert_held(&engine->timeline.lock);
+
        /*
         * Note that csb_write, csb_status may be either in HWSP or mmio.
         * When reading from the csb_write mmio register, we have to be
-- 
2.20.1

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

Reply via email to