]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/gpu/drm/i915/i915_irq.c
drm/i915: Separate out reset flags from the reset counter
[mirror_ubuntu-artful-kernel.git] / drivers / gpu / drm / i915 / i915_irq.c
index 1c2aec39241257fe63183a770053a135b68e2f61..ed172d7beecb757ea0b9b4462fa540123c7529f0 100644 (file)
@@ -656,12 +656,6 @@ static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv)
  *   of horizontal active on the first line of vertical active
  */
 
-static u32 i8xx_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
-{
-       /* Gen2 doesn't have a hardware frame counter */
-       return 0;
-}
-
 /* Called from drm generic code, passed a 'crtc', which
  * we use as a pipe index
  */
@@ -978,10 +972,8 @@ static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
 static void notify_ring(struct intel_engine_cs *engine)
 {
        smp_store_mb(engine->breadcrumbs.irq_posted, true);
-       if (intel_engine_wakeup(engine)) {
+       if (intel_engine_wakeup(engine))
                trace_i915_gem_request_notify(engine);
-               engine->breadcrumbs.irq_wakeups++;
-       }
 }
 
 static void vlv_c0_read(struct drm_i915_private *dev_priv,
@@ -1105,9 +1097,10 @@ static void gen6_pm_rps_work(struct work_struct *work)
        new_delay = dev_priv->rps.cur_freq;
        min = dev_priv->rps.min_freq_softlimit;
        max = dev_priv->rps.max_freq_softlimit;
-
-       if (client_boost) {
-               new_delay = dev_priv->rps.max_freq_softlimit;
+       if (client_boost || any_waiters(dev_priv))
+               max = dev_priv->rps.max_freq;
+       if (client_boost && new_delay < dev_priv->rps.boost_freq) {
+               new_delay = dev_priv->rps.boost_freq;
                adj = 0;
        } else if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
                if (adj > 0)
@@ -1122,7 +1115,7 @@ static void gen6_pm_rps_work(struct work_struct *work)
                        new_delay = dev_priv->rps.efficient_freq;
                        adj = 0;
                }
-       } else if (any_waiters(dev_priv)) {
+       } else if (client_boost || any_waiters(dev_priv)) {
                adj = 0;
        } else if (pm_iir & GEN6_PM_RP_DOWN_TIMEOUT) {
                if (dev_priv->rps.cur_freq > dev_priv->rps.efficient_freq)
@@ -2508,53 +2501,41 @@ static void i915_reset_and_wakeup(struct drm_i915_private *dev_priv)
 
        kobject_uevent_env(kobj, KOBJ_CHANGE, error_event);
 
+       DRM_DEBUG_DRIVER("resetting chip\n");
+       kobject_uevent_env(kobj, KOBJ_CHANGE, reset_event);
+
        /*
-        * Note that there's only one work item which does gpu resets, so we
-        * need not worry about concurrent gpu resets potentially incrementing
-        * error->reset_counter twice. We only need to take care of another
-        * racing irq/hangcheck declaring the gpu dead for a second time. A
-        * quick check for that is good enough: schedule_work ensures the
-        * correct ordering between hang detection and this work item, and since
-        * the reset in-progress bit is only ever set by code outside of this
-        * work we don't need to worry about any other races.
+        * In most cases it's guaranteed that we get here with an RPM
+        * reference held, for example because there is a pending GPU
+        * request that won't finish until the reset is done. This
+        * isn't the case at least when we get here by doing a
+        * simulated reset via debugs, so get an RPM reference.
         */
-       if (i915_reset_in_progress(&dev_priv->gpu_error)) {
-               DRM_DEBUG_DRIVER("resetting chip\n");
-               kobject_uevent_env(kobj, KOBJ_CHANGE, reset_event);
+       intel_runtime_pm_get(dev_priv);
 
-               /*
-                * In most cases it's guaranteed that we get here with an RPM
-                * reference held, for example because there is a pending GPU
-                * request that won't finish until the reset is done. This
-                * isn't the case at least when we get here by doing a
-                * simulated reset via debugs, so get an RPM reference.
-                */
-               intel_runtime_pm_get(dev_priv);
+       intel_prepare_reset(dev_priv);
 
-               intel_prepare_reset(dev_priv);
-
-               /*
-                * All state reset _must_ be completed before we update the
-                * reset counter, for otherwise waiters might miss the reset
-                * pending state and not properly drop locks, resulting in
-                * deadlocks with the reset work.
-                */
-               ret = i915_reset(dev_priv);
+       /*
+        * All state reset _must_ be completed before we update the
+        * reset counter, for otherwise waiters might miss the reset
+        * pending state and not properly drop locks, resulting in
+        * deadlocks with the reset work.
+        */
+       ret = i915_reset(dev_priv);
 
-               intel_finish_reset(dev_priv);
+       intel_finish_reset(dev_priv);
 
-               intel_runtime_pm_put(dev_priv);
+       intel_runtime_pm_put(dev_priv);
 
-               if (ret == 0)
-                       kobject_uevent_env(kobj,
-                                          KOBJ_CHANGE, reset_done_event);
+       if (ret == 0)
+               kobject_uevent_env(kobj,
+                                  KOBJ_CHANGE, reset_done_event);
 
-               /*
-                * Note: The wake_up also serves as a memory barrier so that
-                * waiters see the update value of the reset counter atomic_t.
-                */
-               wake_up_all(&dev_priv->gpu_error.reset_queue);
-       }
+       /*
+        * Note: The wake_up also serves as a memory barrier so that
+        * waiters see the updated value of the dev_priv->gpu_error.
+        */
+       wake_up_all(&dev_priv->gpu_error.reset_queue);
 }
 
 static void i915_report_and_clear_eir(struct drm_i915_private *dev_priv)
