summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/i915_gem.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/i915_gem.c')
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c62
1 files changed, 60 insertions, 2 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index a866a9d0c8d9..983aafecce7d 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2568,6 +2568,14 @@ i915_gem_init_seqno(struct drm_device *dev, u32 seqno)
engine->last_irq_seqno = 0;
}
+ /* Also reset sw batch tracking state */
+ for_each_engine(engine, dev_priv, i) {
+ intel_write_status_page(engine, I915_BATCH_DONE_SEQNO, 0);
+ intel_write_status_page(engine, I915_BATCH_ACTIVE_SEQNO, 0);
+ intel_write_status_page(engine, I915_PREEMPTIVE_DONE_SEQNO, 0);
+ intel_write_status_page(engine, I915_PREEMPTIVE_ACTIVE_SEQNO, 0);
+ }
+
return 0;
}
@@ -2926,7 +2934,10 @@ void i915_gem_request_notify(struct intel_engine_cs *engine, bool fence_locked)
*/
seqno = engine->get_seqno(engine, false);
trace_i915_gem_request_notify(engine, seqno);
- if (seqno == engine->last_irq_seqno)
+
+ /* Is there anything new to process? */
+ if ((seqno == engine->last_irq_seqno) &&
+ !i915_scheduler_is_engine_preempting(engine))
return;
if (!fence_locked)
@@ -2955,7 +2966,7 @@ void i915_gem_request_notify(struct intel_engine_cs *engine, bool fence_locked)
* and call scheduler_clean() while the scheduler
* thinks it is still active.
*/
- if (i915_scheduler_notify_request(req))
+ if (i915_scheduler_notify_request(req, false))
wake_sched = true;
if (!req->cancelled) {
@@ -2972,6 +2983,53 @@ void i915_gem_request_notify(struct intel_engine_cs *engine, bool fence_locked)
list_add_tail(&req->unsignal_link, &engine->fence_unsignal_list);
}
+ if (i915_scheduler_is_engine_preempting(engine)) {
+ u32 preempt_start, preempt_done;
+
+ preempt_start = intel_read_status_page(engine,
+ I915_PREEMPTIVE_ACTIVE_SEQNO);
+ preempt_done = intel_read_status_page(engine,
+ I915_PREEMPTIVE_DONE_SEQNO);
+
+ /*
+ * A preemption request leaves both ACTIVE and DONE set to the
+ * same seqno. If we find ACTIVE set but DONE is different,
+ * the preemption has started but not yet completed, so leave
+ * it until next time. After successfully processing a
+ * preemption request, we clear ACTIVE below to ensure we
+ * don't see it again.
+ */
+ if (preempt_start && preempt_done == preempt_start) {
+ bool sched_ack = false;
+
+ list_for_each_entry_safe(req, req_next, &engine->fence_signal_list, signal_link) {
+ if (req->seqno == preempt_done) {
+ /*
+ * De-list and notify the scheduler,
+ * but don't signal yet
+ */
+ list_del_init(&req->signal_link);
+ sched_ack = i915_scheduler_notify_request(req, true);
+ break;
+ }
+ }
+
+ WARN_ON(!sched_ack);
+ wake_sched = true;
+
+ /*
+ * Capture BATCH ACTIVE to determine whether a batch
+ * was in progress when preempted
+ */
+ engine->last_batch_start = intel_read_status_page(engine,
+ I915_BATCH_ACTIVE_SEQNO);
+
+ /* Acknowledge/clear preemption-active flag */
+ intel_write_status_page(req->engine,
+ I915_PREEMPTIVE_ACTIVE_SEQNO, 0);
+ }
+ }
+
if (!fence_locked)
spin_unlock_irqrestore(&engine->fence_lock, flags);