]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/gpu/drm/i915/i915_irq.c
drm/i915: Break modeset deadlocks on reset
[linux.git] / drivers / gpu / drm / i915 / i915_irq.c
index fd97fe00cd0d2ad00e1c7258eeb51ecf0f60d4c1..e4934d5adc9e956232d7424b15a5f770dc97769d 100644 (file)
@@ -720,9 +720,7 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
        struct drm_i915_private *dev_priv = to_i915(dev);
        i915_reg_t high_frame, low_frame;
        u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
-       struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
-                                                               pipe);
-       const struct drm_display_mode *mode = &intel_crtc->base.hwmode;
+       const struct drm_display_mode *mode = &dev->vblank[pipe].hwmode;
        unsigned long irqflags;
 
        htotal = mode->crtc_htotal;
@@ -779,13 +777,17 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
 {
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
-       const struct drm_display_mode *mode = &crtc->base.hwmode;
+       const struct drm_display_mode *mode;
+       struct drm_vblank_crtc *vblank;
        enum pipe pipe = crtc->pipe;
        int position, vtotal;
 
        if (!crtc->active)
                return -1;
 
+       vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
+       mode = &vblank->hwmode;
+
        vtotal = mode->crtc_vtotal;
        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
                vtotal /= 2;
@@ -827,10 +829,10 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
        return (position + crtc->scanline_offset) % vtotal;
 }
 
-static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
-                                   unsigned int flags, int *vpos, int *hpos,
-                                   ktime_t *stime, ktime_t *etime,
-                                   const struct drm_display_mode *mode)
+static bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
+                                    bool in_vblank_irq, int *vpos, int *hpos,
+                                    ktime_t *stime, ktime_t *etime,
+                                    const struct drm_display_mode *mode)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
@@ -838,13 +840,12 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
        int position;
        int vbl_start, vbl_end, hsync_start, htotal, vtotal;
        bool in_vbl = true;
-       int ret = 0;
        unsigned long irqflags;
 
        if (WARN_ON(!mode->crtc_clock)) {
                DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
                                 "pipe %c\n", pipe_name(pipe));
-               return 0;
+               return false;
        }
 
        htotal = mode->crtc_htotal;
@@ -859,8 +860,6 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
                vtotal /= 2;
        }
 
-       ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
-
        /*
         * Lock uncore.lock, as we will do multiple timing critical raw
         * register reads, potentially with preemption disabled, so the
@@ -944,11 +943,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
                *hpos = position - (*vpos * htotal);
        }
 
-       /* In vblank? */
-       if (in_vbl)
-               ret |= DRM_SCANOUTPOS_IN_VBLANK;
-
-       return ret;
+       return true;
 }
 
 int intel_get_crtc_scanline(struct intel_crtc *crtc)
@@ -964,37 +959,6 @@ int intel_get_crtc_scanline(struct intel_crtc *crtc)
        return position;
 }
 
