drm/amdgpu: Add check to prevent IH overflow
authorDefang Bo <bodefang@126.com>
Tue, 5 Jan 2021 16:06:39 +0000 (00:06 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Tue, 5 Jan 2021 20:05:16 +0000 (15:05 -0500)
Similar to commit <b82175750131>("drm/amdgpu: fix IH overflow on Vega10 v2").
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.

Reviewed-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Defang Bo <bodefang@126.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/amdgpu/cz_ih.c
drivers/gpu/drm/amd/amdgpu/iceland_ih.c
drivers/gpu/drm/amd/amdgpu/tonga_ih.c

index da37f8a900afb737ee1d55b1e62fd76445483542..307c01301c87a7f600e98bf0c927376028abb863 100644 (file)
@@ -194,19 +194,30 @@ static u32 cz_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 + 16). Hopefully
-                * this should allow us to catchup.
-                */
-               dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
-                       wptr, ih->rptr, (wptr + 16) & ih->ptr_mask);
-               ih->rptr = (wptr + 16) & ih->ptr_mask;
-               tmp = RREG32(mmIH_RB_CNTL);
-               tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
-               WREG32(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(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 + 16). Hopefully
+        * this should allow us to catchup.
+        */
+       dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
+               wptr, ih->rptr, (wptr + 16) & ih->ptr_mask);
+       ih->rptr = (wptr + 16) & ih->ptr_mask;
+       tmp = RREG32(mmIH_RB_CNTL);
+       tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
+       WREG32(mmIH_RB_CNTL, tmp);
+
+
+out:
        return (wptr & ih->ptr_mask);
 }
 
index 37d8b6ca4dab8a76261d6e4cce1cf641bcdd4ed0..cc957471f31ea04b05fa4e12f4181b9680da6208 100644 (file)
@@ -194,19 +194,29 @@ static u32 iceland_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 + 16). Hopefully
-                * this should allow us to catchup.
-                */
-               dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
-                        wptr, ih->rptr, (wptr + 16) & ih->ptr_mask);
-               ih->rptr = (wptr + 16) & ih->ptr_mask;
-               tmp = RREG32(mmIH_RB_CNTL);
-               tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
-               WREG32(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(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 + 16). Hopefully
+        * this should allow us to catchup.
+        */
+       dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
+               wptr, ih->rptr, (wptr + 16) & ih->ptr_mask);
+       ih->rptr = (wptr + 16) & ih->ptr_mask;
+       tmp = RREG32(mmIH_RB_CNTL);
+       tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
+       WREG32(mmIH_RB_CNTL, tmp);
+
+
+out:
        return (wptr & ih->ptr_mask);
 }
 
index ce3319993b4bd4553025a34a92cb41e7280b483c..249fcbee7871cc6525858a8b8284fde598bfb719 100644 (file)
@@ -196,19 +196,30 @@ static u32 tonga_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 + 16). Hopefully
-                * this should allow us to catchup.
-                */
-               dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
-                        wptr, ih->rptr, (wptr + 16) & ih->ptr_mask);
-               ih->rptr = (wptr + 16) & ih->ptr_mask;
-               tmp = RREG32(mmIH_RB_CNTL);
-               tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
-               WREG32(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(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 + 16). Hopefully
+        * this should allow us to catchup.
+        */
+
+       dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
+               wptr, ih->rptr, (wptr + 16) & ih->ptr_mask);
+       ih->rptr = (wptr + 16) & ih->ptr_mask;
+       tmp = RREG32(mmIH_RB_CNTL);
+       tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
+       WREG32(mmIH_RB_CNTL, tmp);
+
+out:
        return (wptr & ih->ptr_mask);
 }