[Intel-gfx] [RFC 35/38] drm/i915/preempt: Implement mid-batch preemption support
John.C.Harrison at Intel.com
John.C.Harrison at Intel.com
Fri Dec 11 06:50:43 PST 2015
From: Dave Gordon <david.s.gordon at intel.com>
Batch buffers which have been pre-emption mid-way through execution
must be handled seperately. Rather than simply re-submitting the batch
as a brand new piece of work, the driver only needs to requeue the
context. The hardware will take care of picking up where it left off.
v2: New patch in series.
For: VIZ-2021
Signed-off-by: Dave Gordon <david.s.gordon at intel.com>
---
drivers/gpu/drm/i915/i915_debugfs.c | 1 +
drivers/gpu/drm/i915/i915_scheduler.c | 55 +++++++++++++++++++++++++++++++----
drivers/gpu/drm/i915/i915_scheduler.h | 3 ++
drivers/gpu/drm/i915/intel_lrc.c | 51 ++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_lrc.h | 1 +
5 files changed, 105 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 7137439..6798f9c 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -3722,6 +3722,7 @@ static int i915_scheduler_info(struct seq_file *m, void *unused)
PRINT_VAR(" Queued", "u", stats[r].queued);
PRINT_VAR(" Submitted", "u", stats[r].submitted);
PRINT_VAR(" Preempted", "u", stats[r].preempted);
+ PRINT_VAR(" Midbatch preempted", "u", stats[r].mid_preempted);
PRINT_VAR(" Completed", "u", stats[r].completed);
PRINT_VAR(" Expired", "u", stats[r].expired);
seq_putc(m, '\n');
diff --git a/drivers/gpu/drm/i915/i915_scheduler.c b/drivers/gpu/drm/i915/i915_scheduler.c
index d0c4b46..d96eefb 100644
--- a/drivers/gpu/drm/i915/i915_scheduler.c
+++ b/drivers/gpu/drm/i915/i915_scheduler.c
@@ -743,6 +743,7 @@ i915_scheduler_preemption_postprocess(struct intel_engine_cs *ring)
struct i915_scheduler *scheduler = dev_priv->scheduler;
struct i915_scheduler_queue_entry *pnode = NULL;
struct drm_i915_gem_request *preq = NULL;
+ struct drm_i915_gem_request *midp = NULL;
struct i915_scheduler_stats *stats;
unsigned long flags;
int preempted = 0, preemptive = 0;
@@ -806,8 +807,12 @@ i915_scheduler_preemption_postprocess(struct intel_engine_cs *ring)
node->status = i915_sqs_preempted;
trace_i915_scheduler_unfly(ring, node);
trace_i915_scheduler_node_state_change(ring, node);
- /* Empty the preempted ringbuffer */
- intel_lr_context_resync(req->ctx, ring, false);
+
+ /* Identify a mid-batch preemption */
+ if (req->seqno == ring->last_batch_start) {
+ WARN(midp, "Multiple mid-batch-preempted requests?\n");
+ midp = req;
+ }
}
i915_gem_request_dequeue(req);
@@ -821,11 +826,47 @@ i915_scheduler_preemption_postprocess(struct intel_engine_cs *ring)
if (stats->max_preempted < preempted)
stats->max_preempted = preempted;
+ /* Now fix up the contexts of all preempt{ive,ed} requests */
{
- /* XXX: Sky should be empty now */
+ struct intel_context *mid_ctx = NULL;
struct i915_scheduler_queue_entry *node;
- list_for_each_entry(node, &scheduler->node_queue[ring->id], link)
- WARN_ON(I915_SQS_IS_FLYING(node));
+ u32 started = ring->last_batch_start;
+
+ /*
+ * Iff preemption was mid-batch, we should have found a
+ * mid-batch-preempted request
+ */
+ if (started && started != ring->last_irq_seqno)
+ WARN(!midp, "Mid-batch preempted, but request not found\n");
+ else
+ WARN(midp, "Found unexpected mid-batch preemption?\n");
+
+ if (midp) {
+ /* Rewrite this context rather than emptying it */
+ intel_lr_context_resync_req(midp);
+ midp->scheduler_flags |= i915_req_sf_restart;
+ mid_ctx = midp->ctx;
+ stats->mid_preempted += 1;
+ WARN_ON(preq == midp);
+ }
+
+ list_for_each_entry(node, &scheduler->node_queue[ring->id], link) {
+ /* XXX: Sky should be empty now */
+ if (WARN_ON(I915_SQS_IS_FLYING(node)))
+ continue;
+
+ /* Clean up preempted contexts */
+ if (node->status != i915_sqs_preempted)
+ continue;
+
+ if (node->params.ctx != mid_ctx) {
+ /* Empty the preempted ringbuffer */
+ intel_lr_context_resync(node->params.ctx, ring, false);
+ /* Request is now queued, not preempted */
+ node->status = i915_sqs_queued;
+ trace_i915_scheduler_node_state_change(ring, node);
+ }
+ }
}
/* Anything else to do here ... ? */
@@ -1746,7 +1787,9 @@ static int i915_scheduler_submit(struct intel_engine_cs *ring, bool was_locked)
spin_lock_irqsave(&scheduler->lock, flags);
scheduler->flags[ring->id] &= ~i915_sf_submitting;
- if (ret) {
+ if (ret == 0) {
+ req->scheduler_flags &= ~i915_req_sf_restart;
+ } else {
int requeue = 1;
/* Oh dear! Either the node is broken or the ring is
diff --git a/drivers/gpu/drm/i915/i915_scheduler.h b/drivers/gpu/drm/i915/i915_scheduler.h
index 7e7e974..86ebfdd 100644
--- a/drivers/gpu/drm/i915/i915_scheduler.h
+++ b/drivers/gpu/drm/i915/i915_scheduler.h
@@ -33,6 +33,8 @@ enum {
i915_req_sf_was_preempt = (1 << 1),
/* Request is preemptive */
i915_req_sf_preempt = (1 << 2),
+ /* Request has been preempted midbatch, need to restart */
+ i915_req_sf_restart = (1 << 3),
};
enum i915_scheduler_queue_status {
@@ -108,6 +110,7 @@ struct i915_scheduler_stats {
uint32_t queued;
uint32_t submitted;
uint32_t preempted;
+ uint32_t mid_preempted;
uint32_t completed;
uint32_t expired;
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index cc9f213..36d63b7 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -1012,6 +1012,18 @@ int intel_execlists_submission_final(struct i915_execbuffer_params *params)
if (ret)
goto err;
+ /*
+ * For the case of restarting a mid-batch preempted request,
+ * the ringbuffer already contains all necessary instructions,
+ * so we can just go straight to submitting it
+ */
+ if ((req->scheduler_flags & i915_req_sf_restart)) {
+ DRM_DEBUG_DRIVER("restart: req head/tail 0x%x/%x ringbuf 0x%x/%x\n",
+ req->head, req->tail, ringbuf->head, ringbuf->tail);
+ i915_gem_execbuffer_retire_commands(params);
+ return 0;
+ }
+
/* record where we start filling the ring */
req->head = intel_ring_get_tail(ringbuf);
@@ -2670,6 +2682,45 @@ error_pm:
}
/*
+ * Update the ringbuffer associated with the specified request
+ * so that only the section relating to that request is valid.
+ * Then propagate the change to the associated context image.
+ */
+void intel_lr_context_resync_req(struct drm_i915_gem_request *req)
+{
+ enum intel_ring_id ring_id = req->ring->id;
+ struct drm_i915_gem_object *ctx_obj;
+ struct intel_ringbuffer *ringbuf;
+ struct page *page;
+ uint32_t *reg_state;
+
+ ctx_obj = req->ctx->engine[ring_id].state;
+ ringbuf = req->ringbuf;
+
+ if (WARN_ON(!ringbuf || !ctx_obj))
+ return;
+ if (WARN_ON(i915_gem_object_get_pages(ctx_obj)))
+ return;
+
+ page = i915_gem_object_get_page(ctx_obj, LRC_STATE_PN);
+ reg_state = kmap_atomic(page);
+
+ DRM_DEBUG_DRIVER("Updating ringbuf head/tail, previously 0x%x/%x ...\n",
+ ringbuf->head, ringbuf->tail);
+
+ ringbuf->tail = req->tail;
+ ringbuf->last_retired_head = req->head;
+ intel_ring_update_space(ringbuf);
+
+ DRM_DEBUG_DRIVER("Updated ringbuf, now 0x%x/%x space %d\n",
+ ringbuf->head, ringbuf->tail, ringbuf->space);
+
+ reg_state[CTX_RING_TAIL+1] = ringbuf->tail;
+
+ kunmap_atomic(reg_state);
+}
+
+/*
* Empty the ringbuffer associated with the specified request
* by updating the ringbuffer 'head' to the value of 'tail', or,
* if 'rezero' is true, setting both 'head' and 'tail' to zero.
diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
index fa2f814..653064a 100644
--- a/drivers/gpu/drm/i915/intel_lrc.h
+++ b/drivers/gpu/drm/i915/intel_lrc.h
@@ -83,6 +83,7 @@ uint32_t intel_lr_context_size(struct intel_engine_cs *ring);
int intel_lr_context_deferred_alloc(struct intel_context *ctx,
struct intel_engine_cs *ring);
void intel_lr_context_unpin(struct drm_i915_gem_request *req);
+void intel_lr_context_resync_req(struct drm_i915_gem_request *req);
void intel_lr_context_resync(struct intel_context *ctx,
struct intel_engine_cs *ring,
bool rezero);
--
1.9.1
More information about the Intel-gfx
mailing list