[Intel-gfx] [PATCH] drm/i915: Change context lifecycle

Daniel Vetter daniel at ffwll.ch
Thu Nov 26 02:08:33 PST 2015


On Thu, Nov 26, 2015 at 09:19:44AM +0000, Nick Hoath wrote:
> On 26/11/2015 08:48, Daniel Vetter wrote:
> >On Wed, Nov 25, 2015 at 05:02:44PM +0200, Mika Kuoppala wrote:
> >>Nick Hoath <nicholas.hoath at intel.com> writes:
> >>
> >>>Use the first retired request on a new context to unpin
> >>>the old context. This ensures that the hw context remains
> >>>bound until it has been written back to by the GPU.
> >>>Now that the context is pinned until later in the request/context
> >>>lifecycle, it no longer needs to be pinned from context_queue to
> >>>retire_requests.
> >>>
> >>>v2: Moved the new pin to cover GuC submission (Alex Dai)
> >>>     Moved the new unpin to request_retire to fix coverage leak
> >>>v3: Added switch to default context if freeing a still pinned
> >>>     context just in case the hw was actually still using it
> >>>v4: Unwrapped context unpin to allow calling without a request
> >>>v5: Only create a switch to idle context if the ring doesn't
> >>>     already have a request pending on it (Alex Dai)
> >>>     Rename unsaved to dirty to avoid double negatives (Dave Gordon)
> >>>     Changed _no_req postfix to __ prefix for consistency (Dave Gordon)
> >>>     Split out per engine cleanup from context_free as it
> >>>     was getting unwieldy
> >>>     Corrected locking (Dave Gordon)
> >>>
> >>>Signed-off-by: Nick Hoath <nicholas.hoath at intel.com>
> >>>Issue: VIZ-4277
> >>>Cc: Daniel Vetter <daniel.vetter at ffwll.ch>
> >>>Cc: David Gordon <david.s.gordon at intel.com>
> >>>Cc: Chris Wilson <chris at chris-wilson.co.uk>
> >>>Cc: Alex Dai <yu.dai at intel.com>
> >>>---
> >>>  drivers/gpu/drm/i915/i915_drv.h  |   1 +
> >>>  drivers/gpu/drm/i915/i915_gem.c  |   3 +
> >>>  drivers/gpu/drm/i915/intel_lrc.c | 124 +++++++++++++++++++++++++++++++--------
> >>>  drivers/gpu/drm/i915/intel_lrc.h |   1 +
> >>>  4 files changed, 105 insertions(+), 24 deletions(-)
> >>>
> >>>diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> >>>index d5cf30b..e82717a 100644
> >>>--- a/drivers/gpu/drm/i915/i915_drv.h
> >>>+++ b/drivers/gpu/drm/i915/i915_drv.h
> >>>@@ -889,6 +889,7 @@ struct intel_context {
> >>>  	struct {
> >>>  		struct drm_i915_gem_object *state;
> >>>  		struct intel_ringbuffer *ringbuf;
> >>>+		bool dirty;
> >>>  		int pin_count;
> >>>  	} engine[I915_NUM_RINGS];
> >>>
> >>>diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> >>>index e955499..3829bc1 100644
> >>>--- a/drivers/gpu/drm/i915/i915_gem.c
> >>>+++ b/drivers/gpu/drm/i915/i915_gem.c
> >>>@@ -1354,6 +1354,9 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request)
> >>>  {
> >>>  	trace_i915_gem_request_retire(request);
> >>>
> >>>+	if (i915.enable_execlists)
> >>>+		intel_lr_context_complete_check(request);
> >>>+
> >>>  	/* We know the GPU must have read the request to have
> >>>  	 * sent us the seqno + interrupt, so use the position
> >>>  	 * of tail of the request to update the last known position
> >>>diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
> >>>index 06180dc..03d5bca 100644
> >>>--- a/drivers/gpu/drm/i915/intel_lrc.c
> >>>+++ b/drivers/gpu/drm/i915/intel_lrc.c
> >>>@@ -566,9 +566,6 @@ static int execlists_context_queue(struct drm_i915_gem_request *request)
> >>>  	struct drm_i915_gem_request *cursor;
> >>>  	int num_elements = 0;
> >>>
> >>>-	if (request->ctx != ring->default_context)
> >>>-		intel_lr_context_pin(request);
> >>>-
> >>>  	i915_gem_request_reference(request);
> >>>
> >>>  	spin_lock_irq(&ring->execlist_lock);
> >>>@@ -732,6 +729,13 @@ intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request)
> >>>  	if (intel_ring_stopped(ring))
> >>>  		return;
> >>>
> >>>+	if (request->ctx != ring->default_context) {
> >>>+		if (!request->ctx->engine[ring->id].dirty) {
> >>>+			intel_lr_context_pin(request);
> >>>+			request->ctx->engine[ring->id].dirty = true;
> >>>+		}
> >>>+	}
> >>>+
> >>>  	if (dev_priv->guc.execbuf_client)
> >>>  		i915_guc_submit(dev_priv->guc.execbuf_client, request);
> >>>  	else
> >>>@@ -958,12 +962,6 @@ void intel_execlists_retire_requests(struct intel_engine_cs *ring)
> >>>  	spin_unlock_irq(&ring->execlist_lock);
> >>>
> >>>  	list_for_each_entry_safe(req, tmp, &retired_list, execlist_link) {
> >>>-		struct intel_context *ctx = req->ctx;
> >>>-		struct drm_i915_gem_object *ctx_obj =
> >>>-				ctx->engine[ring->id].state;
> >>>-
> >>>-		if (ctx_obj && (ctx != ring->default_context))
> >>>-			intel_lr_context_unpin(req);
> >>>  		list_del(&req->execlist_link);
> >>>  		i915_gem_request_unreference(req);
> >>>  	}
> >>>@@ -1058,21 +1056,39 @@ reset_pin_count:
> >>>  	return ret;
> >>>  }
> >>>
> >>>-void intel_lr_context_unpin(struct drm_i915_gem_request *rq)
> >>>+static void __intel_lr_context_unpin(struct intel_engine_cs *ring,
> >>>+		struct intel_context *ctx)
> >>>  {
> >>>-	struct intel_engine_cs *ring = rq->ring;
> >>>-	struct drm_i915_gem_object *ctx_obj = rq->ctx->engine[ring->id].state;
> >>>-	struct intel_ringbuffer *ringbuf = rq->ringbuf;
> >>>-
> >>>+	struct drm_i915_gem_object *ctx_obj = ctx->engine[ring->id].state;
> >>>+	struct intel_ringbuffer *ringbuf = ctx->engine[ring->id].ringbuf;
> >>>  	if (ctx_obj) {
> >>>  		WARN_ON(!mutex_is_locked(&ring->dev->struct_mutex));
> >>>-		if (--rq->ctx->engine[ring->id].pin_count == 0) {
> >>>+		if (--ctx->engine[ring->id].pin_count == 0) {
> >>>  			intel_unpin_ringbuffer_obj(ringbuf);
> >>>  			i915_gem_object_ggtt_unpin(ctx_obj);
> >>>  		}
> >>>  	}
> >>>  }
> >>>
> >>>+void intel_lr_context_unpin(struct drm_i915_gem_request *rq)
> >>>+{
> >>>+	__intel_lr_context_unpin(rq->ring, rq->ctx);
> >>>+}
> >>>+
> >>>+void intel_lr_context_complete_check(struct drm_i915_gem_request *req)
> >>>+{
> >>>+	struct intel_engine_cs *ring = req->ring;
> >>>+
> >>>+	if (ring->last_context && ring->last_context != req->ctx &&
> >>>+			ring->last_context->engine[ring->id].dirty) {
> >>>+		__intel_lr_context_unpin(
> >>>+				ring,
> >>>+				ring->last_context);
> >>>+		ring->last_context->engine[ring->id].dirty = false;
> >>>+	}
> >>>+	ring->last_context = req->ctx;
> >>
> >>What ensures that the last_context stays alive?
> >
> >The other part that's missing compared to the legacy context cycling logic
> >is move_to_active. Without that the shrinker can't eat into retired
> >contexts, which renders this exercise fairly moot imo.
> >
> >The overall idea is that since i915 does dynamic memory management, and
> >that infects everything in the code that deals with gpu objects, we need
> >to make sure that everyone is using the same logicy to cycle through
> >active objects. Otherwise the shrinker will be an unmaintainable hydra
> >with per-feature special cases.
> >-Daniel
> 
> This is the first part of the VIZ-4277 fix changeset that I'm working on. It
> has been 'fasttracked' as it is needed to fix a lockup with GuC submission.
> If you like, I can update the commit message to explain that?

Yeah if this is a minimal bugfix for some issue then this definitely
should be mentioned in the commit message, including a precise description
of what and how exactly the current code blows up.
-Daniel

> 
> >
> >>
> >>>+}
> >>>+
> >>>  static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req)
> >>>  {
> >>>  	int ret, i;
> >>>@@ -2367,6 +2383,62 @@ populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_o
> >>>  }
> >>>
> >>>  /**
> >>>+ * intel_lr_context_clean_ring() - clean the ring specific parts of an LRC
> >>>+ * @ctx: the LR context being freed.
> >>>+ * @ring: the engine being cleaned
> >>>+ * @ctx_obj: the hw context being unreferenced
> >>>+ * @ringbuf: the ringbuf being freed
> >>>+ *
> >>>+ * Take care of cleaning up the per-engine backing
> >>>+ * objects and the logical ringbuffer.
> >>>+ */
> >>>+static void
> >>>+intel_lr_context_clean_ring(struct intel_context *ctx,
> >>>+			    struct intel_engine_cs *ring,
> >>>+			    struct drm_i915_gem_object *ctx_obj,
> >>>+			    struct intel_ringbuffer *ringbuf)
> >>>+{
> >>>+	WARN_ON(!mutex_is_locked(&ring->dev->struct_mutex));
> >>>+
> >>>+	if (ctx == ring->default_context) {
> >>>+		intel_unpin_ringbuffer_obj(ringbuf);
> >>>+		i915_gem_object_ggtt_unpin(ctx_obj);
> >>>+	}
> >>>+
> >>>+	if (ctx->engine[ring->id].dirty) {
> >>>+		struct drm_i915_gem_request *req = NULL;
> >>>+
> >>>+		/**
> >>>+		 * If there is already a request pending on
> >>>+		 * this ring, wait for that to complete,
> >>>+		 * otherwise create a switch to idle request
> >>>+		 */
> >>>+		if (list_empty(&ring->request_list)) {
> >>>+			int ret;
> >>>+
> >>>+			ret = i915_gem_request_alloc(
> >>>+					ring,
> >>>+					ring->default_context,
> >>>+					&req);
> >>>+			if (!ret)
> >>>+				i915_add_request(req);
> >>>+			else
> >>>+				DRM_DEBUG("Failed to ensure context saved");
> >>>+		} else {
> >>>+			req = list_first_entry(
> >>>+					&ring->request_list,
> >>>+					typeof(*req), list);
> >>>+		}
> >>>+		if (req)
> >>>+			i915_wait_request(req);
> >>>+	}
> >>>+
> >>>+	WARN_ON(ctx->engine[ring->id].pin_count);
> >>>+	intel_ringbuffer_free(ringbuf);
> >>>+	drm_gem_object_unreference(&ctx_obj->base);
> >>>+}
> >>>+
> >>>+/**
> >>>   * intel_lr_context_free() - free the LRC specific bits of a context
> >>>   * @ctx: the LR context to free.
> >>>   *
> >>>@@ -2378,21 +2450,18 @@ void intel_lr_context_free(struct intel_context *ctx)
> >>>  {
> >>>  	int i;
> >>>
> >>>-	for (i = 0; i < I915_NUM_RINGS; i++) {
> >>>+	for (i = 0; i < I915_NUM_RINGS; ++i) {
> >>>  		struct drm_i915_gem_object *ctx_obj = ctx->engine[i].state;
> >>>
> >>>-		if (ctx_obj) {
> >>>+		if (ctx->engine[i].state) {
> >>
> >>++i and the above are superflouous?
> >>
> >>-Mika
> >>
> >>>  			struct intel_ringbuffer *ringbuf =
> >>>  					ctx->engine[i].ringbuf;
> >>>  			struct intel_engine_cs *ring = ringbuf->ring;
> >>>
> >>>-			if (ctx == ring->default_context) {
> >>>-				intel_unpin_ringbuffer_obj(ringbuf);
> >>>-				i915_gem_object_ggtt_unpin(ctx_obj);
> >>>-			}
> >>>-			WARN_ON(ctx->engine[ring->id].pin_count);
> >>>-			intel_ringbuffer_free(ringbuf);
> >>>-			drm_gem_object_unreference(&ctx_obj->base);
> >>>+			intel_lr_context_clean_ring(ctx,
> >>>+						    ring,
> >>>+						    ctx_obj,
> >>>+						    ringbuf);
> >>>  		}
> >>>  	}
> >>>  }
> >>>@@ -2554,5 +2623,12 @@ void intel_lr_context_reset(struct drm_device *dev,
> >>>
> >>>  		ringbuf->head = 0;
> >>>  		ringbuf->tail = 0;
> >>>+
> >>>+		if (ctx->engine[ring->id].dirty) {
> >>>+			__intel_lr_context_unpin(
> >>>+					ring,
> >>>+					ctx);
> >>>+			ctx->engine[ring->id].dirty = false;
> >>>+		}
> >>>  	}
> >>>  }
> >>>diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
> >>>index 4e60d54..cbc42b8 100644
> >>>--- a/drivers/gpu/drm/i915/intel_lrc.h
> >>>+++ b/drivers/gpu/drm/i915/intel_lrc.h
> >>>@@ -86,6 +86,7 @@ void intel_lr_context_reset(struct drm_device *dev,
> >>>  			struct intel_context *ctx);
> >>>  uint64_t intel_lr_context_descriptor(struct intel_context *ctx,
> >>>  				     struct intel_engine_cs *ring);
> >>>+void intel_lr_context_complete_check(struct drm_i915_gem_request *req);
> >>>
> >>>  /* Execlists */
> >>>  int intel_sanitize_enable_execlists(struct drm_device *dev, int enable_execlists);
> >>>--
> >>>1.9.1
> >>>
> >>>_______________________________________________
> >>>Intel-gfx mailing list
> >>>Intel-gfx at lists.freedesktop.org
> >>>http://lists.freedesktop.org/mailman/listinfo/intel-gfx
> >>_______________________________________________
> >>Intel-gfx mailing list
> >>Intel-gfx at lists.freedesktop.org
> >>http://lists.freedesktop.org/mailman/listinfo/intel-gfx
> >
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


More information about the Intel-gfx mailing list