-static int i915_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
-                             int *max_error,
-                             struct timeval *vblank_time,
-                             unsigned flags)
-{
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_crtc *crtc;
-
-       if (pipe >= INTEL_INFO(dev_priv)->num_pipes) {
-               DRM_ERROR("Invalid crtc %u\n", pipe);
-               return -EINVAL;
-       }
-
-       /* Get drm_crtc to timestamp: */
-       crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
-       if (crtc == NULL) {
-               DRM_ERROR("Invalid crtc %u\n", pipe);
-               return -EINVAL;
-       }
-
-       if (!crtc->base.hwmode.crtc_clock) {
-               DRM_DEBUG_KMS("crtc %u is disabled\n", pipe);
-               return -EBUSY;
-       }
-
-       /* Helper routine in DRM core does all the work: */
-       return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
-                                                    vblank_time, flags,
-                                                    &crtc->base.hwmode);
-}
-
 static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
 {
        u32 busy_up, busy_down, max_avg, min_avg;
@@ -1236,7 +1200,7 @@ static void gen6_pm_rps_work(struct work_struct *work)
 static void ivybridge_parity_work(struct work_struct *work)
 {
        struct drm_i915_private *dev_priv =
-               container_of(work, struct drm_i915_private, l3_parity.error_work);
+               container_of(work, typeof(*dev_priv), l3_parity.error_work);
        u32 error_status, row, bank, subbank;
        char *parity_event[6];
        uint32_t misccpctl;
@@ -1353,14 +1317,16 @@ static void snb_gt_irq_handler(struct drm_i915_private *dev_priv,
                ivybridge_parity_error_irq_handler(dev_priv, gt_iir);
 }
 
-static __always_inline void
+static void
 gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift)
 {
        bool tasklet = false;
 
        if (iir & (GT_CONTEXT_SWITCH_INTERRUPT << test_shift)) {
-               set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
-               tasklet = true;
+               if (port_count(&engine->execlist_port[0])) {
+                       __set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
+                       tasklet = true;
+               }
        }
 
        if (iir & (GT_RENDER_USER_INTERRUPT << test_shift)) {
@@ -2582,7 +2548,8 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl)
                        I915_WRITE(SDEIIR, iir);
                        ret = IRQ_HANDLED;
 
-                       if (HAS_PCH_SPT(dev_priv) || HAS_PCH_KBP(dev_priv))
+                       if (HAS_PCH_SPT(dev_priv) || HAS_PCH_KBP(dev_priv) ||
+                           HAS_PCH_CNP(dev_priv))
                                spt_irq_handler(dev_priv, iir);
                        else
                                cpt_irq_handler(dev_priv, iir);
@@ -2632,60 +2599,93 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
        return ret;
 }
 
+struct wedge_me {
+       struct delayed_work work;
+       struct drm_i915_private *i915;
+       const char *name;
+};
+
+static void wedge_me(struct work_struct *work)
+{
+       struct wedge_me *w = container_of(work, typeof(*w), work.work);
+
+       dev_err(w->i915->drm.dev,
+               "%s timed out, cancelling all in-flight rendering.\n",
+               w->name);
+       i915_gem_set_wedged(w->i915);
+}
+
+static void __init_wedge(struct wedge_me *w,
+                        struct drm_i915_private *i915,
+                        long timeout,
+                        const char *name)
+{
+       w->i915 = i915;
+       w->name = name;
+
+       INIT_DELAYED_WORK_ONSTACK(&w->work, wedge_me);
+       schedule_delayed_work(&w->work, timeout);
+}
+
+static void __fini_wedge(struct wedge_me *w)
+{
+       cancel_delayed_work_sync(&w->work);
+       destroy_delayed_work_on_stack(&w->work);
+       w->i915 = NULL;
+}
+
+#define i915_wedge_on_timeout(W, DEV, TIMEOUT)                         \
+       for (__init_wedge((W), (DEV), (TIMEOUT), __func__);             \
+            (W)->i915;                                                 \
+            __fini_wedge((W)))
+
 /**
- * i915_reset_and_wakeup - do process context error handling work
+ * i915_reset_device - do process context error handling work
  * @dev_priv: i915 device private
  *
  * Fire an error uevent so userspace can see that a hang or error
  * was detected.
  */
-static void i915_reset_and_wakeup(struct drm_i915_private *dev_priv)
+static void i915_reset_device(struct drm_i915_private *dev_priv)
 {
        struct kobject *kobj = &dev_priv->drm.primary->kdev->kobj;
        char *error_event[] = { I915_ERROR_UEVENT "=1", NULL };
        char *reset_event[] = { I915_RESET_UEVENT "=1", NULL };
        char *reset_done_event[] = { I915_ERROR_UEVENT "=0", NULL };
+       struct wedge_me w;
 
        kobject_uevent_env(kobj, KOBJ_CHANGE, error_event);
 
        DRM_DEBUG_DRIVER("resetting chip\n");
        kobject_uevent_env(kobj, KOBJ_CHANGE, reset_event);
 
-       intel_prepare_reset(dev_priv);
+       /* Use a watchdog to ensure that our reset completes */
+       i915_wedge_on_timeout(&w, dev_priv, 5*HZ) {
+               intel_prepare_reset(dev_priv);
 
-       set_bit(I915_RESET_HANDOFF, &dev_priv->gpu_error.flags);
-       wake_up_all(&dev_priv->gpu_error.wait_queue);
+               /* Signal that locked waiters should reset the GPU */
+               set_bit(I915_RESET_HANDOFF, &dev_priv->gpu_error.flags);
+               wake_up_all(&dev_priv->gpu_error.wait_queue);
 
-       do {
-               /*
-                * 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.
+               /* Wait for anyone holding the lock to wakeup, without
+                * blocking indefinitely on struct_mutex.
                 */
-               if (mutex_trylock(&dev_priv->drm.struct_mutex)) {
-                       i915_reset(dev_priv);
-                       mutex_unlock(&dev_priv->drm.struct_mutex);
-               }
-
-               /* We need to wait for anyone holding the lock to wakeup */
-       } while (wait_on_bit_timeout(&dev_priv->gpu_error.flags,
-                                    I915_RESET_HANDOFF,
-                                    TASK_UNINTERRUPTIBLE,
-                                    HZ));
+               do {
+                       if (mutex_trylock(&dev_priv->drm.struct_mutex)) {
+                               i915_reset(dev_priv);
+                               mutex_unlock(&dev_priv->drm.struct_mutex);
+                       }
+               } while (wait_on_bit_timeout(&dev_priv->gpu_error.flags,
+                                            I915_RESET_HANDOFF,
+                                            TASK_UNINTERRUPTIBLE,
+                                            1));
 
-       intel_finish_reset(dev_priv);
+               intel_finish_reset(dev_priv);
+       }
 
        if (!test_bit(I915_WEDGED, &dev_priv->gpu_error.flags))
                kobject_uevent_env(kobj,
                                   KOBJ_CHANGE, reset_done_event);
-
-       /*
-        * Note: The wake_up also serves as a memory barrier so that
-        * waiters see the updated value of the dev_priv->gpu_error.
-        */
-       clear_bit(I915_RESET_BACKOFF, &dev_priv->gpu_error.flags);
-       wake_up_all(&dev_priv->gpu_error.reset_queue);
 }
 
 static inline void
@@ -2755,6 +2755,8 @@ void i915_handle_error(struct drm_i915_private *dev_priv,
                       u32 engine_mask,
                       const char *fmt, ...)
 {
+       struct intel_engine_cs *engine;
+       unsigned int tmp;
        va_list args;
        char error_msg[80];
 
@@ -2774,14 +2776,56 @@ void i915_handle_error(struct drm_i915_private *dev_priv,
        i915_capture_error_state(dev_priv, engine_mask, error_msg);
        i915_clear_error_registers(dev_priv);
 
+       /*
+        * Try engine reset when available. We fall back to full reset if
+        * single reset fails.
+        */
+       if (intel_has_reset_engine(dev_priv)) {
+               for_each_engine_masked(engine, dev_priv, engine_mask, tmp) {
+                       BUILD_BUG_ON(I915_RESET_HANDOFF >= I915_RESET_ENGINE);
+                       if (test_and_set_bit(I915_RESET_ENGINE + engine->id,
+                                            &dev_priv->gpu_error.flags))
+                               continue;
+
+                       if (i915_reset_engine(engine) == 0)
+                               engine_mask &= ~intel_engine_flag(engine);
+
+                       clear_bit(I915_RESET_ENGINE + engine->id,
+                                 &dev_priv->gpu_error.flags);
+                       wake_up_bit(&dev_priv->gpu_error.flags,
+                                   I915_RESET_ENGINE + engine->id);
+               }
+       }
+
        if (!engine_mask)
                goto out;
 
-       if (test_and_set_bit(I915_RESET_BACKOFF,
-                            &dev_priv->gpu_error.flags))
+       /* Full reset needs the mutex, stop any other user trying to do so. */
+       if (test_and_set_bit(I915_RESET_BACKOFF, &dev_priv->gpu_error.flags)) {
+               wait_event(dev_priv->gpu_error.reset_queue,
+                          !test_bit(I915_RESET_BACKOFF,
+                                    &dev_priv->gpu_error.flags));
                goto out;
+       }
+
+       /* Prevent any other reset-engine attempt. */
+       for_each_engine(engine, dev_priv, tmp) {
+               while (test_and_set_bit(I915_RESET_ENGINE + engine->id,
+                                       &dev_priv->gpu_error.flags))
+                       wait_on_bit(&dev_priv->gpu_error.flags,
+                                   I915_RESET_ENGINE + engine->id,
+                                   TASK_UNINTERRUPTIBLE);
+       }
 
-       i915_reset_and_wakeup(dev_priv);
+       i915_reset_device(dev_priv);
+
+       for_each_engine(engine, dev_priv, tmp) {
+               clear_bit(I915_RESET_ENGINE + engine->id,
+                         &dev_priv->gpu_error.flags);
+       }
+
+       clear_bit(I915_RESET_BACKOFF, &dev_priv->gpu_error.flags);
+       wake_up_all(&dev_priv->gpu_error.reset_queue);
 
 out:
        intel_runtime_pm_put(dev_priv);
@@ -2953,7 +2997,6 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv)
        u32 pipestat_mask;
        u32 enable_mask;
        enum pipe pipe;
-       u32 val;
 
        pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV |
                        PIPE_CRC_DONE_INTERRUPT_STATUS;
@@ -2964,18 +3007,16 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv)
 
        enable_mask = I915_DISPLAY_PORT_INTERRUPT |
                I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
-               I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+               I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
+               I915_LPE_PIPE_A_INTERRUPT |
+               I915_LPE_PIPE_B_INTERRUPT;
+
        if (IS_CHERRYVIEW(dev_priv))
-               enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
+               enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT |
+                       I915_LPE_PIPE_C_INTERRUPT;
 
        WARN_ON(dev_priv->irq_mask != ~0);
 
-       val = (I915_LPE_PIPE_A_INTERRUPT |
-               I915_LPE_PIPE_B_INTERRUPT |
-               I915_LPE_PIPE_C_INTERRUPT);
-
-       enable_mask |= val;
-
        dev_priv->irq_mask = ~enable_mask;
 
        GEN5_IRQ_INIT(VLV_, dev_priv->irq_mask, enable_mask);
@@ -4233,11 +4274,15 @@ static void i965_irq_uninstall(struct drm_device * dev)
 void intel_irq_init(struct drm_i915_private *dev_priv)
 {
        struct drm_device *dev = &dev_priv->drm;
+       int i;
 
        intel_hpd_init_work(dev_priv);
 
        INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
+
        INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work);
+       for (i = 0; i < MAX_L3_SLICES; ++i)
+               dev_priv->l3_parity.remap_info[i] = NULL;
 
        if (HAS_GUC_SCHED(dev_priv))
                dev_priv->pm_guc_events = GEN9_GUC_TO_HOST_INT_EVENT;
@@ -4294,7 +4339,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
 
        dev_priv->hotplug.hpd_storm_threshold = HPD_STORM_DEFAULT_THRESHOLD;
 
-       dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
+       dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
        dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
 
        if (IS_CHERRYVIEW(dev_priv)) {
@@ -4322,7 +4367,8 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
                dev->driver->disable_vblank = gen8_disable_vblank;
                if (IS_GEN9_LP(dev_priv))
                        dev_priv->display.hpd_irq_setup = bxt_hpd_irq_setup;
-               else if (HAS_PCH_SPT(dev_priv) || HAS_PCH_KBP(dev_priv))
+               else if (HAS_PCH_SPT(dev_priv) || HAS_PCH_KBP(dev_priv) ||
+                        HAS_PCH_CNP(dev_priv))
                        dev_priv->display.hpd_irq_setup = spt_hpd_irq_setup;
                else
                        dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup;
@@ -4362,6 +4408,20 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
        }
 }
 
+/**
+ * intel_irq_fini - deinitializes IRQ support
+ * @i915: i915 device instance
+ *
+ * This function deinitializes all the IRQ support.
+ */
+void intel_irq_fini(struct drm_i915_private *i915)
+{
+       int i;
+
+       for (i = 0; i < MAX_L3_SLICES; ++i)
+               kfree(i915->l3_parity.remap_info[i]);
+}
+
 /**
  * intel_irq_install - enables the hardware interrupt
  * @dev_priv: i915 device instance