]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
drm/v3d: Address race-condition in MMU flush
authorMaíra Canal <mcanal@igalia.com>
Mon, 23 Sep 2024 13:55:05 +0000 (10:55 -0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 14 Dec 2024 18:50:52 +0000 (19:50 +0100)
[ Upstream commit cf1becb7f996a0a23ea2c270cf6bb0911ec3ca1a ]

We must first flush the MMU cache and then, flush the TLB, not the other
way around. Currently, we can see a race condition between the MMU cache
and the TLB when running multiple rendering processes at the same time.
This is evidenced by MMU errors triggered by the IRQ.

Fix the MMU flush order by flushing the MMU cache and then the TLB.
Also, in order to address the race condition, wait for the MMU cache flush
to finish before starting the TLB flush.

Fixes: 57692c94dcbe ("drm/v3d: Introduce a new DRM driver for Broadcom V3D V3.x+")
Signed-off-by: Maíra Canal <mcanal@igalia.com>
Reviewed-by: Iago Toral Quiroga <itoral@igalia.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240923141348.2422499-2-mcanal@igalia.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/gpu/drm/v3d/v3d_mmu.c

index 5a453532901f15cf2cdddd79d5d5042236dff844..166d4a88daee5b350b5f21244bac3115e1e7372d 100644 (file)
@@ -34,32 +34,23 @@ static int v3d_mmu_flush_all(struct v3d_dev *v3d)
 {
        int ret;
 
-       /* Make sure that another flush isn't already running when we
-        * start this one.
-        */
-       ret = wait_for(!(V3D_READ(V3D_MMU_CTL) &
-                        V3D_MMU_CTL_TLB_CLEARING), 100);
-       if (ret)
-               dev_err(v3d->drm.dev, "TLB clear wait idle pre-wait failed\n");
-
-       V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL) |
-                 V3D_MMU_CTL_TLB_CLEAR);
-
-       V3D_WRITE(V3D_MMUC_CONTROL,
-                 V3D_MMUC_CONTROL_FLUSH |
+       V3D_WRITE(V3D_MMUC_CONTROL, V3D_MMUC_CONTROL_FLUSH |
                  V3D_MMUC_CONTROL_ENABLE);
 
-       ret = wait_for(!(V3D_READ(V3D_MMU_CTL) &
-                        V3D_MMU_CTL_TLB_CLEARING), 100);
+       ret = wait_for(!(V3D_READ(V3D_MMUC_CONTROL) &
+                        V3D_MMUC_CONTROL_FLUSHING), 100);
        if (ret) {
-               dev_err(v3d->drm.dev, "TLB clear wait idle failed\n");
+               dev_err(v3d->drm.dev, "MMUC flush wait idle failed\n");
                return ret;
        }
 
-       ret = wait_for(!(V3D_READ(V3D_MMUC_CONTROL) &
-                        V3D_MMUC_CONTROL_FLUSHING), 100);
+       V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL) |
+                 V3D_MMU_CTL_TLB_CLEAR);
+
+       ret = wait_for(!(V3D_READ(V3D_MMU_CTL) &
+                        V3D_MMU_CTL_TLB_CLEARING), 100);
        if (ret)
-               dev_err(v3d->drm.dev, "MMUC flush wait idle failed\n");
+               dev_err(v3d->drm.dev, "MMU TLB clear wait idle failed\n");
 
        return ret;
 }