| From 8f2a1df53dda1674ffed043a982e7b5514e7dc50 Mon Sep 17 00:00:00 2001 |
| From: Mika Kuoppala <mika.kuoppala@linux.intel.com> |
| Date: Wed, 12 Jun 2013 15:13:20 +0300 |
| Subject: drm/i915: find guilty batch buffer on ring resets |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| After hang check timer has declared gpu to be hung, |
| rings are reset. In ring reset, when clearing |
| request list, do post mortem analysis to find out |
| the guilty batch buffer. |
| |
| Select requests for further analysis by inspecting |
| the completed sequence number which has been updated |
| into the HWS page. If request was completed, it can't |
| be related to the hang. |
| |
| For noncompleted requests mark the batch as guilty |
| if the ring was not waiting and the ring head was |
| stuck inside the buffer object or in the flush region |
| right after the batch. For everything else, mark |
| them as innocents. |
| |
| v2: Fixed a typo in commit message (Ville Syrjälä) |
| |
| v3: - more descriptive function parameters (Chris Wilson) |
| - use masked head address when inspecting if request is in ring |
| - s/hangcheck.last_action/hangcheck.action |
| - added comment about unmasked head hitting batch_obj range |
| |
| Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> |
| Signed-off-by: Mika Kuoppala <mika.kuoppala@intel.com> |
| Acked-by: Ben Widawsky <ben@bwidawsk.net> |
| Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> |
| (cherry picked from commit aa60c664e6df502578454621c3a9b1f087ff8d25) |
| Signed-off-by: Darren Hart <dvhart@linux.intel.com> |
| --- |
| drivers/gpu/drm/i915/i915_gem.c | 97 +++++++++++++++++++++++++++++++++++++++++ |
| 1 file changed, 97 insertions(+) |
| |
| diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c |
| index 855742a058b3..70af73dff001 100644 |
| --- a/drivers/gpu/drm/i915/i915_gem.c |
| +++ b/drivers/gpu/drm/i915/i915_gem.c |
| @@ -2121,6 +2121,94 @@ i915_gem_request_remove_from_client(struct drm_i915_gem_request *request) |
| spin_unlock(&file_priv->mm.lock); |
| } |
| |
| +static bool i915_head_inside_object(u32 acthd, struct drm_i915_gem_object *obj) |
| +{ |
| + if (acthd >= obj->gtt_offset && |
| + acthd < obj->gtt_offset + obj->base.size) |
| + return true; |
| + |
| + return false; |
| +} |
| + |
| +static bool i915_head_inside_request(const u32 acthd_unmasked, |
| + const u32 request_start, |
| + const u32 request_end) |
| +{ |
| + const u32 acthd = acthd_unmasked & HEAD_ADDR; |
| + |
| + if (request_start < request_end) { |
| + if (acthd >= request_start && acthd < request_end) |
| + return true; |
| + } else if (request_start > request_end) { |
| + if (acthd >= request_start || acthd < request_end) |
| + return true; |
| + } |
| + |
| + return false; |
| +} |
| + |
| +static bool i915_request_guilty(struct drm_i915_gem_request *request, |
| + const u32 acthd, bool *inside) |
| +{ |
| + /* There is a possibility that unmasked head address |
| + * pointing inside the ring, matches the batch_obj address range. |
| + * However this is extremely unlikely. |
| + */ |
| + |
| + if (request->batch_obj) { |
| + if (i915_head_inside_object(acthd, request->batch_obj)) { |
| + *inside = true; |
| + return true; |
| + } |
| + } |
| + |
| + if (i915_head_inside_request(acthd, request->head, request->tail)) { |
| + *inside = false; |
| + return true; |
| + } |
| + |
| + return false; |
| +} |
| + |
| +static void i915_set_reset_status(struct intel_ring_buffer *ring, |
| + struct drm_i915_gem_request *request, |
| + u32 acthd) |
| +{ |
| + struct i915_ctx_hang_stats *hs = NULL; |
| + bool inside, guilty; |
| + |
| + /* Innocent until proven guilty */ |
| + guilty = false; |
| + |
| + if (ring->hangcheck.action != wait && |
| + i915_request_guilty(request, acthd, &inside)) { |
| + DRM_ERROR("%s hung %s bo (0x%x ctx %d) at 0x%x\n", |
| + ring->name, |
| + inside ? "inside" : "flushing", |
| + request->batch_obj ? |
| + request->batch_obj->gtt_offset : 0, |
| + request->ctx ? request->ctx->id : 0, |
| + acthd); |
| + |
| + guilty = true; |
| + } |
| + |
| + /* If contexts are disabled or this is the default context, use |
| + * file_priv->reset_state |
| + */ |
| + if (request->ctx && request->ctx->id != DEFAULT_CONTEXT_ID) |
| + hs = &request->ctx->hang_stats; |
| + else if (request->file_priv) |
| + hs = &request->file_priv->hang_stats; |
| + |
| + if (hs) { |
| + if (guilty) |
| + hs->batch_active++; |
| + else |
| + hs->batch_pending++; |
| + } |
| +} |
| + |
| static void i915_gem_free_request(struct drm_i915_gem_request *request) |
| { |
| list_del(&request->list); |
| @@ -2135,6 +2223,12 @@ static void i915_gem_free_request(struct drm_i915_gem_request *request) |
| static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv, |
| struct intel_ring_buffer *ring) |
| { |
| + u32 completed_seqno; |
| + u32 acthd; |
| + |
| + acthd = intel_ring_get_active_head(ring); |
| + completed_seqno = ring->get_seqno(ring, false); |
| + |
| while (!list_empty(&ring->request_list)) { |
| struct drm_i915_gem_request *request; |
| |
| @@ -2142,6 +2236,9 @@ static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv, |
| struct drm_i915_gem_request, |
| list); |
| |
| + if (request->seqno > completed_seqno) |
| + i915_set_reset_status(ring, request, acthd); |
| + |
| i915_gem_free_request(request); |
| } |
| |
| -- |
| 1.8.5.rc3 |
| |