@@ -2673,25 +2654,26 @@ void i915_handle_error(struct drm_i915_private *dev_priv,
        i915_capture_error_state(dev_priv, engine_mask, error_msg);
        i915_report_and_clear_eir(dev_priv);
 
-       if (engine_mask) {
-               atomic_or(I915_RESET_IN_PROGRESS_FLAG,
-                               &dev_priv->gpu_error.reset_counter);
+       if (!engine_mask)
+               return;
 
-               /*
-                * Wakeup waiting processes so that the reset function
-                * i915_reset_and_wakeup doesn't deadlock trying to grab
-                * various locks. By bumping the reset counter first, the woken
-                * processes will see a reset in progress and back off,
-                * releasing their locks and then wait for the reset completion.
-                * We must do this for _all_ gpu waiters that might hold locks
-                * that the reset work needs to acquire.
-                *
-                * Note: The wake_up serves as the required memory barrier to
-                * ensure that the waiters see the updated value of the reset
-                * counter atomic_t.
-                */
-               i915_error_wake_up(dev_priv);
-       }
+       if (test_and_set_bit(I915_RESET_IN_PROGRESS,
+                            &dev_priv->gpu_error.flags))
+               return;
+
+       /*
+        * Wakeup waiting processes so that the reset function
+        * i915_reset_and_wakeup doesn't deadlock trying to grab
+        * various locks. By bumping the reset counter first, the woken
+        * processes will see a reset in progress and back off,
+        * releasing their locks and then wait for the reset completion.
+        * We must do this for _all_ gpu waiters that might hold locks
+        * that the reset work needs to acquire.
+        *
+        * Note: The wake_up also provides a memory barrier to ensure that the
+        * waiters see the updated value of the reset flags.
+        */
+       i915_error_wake_up(dev_priv);
 
        i915_reset_and_wakeup(dev_priv);
 }
@@ -2803,13 +2785,6 @@ static void gen8_disable_vblank(struct drm_device *dev, unsigned int pipe)
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
 
