execlists->first = rb;
        if (submit) {
                port_assign(port, last);
-               execlists_set_active(execlists, EXECLISTS_ACTIVE_USER);
+               execlists_user_begin(execlists, execlists->port);
                guc_submit(engine);
        }
 
        struct execlist_port *port = execlists->port;
        struct i915_request *rq;
 
-       rq = port_request(&port[0]);
+       rq = port_request(port);
        while (rq && i915_request_completed(rq)) {
                trace_i915_request_out(rq);
                i915_request_put(rq);
 
-               execlists_port_complete(execlists, port);
-
-               rq = port_request(&port[0]);
+               port = execlists_port_complete(execlists, port);
+               if (port_isset(port)) {
+                       execlists_user_begin(execlists, port);
+                       rq = port_request(port);
+               } else {
+                       execlists_user_end(execlists);
+                       rq = NULL;
+               }
        }
-       if (!rq)
-               execlists_clear_active(execlists, EXECLISTS_ACTIVE_USER);
 
        if (execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT) &&
            intel_read_status_page(engine, I915_GEM_HWS_PREEMPT_INDEX) ==
 
                                   status, rq);
 }
 
+inline void
+execlists_user_begin(struct intel_engine_execlists *execlists,
+                    const struct execlist_port *port)
+{
+       execlists_set_active_once(execlists, EXECLISTS_ACTIVE_USER);
+}
+
+inline void
+execlists_user_end(struct intel_engine_execlists *execlists)
+{
+       execlists_clear_active(execlists, EXECLISTS_ACTIVE_USER);
+}
+
 static inline void
 execlists_context_schedule_in(struct i915_request *rq)
 {
        spin_unlock_irq(&engine->timeline->lock);
 
        if (submit) {
-               execlists_set_active(execlists, EXECLISTS_ACTIVE_USER);
+               execlists_user_begin(execlists, execlists->port);
                execlists_submit_ports(engine);
        }
 
                port++;
        }
 
-       execlists_clear_active(execlists, EXECLISTS_ACTIVE_USER);
+       execlists_user_end(execlists);
 }
 
 static void clear_gtiir(struct intel_engine_cs *engine)
 {
        struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
        struct intel_engine_execlists * const execlists = &engine->execlists;
-       struct execlist_port * const port = execlists->port;
+       struct execlist_port *port = execlists->port;
        struct drm_i915_private *dev_priv = engine->i915;
        bool fw = false;
 
 
                        GEM_BUG_ON(count == 0);
                        if (--count == 0) {
+                               /*
+                                * On the final event corresponding to the
+                                * submission of this context, we expect either
+                                * an element-switch event or a completion
+                                * event (and on completion, the active-idle
+                                * marker). No more preemptions, lite-restore
+                                * or otherwise.
+                                */
                                GEM_BUG_ON(status & GEN8_CTX_STATUS_PREEMPTED);
                                GEM_BUG_ON(port_isset(&port[1]) &&
                                           !(status & GEN8_CTX_STATUS_ELEMENT_SWITCH));
+                               GEM_BUG_ON(!port_isset(&port[1]) &&
+                                          !(status & GEN8_CTX_STATUS_ACTIVE_IDLE));
+
+                               /*
+                                * We rely on the hardware being strongly
+                                * ordered, that the breadcrumb write is
+                                * coherent (visible from the CPU) before the
+                                * user interrupt and CSB is processed.
+                                */
                                GEM_BUG_ON(!i915_request_completed(rq));
+
                                execlists_context_schedule_out(rq);
                                trace_i915_request_out(rq);
                                i915_request_put(rq);
                                GEM_TRACE("%s completed ctx=%d\n",
                                          engine->name, port->context_id);
 
-                               execlists_port_complete(execlists, port);
+                               port = execlists_port_complete(execlists, port);
+                               if (port_isset(port))
+                                       execlists_user_begin(execlists, port);
+                               else
+                                       execlists_user_end(execlists);
                        } else {
                                port_set(port, port_pack(rq, count));
                        }
-
-                       /* After the final element, the hw should be idle */
-                       GEM_BUG_ON(port_count(port) == 0 &&
-                                  !(status & GEN8_CTX_STATUS_ACTIVE_IDLE));
-                       if (port_count(port) == 0)
-                               execlists_clear_active(execlists,
-                                                      EXECLISTS_ACTIVE_USER);
                }
 
                if (head != execlists->csb_head) {
 
        __set_bit(bit, (unsigned long *)&execlists->active);
 }
 
+static inline bool
+execlists_set_active_once(struct intel_engine_execlists *execlists,
+                         unsigned int bit)
+{
+       return !__test_and_set_bit(bit, (unsigned long *)&execlists->active);
+}
+
 static inline void
 execlists_clear_active(struct intel_engine_execlists *execlists,
                       unsigned int bit)
        return test_bit(bit, (unsigned long *)&execlists->active);
 }
 
+void execlists_user_begin(struct intel_engine_execlists *execlists,
+                         const struct execlist_port *port);
+void execlists_user_end(struct intel_engine_execlists *execlists);
+
 void
 execlists_cancel_port_requests(struct intel_engine_execlists * const execlists);
 
        return execlists->port_mask + 1;
 }
 
-static inline void
+static inline struct execlist_port *
 execlists_port_complete(struct intel_engine_execlists * const execlists,
                        struct execlist_port * const port)
 {
 
        memmove(port, port + 1, m * sizeof(struct execlist_port));
        memset(port + m, 0, sizeof(struct execlist_port));
+
+       return port;
 }
 
 static inline unsigned int