drm/i915: reorganize the unclaimed register detection code
authorPaulo Zanoni <paulo.r.zanoni@intel.com>
Wed, 16 Jul 2014 20:49:29 +0000 (17:49 -0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Wed, 23 Jul 2014 05:05:36 +0000 (07:05 +0200)
The current code only runs when we do an I915_WRITE operation. It
checks if the unclaimed register flag is set before we do the
operation, and then it checks it again after we do the operation. This
double check allows us to find out if the I915_WRITE operation in
question is the bad one, or if some previous code is the bad one. When
it finds a problem, our code uses DRM_ERROR to signal it.

The good thing about the current code is that it detects the problem,
so at least we can know we did something wrong. The problem is that
even though we find the problem, we don't really have much information
to actually debug it. So whenever I see one of these DRM_ERROR
messages on my systems, the first thing I do is apply a patch to
change the DRM_ERROR to a WARN and also check for unclaimed registers
on I915_READ operations. This local patch makes things even slower,
but it usually helps a lot in finding the bad code.

The first point here is that since the current code is only useful to
detect whether we have a problem or not, but it is not really good to
find the cause of the problem, I don't think we should be checking
both before and after every I915_WRITE operation: just doing the check
once should be enough for us to quickly detect problems. With this
change, the code that runs by default for every single user will only
do 1 read operation for every single I915_WRITE, instead of 2. This
patch does this change.

The second point is that the local patch I have should be upstream,
but since it makes things slower it should be disabled by default. So
I added the i915.mmio_debug option to enable it.

So after this patch, this is what will happen:
 - By default, we will try to detect unclaimed registers once after
   every I915_WRITE operation. Previously we tried twice for every
   I915_WRITE.
 - When we find an unclaimed register we will still print a DRM_ERROR
   message, but we will now tell the user to try again with
   i915.mmio_debug=1.
 - When we use i915.mmio_debug=1 we will try to find unclaimed
   registers both before and after every I915_READ and I915_WRITE
   operation, and we will print stack traces in case we find them.
   This should really help locating the exact point of the bad code
   (or at least finding out that i915.ko is not the problem).

This commit also opens space for really-slow register debugging
operations on other platforms. In theory we can now add lots and lots
of debug code behind i915.mmio_debug, enable this option on our tests,
and catch more problems.

v2: - Remove not-so-useful comments (Daniel)
    - Fix the param definition macros (Rodrigo)

Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com>
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_params.c
drivers/gpu/drm/i915/intel_uncore.c

index 8b781f8ad3a9ee7a471fd47320d54fe48078a0e0..46ee0a90d5587de52405c2dae5b94a0b49eb4801 100644 (file)
@@ -2135,6 +2135,7 @@ struct i915_params {
        bool disable_display;
        bool disable_vtd_wa;
        int use_mmio_flip;
+       bool mmio_debug;
 };
 extern struct i915_params i915 __read_mostly;
 
index bbdee21aec0e6ce0fa1a4887ae8f6e2d2582385c..62ee8308d6829140976b860f9d1e574b3d5c93b2 100644 (file)
@@ -49,6 +49,7 @@ struct i915_params i915 __read_mostly = {
        .enable_cmd_parser = 1,
        .disable_vtd_wa = 0,
        .use_mmio_flip = 0,
+       .mmio_debug = 0,
 };
 
 module_param_named(modeset, i915.modeset, int, 0400);
@@ -161,3 +162,8 @@ MODULE_PARM_DESC(enable_cmd_parser,
 module_param_named(use_mmio_flip, i915.use_mmio_flip, int, 0600);
 MODULE_PARM_DESC(use_mmio_flip,
                 "use MMIO flips (-1=never, 0=driver discretion [default], 1=always)");
+
+module_param_named(mmio_debug, i915.mmio_debug, bool, 0600);
+MODULE_PARM_DESC(mmio_debug,
+       "Enable the MMIO debug code (default: false). This may negatively "
+       "affect performance.");
index e0f0843569a63ec2a96fe054b96d2e3de91af3ce..6fee122eb2524bf5b071f527544b4d5e03747892 100644 (file)
@@ -514,20 +514,30 @@ ilk_dummy_write(struct drm_i915_private *dev_priv)
 }
 
 static void
-hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg)
+hsw_unclaimed_reg_debug(struct drm_i915_private *dev_priv, u32 reg, bool read,
+                       bool before)
 {
+       const char *op = read ? "reading" : "writing to";
+       const char *when = before ? "before" : "after";
+
+       if (!i915.mmio_debug)
+               return;
+
        if (__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM) {
-               DRM_ERROR("Unknown unclaimed register before writing to %x\n",
-                         reg);
+               WARN(1, "Unclaimed register detected %s %s register 0x%x\n",
+                    when, op, reg);
                __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
        }
 }
 
 static void
-hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg)
+hsw_unclaimed_reg_detect(struct drm_i915_private *dev_priv)
 {
+       if (i915.mmio_debug)
+               return;
+
        if (__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM) {
-               DRM_ERROR("Unclaimed write to %x\n", reg);
+               DRM_ERROR("Unclaimed register detected. Please use the i915.mmio_debug=1 to debug this problem.");
                __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
        }
 }
@@ -564,6 +574,7 @@ gen5_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
 static u##x \
 gen6_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
        REG_READ_HEADER(x); \
+       hsw_unclaimed_reg_debug(dev_priv, reg, true, true); \
        if (dev_priv->uncore.forcewake_count == 0 && \
            NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
                dev_priv->uncore.funcs.force_wake_get(dev_priv, \
@@ -574,6 +585,7 @@ gen6_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
        } else { \
                val = __raw_i915_read##x(dev_priv, reg); \
        } \
+       hsw_unclaimed_reg_debug(dev_priv, reg, true, false); \
        REG_READ_FOOTER; \
 }
 
@@ -700,12 +712,13 @@ hsw_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace)
        if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
                __fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
        } \
-       hsw_unclaimed_reg_clear(dev_priv, reg); \
+       hsw_unclaimed_reg_debug(dev_priv, reg, false, true); \
        __raw_i915_write##x(dev_priv, reg, val); \
        if (unlikely(__fifo_ret)) { \
                gen6_gt_check_fifodbg(dev_priv); \
        } \
-       hsw_unclaimed_reg_check(dev_priv, reg); \
+       hsw_unclaimed_reg_debug(dev_priv, reg, false, false); \
+       hsw_unclaimed_reg_detect(dev_priv); \
        REG_WRITE_FOOTER; \
 }