On IVB hardware we are given an interrupt whenever a parity error occurs
in the L3 cache. This is a very rare occurrence (in fact to test this I
need to use specially instrumented silicon).

When a row in the L3 cache detects a parity error, we alert userspace
via a uevent. With this information userspace can use a sysfs interface
(follow-up patch) to remap those row. Based on the fabrication
process, and random events like gamma rays, certain rows *can* be more
susceptible.

Signed-off-by: Ben Widawsky <[email protected]>
---
 drivers/gpu/drm/i915/i915_drv.h         |    2 +
 drivers/gpu/drm/i915/i915_irq.c         |   65 ++++++++++++++++++++++++++++++-
 drivers/gpu/drm/i915/i915_reg.h         |   16 ++++++++
 drivers/gpu/drm/i915/intel_ringbuffer.c |    6 ++-
 4 files changed, 86 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 422f424..8f31e0f 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -805,6 +805,8 @@ typedef struct drm_i915_private {
 
        struct drm_property *broadcast_rgb_property;
        struct drm_property *force_audio_property;
+
+       struct work_struct parity_error_work;
 } drm_i915_private_t;
 
 enum hdmi_force_audio {
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index febddc2..ef7c126 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -430,6 +430,63 @@ static void gen6_pm_rps_work(struct work_struct *work)
        mutex_unlock(&dev_priv->dev->struct_mutex);
 }
 
+
+/**
+ * ivybridge_parity_work - Workqueue called when a parity error interrupt
+ * occurred.
+ *
+ * Doesn't actually do anything except notify userspace so that userspace may
+ * disable things later on.
+ */
+static void ivybridge_parity_work(struct work_struct *work)
+{
+       drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
+                                                   parity_error_work);
+
+       u32 error_status, row, bank, subbank;
+       char *parity_event[5];
+
+       /* We must turn off DOP level clock gating to access the L3 registers.
+        * In order to prevent a get/put style interface, acquire struct mutex
+        * any time we access those registers.
+        */
+       mutex_lock(&dev_priv->dev->struct_mutex);
+       I915_WRITE(GEN7_MISCCPCTL,
+                  I915_READ(GEN7_MISCCPCTL) & ~GEN7_DOP_CLOCK_GATE_ENABLE);
+       POSTING_READ(GEN7_MISCCPCTL);
+       error_status = I915_READ(GEN7_L3CDERRST1);
+       row = GEN7_PARITY_ERROR_ROW(error_status);
+       bank = GEN7_PARITY_ERROR_BANK(error_status);
+       subbank = GEN7_PARITY_ERROR_SUBBANK(error_status);
+       I915_WRITE(GEN7_L3CDERRST1, GEN7_PARITY_ERROR_VALID);
+       POSTING_READ(GEN7_MISCCPCTL);
+       I915_WRITE(GEN7_MISCCPCTL,
+                  I915_READ(GEN7_MISCCPCTL) | GEN7_DOP_CLOCK_GATE_ENABLE);
+       mutex_unlock(&dev_priv->dev->struct_mutex);
+
+       parity_event[0] = "L3_PARITY_ERROR=1";
+       parity_event[1] = kasprintf(GFP_KERNEL, "ROW=%d", row);
+       parity_event[2] = kasprintf(GFP_KERNEL, "BANK=%d", bank);
+       parity_event[3] = kasprintf(GFP_KERNEL, "SUBBANK=%d", subbank);
+       parity_event[4] = NULL;
+
+       kobject_uevent_env(&dev_priv->dev->primary->kdev.kobj,
+                          KOBJ_CHANGE, parity_event);
+
+       kfree(parity_event[3]);
+       kfree(parity_event[2]);
+       kfree(parity_event[1]);
+
+}
+
+void ivybridge_handle_parity_error(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       queue_work(dev_priv->wq, &dev_priv->parity_error_work);
+       DRM_INFO("Parity error interrupt. Scheduling work\n");
+}
+
 static void snb_gt_irq_handler(struct drm_device *dev,
                               struct drm_i915_private *dev_priv,
                               u32 gt_iir)
