drm/amdgpu: fix IH overflow on Vega10 v2
authorChristian König <christian.koenig@amd.com>
Fri, 14 Dec 2018 14:31:24 +0000 (15:31 +0100)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 14 Jan 2019 20:04:47 +0000 (15:04 -0500)
When an ring buffer overflow happens the appropriate bit is set in the WPTR
register which is also written back to memory. But clearing the bit in the
WPTR doesn't trigger another memory writeback.

So what can happen is that we end up processing the buffer overflow over and
over again because the bit is never cleared. Resulting in a random system
lockup because of an infinite loop in an interrupt handler.

This is 100% reproducible on Vega10, but it's most likely an issue we have
in the driver over all generations all the way back to radeon.

v2: rebase

Signed-off-by: Christian König <christian.koenig@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/amdgpu/vega10_ih.c

index 3e9ebb0de94d7efd3ebb7d6eef4f9832a2f6b6e9..562701939d3eaaa8d0175d7c2991e49c83f617cc 100644 (file)
@@ -192,22 +192,31 @@ static u32 vega10_ih_get_wptr(struct amdgpu_device *adev,
 
        wptr = le32_to_cpu(*ih->wptr_cpu);
 
-       if (REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) {
-               wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0);
-
-               /* When a ring buffer overflow happen start parsing interrupt
-                * from the last not overwritten vector (wptr + 32). Hopefully
-                * this should allow us to catchup.
-                */
-               tmp = (wptr + 32) & ih->ptr_mask;
-               dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
-                        wptr, ih->rptr, tmp);
-               ih->rptr = tmp;
-
-               tmp = RREG32_NO_KIQ(SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_CNTL));
-               tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
-               WREG32_NO_KIQ(SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_CNTL), tmp);
-       }
+       if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW))
+               goto out;
+
+       /* Double check that the overflow wasn't already cleared. */
+       wptr = RREG32_NO_KIQ(SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_WPTR));
+       if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW))
+               goto out;
+
+       wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0);
+
+       /* When a ring buffer overflow happen start parsing interrupt
+        * from the last not overwritten vector (wptr + 32). Hopefully
+        * this should allow us to catchup.
+        */
+       tmp = (wptr + 32) & ih->ptr_mask;
+       dev_warn(adev->dev, "IH ring buffer overflow "
+                "(0x%08X, 0x%08X, 0x%08X)\n",
+                wptr, ih->rptr, tmp);
+       ih->rptr = tmp;
+
+       tmp = RREG32_NO_KIQ(SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_CNTL));
+       tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
+       WREG32_NO_KIQ(SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_CNTL), tmp);
+
+out:
        return (wptr & ih->ptr_mask);
 }