-static bool
-ring_idle(struct intel_engine_cs *engine, u32 seqno)
-{
-       return i915_seqno_passed(seqno,
-                                READ_ONCE(engine->last_submitted_seqno));
-}
-
 static bool
 ipehr_is_semaphore_wait(struct intel_engine_cs *engine, u32 ipehr)
 {
@@ -2859,6 +2834,7 @@ static struct intel_engine_cs *
 semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno)
 {
        struct drm_i915_private *dev_priv = engine->i915;
+       void __iomem *vaddr;
        u32 cmd, ipehr, head;
        u64 offset = 0;
        int i, backwards;
@@ -2897,6 +2873,7 @@ semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno)
         */
        head = I915_READ_HEAD(engine) & HEAD_ADDR;
        backwards = (INTEL_GEN(dev_priv) >= 8) ? 5 : 4;
+       vaddr = (void __iomem *)engine->buffer->vaddr;
 
        for (i = backwards; i; --i) {
                /*
@@ -2907,7 +2884,7 @@ semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno)
                head &= engine->buffer->size - 1;
 
                /* This here seems to blow up */
-               cmd = ioread32(engine->buffer->virtual_start + head);
+               cmd = ioread32(vaddr + head);
                if (cmd == ipehr)
                        break;
 
@@ -2917,11 +2894,11 @@ semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno)
        if (!i)
                return NULL;
 
-       *seqno = ioread32(engine->buffer->virtual_start + head + 4) + 1;
+       *seqno = ioread32(vaddr + head + 4) + 1;
        if (INTEL_GEN(dev_priv) >= 8) {
-               offset = ioread32(engine->buffer->virtual_start + head + 12);
+               offset = ioread32(vaddr + head + 12);
                offset <<= 32;
-               offset = ioread32(engine->buffer->virtual_start + head + 8);
+               offset |= ioread32(vaddr + head + 8);
        }
        return semaphore_wait_to_signaller_ring(engine, ipehr, offset);
 }
@@ -2990,7 +2967,7 @@ static bool subunits_stuck(struct intel_engine_cs *engine)
        return stuck;
 }
 