@@ -449,6 +506,9 @@ static void snb_gt_irq_handler(struct drm_device *dev,
                DRM_ERROR("GT error interrupt 0x%08x\n", gt_iir);
                i915_handle_error(dev, false);
        }
+
+       if (gt_iir & GT_GEN7_L3_PARITY_ERROR_INTERRUPT)
+               ivybridge_handle_parity_error(dev);
 }
 
 static irqreturn_t valleyview_irq_handler(DRM_IRQ_ARGS)
@@ -1972,6 +2032,9 @@ static void ironlake_irq_preinstall(struct drm_device 
*dev)
        if (IS_GEN6(dev) || IS_IVYBRIDGE(dev))
                INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work);
 
+       if (IS_IVYBRIDGE(dev))
+               INIT_WORK(&dev_priv->parity_error_work, ivybridge_parity_work);
+
        I915_WRITE(HWSTAM, 0xeffe);
 
        /* XXX hotplug from PCH */
@@ -2152,7 +2215,7 @@ static int ivybridge_irq_postinstall(struct drm_device 
*dev)
        I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
 
        render_irqs = GT_USER_INTERRUPT | GEN6_BSD_USER_INTERRUPT |
-               GEN6_BLITTER_USER_INTERRUPT;
+               GEN6_BLITTER_USER_INTERRUPT | GT_GEN7_L3_PARITY_ERROR_INTERRUPT;
        I915_WRITE(GTIER, render_irqs);
        POSTING_READ(GTIER);
 
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 972321f..de1685e 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3991,6 +3991,22 @@
 #define   GEN6_RC6                     3
 #define   GEN6_RC7                     4
 
+#define GEN7_MISCCPCTL                 (0x9424)
+#define   GEN7_DOP_CLOCK_GATE_ENABLE   (1<<0)
+
+/* IVYBRIDGE DPF */
+#define GEN7_L3CDERRST1                        0xB008 /* L3CD Error Status 1 */
+#define   GEN7_L3CDERRST1_ROW_MASK     (0x7ff<<14)
+#define   GEN7_PARITY_ERROR_VALID      (1<<13)
+#define   GEN7_L3CDERRST1_BANK_MASK    (3<<11)
+#define   GEN7_L3CDERRST1_SUBBANK_MASK (7<<8)
+#define GEN7_PARITY_ERROR_ROW(reg) \
+               ((reg & GEN7_L3CDERRST1_ROW_MASK) >> 14)
+#define GEN7_PARITY_ERROR_BANK(reg) \
+               ((reg & GEN7_L3CDERRST1_BANK_MASK) >> 11)
+#define GEN7_PARITY_ERROR_SUBBANK(reg) \
+               ((reg & GEN7_L3CDERRST1_SUBBANK_MASK) >> 8)
+
 #define G4X_AUD_VID_DID                        0x62020
 #define INTEL_AUDIO_DEVCL              0x808629FB
 #define INTEL_AUDIO_DEVBLC             0x80862801
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c 
b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 467b331..f956741 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -1446,7 +1446,8 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
                ring->flush = gen6_render_ring_flush;
                ring->irq_get = gen6_ring_get_irq;
                ring->irq_put = gen6_ring_put_irq;
-               ring->irq_enable = GT_USER_INTERRUPT;
+               ring->irq_enable = GT_USER_INTERRUPT |
+                                  GT_GEN7_L3_PARITY_ERROR_INTERRUPT;
                ring->get_seqno = gen6_ring_get_seqno;
        } else if (IS_GEN5(dev)) {
                ring->add_request = pc_render_add_request;
@@ -1471,7 +1472,8 @@ int intel_render_ring_init_dri(struct drm_device *dev, 
u64 start, u32 size)
                ring->add_request = gen6_add_request;
                ring->irq_get = gen6_ring_get_irq;
                ring->irq_put = gen6_ring_put_irq;
-               ring->irq_enable = GT_USER_INTERRUPT;
+               ring->irq_enable = GT_USER_INTERRUPT |
+                                  GT_GEN7_L3_PARITY_ERROR_INTERRUPT;
        } else if (IS_GEN5(dev)) {
                ring->add_request = pc_render_add_request;
                ring->get_seqno = pc_render_get_seqno;
-- 
1.7.10

_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to