The modeset locks protect software state, but a nonblocking commit can
swap that state before its hardware updates finish. DirtyFB can then
race primary-surface replacement and draw into the preceding surface.

Wait for the current CRTC commits while holding the modeset locks before
checking the primary and issuing dirty updates. Return wait and lock
errors to the caller instead of reporting success.

Fixes: 9973c879cff7 ("drm: qxl: Atomic phase 3: Wire up atomic page_flip 
helper")
Assisted-by: LLM sparse
Signed-off-by: Dillon Amburgey <[email protected]>
---
 drivers/gpu/drm/qxl/qxl_display.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/qxl/qxl_display.c 
b/drivers/gpu/drm/qxl/qxl_display.c
index 1f869734e14d..51087cacff74 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -437,10 +437,17 @@ static int qxl_framebuffer_surface_dirty(struct 
drm_framebuffer *fb,
        struct qxl_bo *qobj;
        struct drm_modeset_acquire_ctx ctx;
        bool is_primary;
+       struct drm_crtc *crtc;
        int inc = 1, ret;
 
        DRM_MODESET_LOCK_ALL_BEGIN(fb->dev, ctx, 
DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret);
 
+       drm_for_each_crtc(crtc, &qdev->ddev) {
+               ret = drm_crtc_commit_wait(crtc->state->commit);
+               if (ret)
+                       goto out_lock_end;
+       }
+
        qobj = gem_to_qxl_bo(fb->obj[0]);
        /* if we aren't primary surface ignore this */
        is_primary = qobj->shadow ? qobj->shadow->is_primary : qobj->is_primary;
@@ -464,7 +471,7 @@ static int qxl_framebuffer_surface_dirty(struct 
drm_framebuffer *fb,
 out_lock_end:
        DRM_MODESET_LOCK_ALL_END(fb->dev, ctx, ret);
 
-       return 0;
+       return ret;
 }
 
 static const struct drm_framebuffer_funcs qxl_fb_funcs = {

Reply via email to