drm/i915: Fix hangcheck to handle multiple rings
authorChris Wilson <chris@chris-wilson.co.uk>
Wed, 27 Oct 2010 13:44:35 +0000 (14:44 +0100)
committerChris Wilson <chris@chris-wilson.co.uk>
Wed, 27 Oct 2010 22:29:36 +0000 (23:29 +0100)
Currently, we believe the GPU is idle if just the RENDER ring is idle.
This is obviously wrong if we only using either the BLT or the BSD
rings and so masking genuine hangs.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
drivers/gpu/drm/i915/i915_irq.c

index 8acdd6d857d368304e9c9cc9637d798582bc7a16..23b28528c642e5ccad299e7f381e000541dd704c 100644 (file)
@@ -1307,12 +1307,29 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
        return -EINVAL;
 }
 
-static struct drm_i915_gem_request *
-i915_get_tail_request(struct drm_device *dev)
+static u32
+ring_last_seqno(struct intel_ring_buffer *ring)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       return list_entry(dev_priv->render_ring.request_list.prev,
-                       struct drm_i915_gem_request, list);
+       return list_entry(ring->request_list.prev,
+                         struct drm_i915_gem_request, list)->seqno;
+}
+
+static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err)
+{
+       if (list_empty(&ring->request_list) ||
+           i915_seqno_passed(ring->get_seqno(ring), ring_last_seqno(ring))) {
+               /* Issue a wake-up to catch stuck h/w. */
+               if (ring->waiting_gem_seqno && waitqueue_active(&ring->irq_queue)) {
+                       DRM_ERROR("Hangcheck timer elapsed... %s idle [waiting on %d, at %d], missed IRQ?\n",
+                                 ring->name,
+                                 ring->waiting_gem_seqno,
+                                 ring->get_seqno(ring));
+                       wake_up_all(&ring->irq_queue);
+                       *err = true;
+               }
+               return true;
+       }
+       return false;
 }
 
 /**
@@ -1326,6 +1343,17 @@ void i915_hangcheck_elapsed(unsigned long data)
        struct drm_device *dev = (struct drm_device *)data;
        drm_i915_private_t *dev_priv = dev->dev_private;
        uint32_t acthd, instdone, instdone1;
+       bool err = false;
+
+       /* If all work is done then ACTHD clearly hasn't advanced. */
+       if (i915_hangcheck_ring_idle(&dev_priv->render_ring, &err) &&
+           i915_hangcheck_ring_idle(&dev_priv->bsd_ring, &err) &&
+           i915_hangcheck_ring_idle(&dev_priv->blt_ring, &err)) {
+               dev_priv->hangcheck_count = 0;
+               if (err)
+                       goto repeat;
+               return;
+       }
 
        if (INTEL_INFO(dev)->gen < 4) {
                acthd = I915_READ(ACTHD);
@@ -1337,38 +1365,6 @@ void i915_hangcheck_elapsed(unsigned long data)
                instdone1 = I915_READ(INSTDONE1);
        }
 
-       /* If all work is done then ACTHD clearly hasn't advanced. */
-       if (list_empty(&dev_priv->render_ring.request_list) ||
-               i915_seqno_passed(dev_priv->render_ring.get_seqno(&dev_priv->render_ring),
-                                 i915_get_tail_request(dev)->seqno)) {
-               bool missed_wakeup = false;
-
-               dev_priv->hangcheck_count = 0;
-
-               /* Issue a wake-up to catch stuck h/w. */
-               if (dev_priv->render_ring.waiting_gem_seqno &&
-                   waitqueue_active(&dev_priv->render_ring.irq_queue)) {
-                       wake_up_all(&dev_priv->render_ring.irq_queue);
-                       missed_wakeup = true;
-               }
-
-               if (dev_priv->bsd_ring.waiting_gem_seqno &&
-                   waitqueue_active(&dev_priv->bsd_ring.irq_queue)) {
-                       wake_up_all(&dev_priv->bsd_ring.irq_queue);
-                       missed_wakeup = true;
-               }
-
-               if (dev_priv->blt_ring.waiting_gem_seqno &&
-                   waitqueue_active(&dev_priv->blt_ring.irq_queue)) {
-                       wake_up_all(&dev_priv->blt_ring.irq_queue);
-                       missed_wakeup = true;
-               }
-
-               if (missed_wakeup)
-                       DRM_ERROR("Hangcheck timer elapsed... GPU idle, missed IRQ.\n");
-               return;
-       }
-
        if (dev_priv->last_acthd == acthd &&
            dev_priv->last_instdone == instdone &&
            dev_priv->last_instdone1 == instdone1) {
@@ -1385,7 +1381,7 @@ void i915_hangcheck_elapsed(unsigned long data)
                                if (tmp & RING_WAIT) {
                                        I915_WRITE(PRB0_CTL, tmp);
                                        POSTING_READ(PRB0_CTL);
-                                       goto out;
+                                       goto repeat;
                                }
                        }
 
@@ -1400,7 +1396,7 @@ void i915_hangcheck_elapsed(unsigned long data)
                dev_priv->last_instdone1 = instdone1;
        }
 
-out:
+repeat:
        /* Reset timer case chip hangs without another request being added */
        mod_timer(&dev_priv->hangcheck_timer,
                  jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));