drm/i915: Reset request handling for gen8+
authorMika Kuoppala <mika.kuoppala@linux.intel.com>
Thu, 18 Jun 2015 09:51:40 +0000 (12:51 +0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 18 Jun 2015 14:58:37 +0000 (16:58 +0200)
In order for gen8+ hardware to guarantee that no context switch
takes place during engine reset and that current context is properly
saved, the driver needs to notify and query hw before commencing
with reset.

There are gpu hangs where the engine gets so stuck that it never will
report to be ready for reset. We could proceed with reset anyway, but
with some hangs with skl, the forced gpu reset will result in a system
hang. By inspecting the unreadiness for reset seems to correlate with
the probable system hang.

We will only proceed with reset if all engines report that they
are ready for reset. If root cause for system hang is found and
can be worked around with another means, we can reconsider if
we can reinstate full reset for unreadiness case.

v2: -EIO, Recovery, gen8 (Chris, Tomas, Daniel)
v3: updated commit msg
v4: timeout_ms, simpler error path (Chris)

References: https://bugs.freedesktop.org/show_bug.cgi?id=89959
References: https://bugs.freedesktop.org/show_bug.cgi?id=90854
Testcase: igt/gem_concurrent_blit/prw-blt-overwrite-source-read-rcs-forked
Testcase: igt/gem_concurrent_blit/gtt-blt-overwrite-source-read-rcs-forked
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Tomas Elf <tomas.elf@intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Mika Kuoppala <mika.kuoppala@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_uncore.c

index c154a7b732cdca5b2d615788e3a0f80e02810d37..64caa470f2c6c5a0bda60c38cca753fc76704551 100644 (file)
@@ -1461,6 +1461,9 @@ enum skl_disp_power_wells {
 #define RING_MAX_IDLE(base)    ((base)+0x54)
 #define RING_HWS_PGA(base)     ((base)+0x80)
 #define RING_HWS_PGA_GEN6(base)        ((base)+0x2080)
+#define RING_RESET_CTL(base)   ((base)+0xd0)
+#define   RESET_CTL_REQUEST_RESET  (1 << 0)
+#define   RESET_CTL_READY_TO_RESET (1 << 1)
 
 #define HSW_GTT_CACHE_EN       0x4024
 #define   GTT_CACHE_EN_ALL     0xF0007FFF
index 4a86cf007aa0b06dc58a6c69d33f28dee20b5499..160a47a9bdd97944658bdd7ed903b87b7966c573 100644 (file)
@@ -1455,9 +1455,50 @@ static int gen6_do_reset(struct drm_device *dev)
        return ret;
 }
 
+static int wait_for_register(struct drm_i915_private *dev_priv,
+                            const u32 reg,
+                            const u32 mask,
+                            const u32 value,
+                            const unsigned long timeout_ms)
+{
+       return wait_for((I915_READ(reg) & mask) == value, timeout_ms);
+}
+
+static int gen8_do_reset(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_engine_cs *engine;
+       int i;
+
+       for_each_ring(engine, dev_priv, i) {
+               I915_WRITE(RING_RESET_CTL(engine->mmio_base),
+                          _MASKED_BIT_ENABLE(RESET_CTL_REQUEST_RESET));
+
+               if (wait_for_register(dev_priv,
+                                     RING_RESET_CTL(engine->mmio_base),
+                                     RESET_CTL_READY_TO_RESET,
+                                     RESET_CTL_READY_TO_RESET,
+                                     700)) {
+                       DRM_ERROR("%s: reset request timeout\n", engine->name);
+                       goto not_ready;
+               }
+       }
+
+       return gen6_do_reset(dev);
+
+not_ready:
+       for_each_ring(engine, dev_priv, i)
+               I915_WRITE(RING_RESET_CTL(engine->mmio_base),
+                          _MASKED_BIT_DISABLE(RESET_CTL_REQUEST_RESET));
+
+       return -EIO;
+}
+
 static int (*intel_get_gpu_reset(struct drm_device *dev))(struct drm_device *)
 {
-       if (INTEL_INFO(dev)->gen >= 6)
+       if (INTEL_INFO(dev)->gen >= 8)
+               return gen8_do_reset;
+       else if (INTEL_INFO(dev)->gen >= 6)
                return gen6_do_reset;
        else if (IS_GEN5(dev))
                return ironlake_do_reset;