drm/amdgpu: Add check to prevent IH overflow
authorDefang Bo <bodefang@126.com>
Tue, 5 Jan 2021 16:06:39 +0000 (00:06 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 7 Mar 2021 11:20:46 +0000 (12:20 +0100)
[ Upstream commit e4180c4253f3f2da09047f5139959227f5cf1173 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
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 1dca0cabc326a1d1fca374f62edee69c060d4034..13520d173296f59fbdb11c89a271eedf3189b211 100644 (file)
@@ -193,19 +193,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 a13dd9a51149a408cb70949ddcd12450f73269ed..7d165f024f072ddcfe6852a8d106e9d0c3519a19 100644 (file)
@@ -193,19 +193,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 e40140bf6699ccae54d060989ff773b6530c3580..db0a3bda13fbed46054307e2b8e08092ce99a0a2 100644 (file)
@@ -195,19 +195,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);
 }