-static enum intel_ring_hangcheck_action
+static enum intel_engine_hangcheck_action
 head_stuck(struct intel_engine_cs *engine, u64 acthd)
 {
        if (acthd != engine->hangcheck.acthd) {
@@ -3008,11 +2985,11 @@ head_stuck(struct intel_engine_cs *engine, u64 acthd)
        return HANGCHECK_HUNG;
 }
 
-static enum intel_ring_hangcheck_action
-ring_stuck(struct intel_engine_cs *engine, u64 acthd)
+static enum intel_engine_hangcheck_action
+engine_stuck(struct intel_engine_cs *engine, u64 acthd)
 {
        struct drm_i915_private *dev_priv = engine->i915;
-       enum intel_ring_hangcheck_action ha;
+       enum intel_engine_hangcheck_action ha;
        u32 tmp;
 
        ha = head_stuck(engine, acthd);
@@ -3054,22 +3031,6 @@ ring_stuck(struct intel_engine_cs *engine, u64 acthd)
        return HANGCHECK_HUNG;
 }
 
-static unsigned long kick_waiters(struct intel_engine_cs *engine)
-{
-       struct drm_i915_private *i915 = engine->i915;
-       unsigned long irq_count = READ_ONCE(engine->breadcrumbs.irq_wakeups);
-
-       if (engine->hangcheck.user_interrupts == irq_count &&
-           !test_and_set_bit(engine->id, &i915->gpu_error.missed_irq_rings)) {
-               if (!test_bit(engine->id, &i915->gpu_error.test_irq_rings))
-                       DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
-                                 engine->name);
-
-               intel_engine_enable_fake_irq(engine);
-       }
-
-       return irq_count;
-}
 /*
  * This is called when the chip hasn't reported back with completed
  * batchbuffers in a long time. We keep track per ring seqno progress and
@@ -3107,7 +3068,7 @@ static void i915_hangcheck_elapsed(struct work_struct *work)
                bool busy = intel_engine_has_waiter(engine);
                u64 acthd;
                u32 seqno;
-               unsigned user_interrupts;
+               u32 submit;
 
                semaphore_clear_deadlocks(dev_priv);
 
@@ -3121,29 +3082,26 @@ static void i915_hangcheck_elapsed(struct work_struct *work)
                if (engine->irq_seqno_barrier)
                        engine->irq_seqno_barrier(engine);
 
-               acthd = intel_ring_get_active_head(engine);
+               acthd = intel_engine_get_active_head(engine);
                seqno = intel_engine_get_seqno(engine);
-
-               /* Reset stuck interrupts between batch advances */
-               user_interrupts = 0;
+               submit = READ_ONCE(engine->last_submitted_seqno);
 
                if (engine->hangcheck.seqno == seqno) {
-                       if (ring_idle(engine, seqno)) {
+                       if (i915_seqno_passed(seqno, submit)) {
                                engine->hangcheck.action = HANGCHECK_IDLE;
                                if (busy) {
                                        /* Safeguard against driver failure */
-                                       user_interrupts = kick_waiters(engine);
                                        engine->hangcheck.score += BUSY;
                                }
                        } else {
                                /* We always increment the hangcheck score
-                                * if the ring is busy and still processing
+                                * if the engine is busy and still processing
                                 * the same request, so that no single request
                                 * can run indefinitely (such as a chain of
                                 * batches). The only time we do not increment
                                 * the hangcheck score on this ring, if this
-                                * ring is in a legitimate wait for another
-                                * ring. In that case the waiting ring is a
+                                * engine is in a legitimate wait for another
+                                * engine. In that case the waiting engine is a
                                 * victim and we want to be sure we catch the
                                 * right culprit. Then every time we do kick
                                 * the ring, add a small increment to the
@@ -3151,8 +3109,8 @@ static void i915_hangcheck_elapsed(struct work_struct *work)
                                 * being repeatedly kicked and so responsible
                                 * for stalling the machine.
                                 */
-                               engine->hangcheck.action = ring_stuck(engine,
-                                                                     acthd);
+                               engine->hangcheck.action =
+                                       engine_stuck(engine, acthd);
 
                                switch (engine->hangcheck.action) {
                                case HANGCHECK_IDLE:
@@ -3195,12 +3153,12 @@ static void i915_hangcheck_elapsed(struct work_struct *work)
 
                engine->hangcheck.seqno = seqno;
                engine->hangcheck.acthd = acthd;
-               engine->hangcheck.user_interrupts = user_interrupts;
                busy_count += busy;
        }
 
        if (hung) {
                char msg[80];
+               unsigned int tmp;
                int len;
 
                /* If some rings hung but others were still busy, only
@@ -3210,7 +3168,7 @@ static void i915_hangcheck_elapsed(struct work_struct *work)
                        hung &= ~stuck;
                len = scnprintf(msg, sizeof(msg),
                                "%s on ", stuck == hung ? "No progress" : "Hang");
-               for_each_engine_masked(engine, dev_priv, hung)
+               for_each_engine_masked(engine, dev_priv, hung, tmp)
                        len += scnprintf(msg + len, sizeof(msg) - len,
                                         "%s, ", engine->name);
                msg[len-2] = '\0';
@@ -4542,8 +4500,9 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
                          i915_hangcheck_elapsed);
 
        if (IS_GEN2(dev_priv)) {
+               /* Gen2 doesn't have a hardware frame counter */
                dev->max_vblank_count = 0;
-               dev->driver->get_vblank_counter = i8xx_get_vblank_counter;
+               dev->driver->get_vblank_counter = drm_vblank_no_hw_counter;
        } else if (IS_G4X(dev_priv) || INTEL_INFO(dev_priv)->gen >= 5) {
                dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
                dev->driver->get_vblank_counter = g4x_get_vblank_counter;