From 357859726aab7bed38913b9f47fb1c145e70ccaa Mon Sep 17 00:00:00 2001 From: KiralyCraft Date: Fri, 2 Oct 2026 23:19:59 +0300 Subject: [PATCH 1/3] present: disarm wait-fence callbacks before re-execution Re-execution can queue a Present request for a later MSC and leave its fence alive. Clear the one-shot callback before retrying so a restarted Sync trigger scan cannot repeatedly fire the same request. A genuine new wait installs its own callback. Port the independently tested Termux:X11 callback correction through the existing Debian quilt series. The protocol-only divisor/requeue test passes five times with the final series; omitting this correction from the restart-scan build reproduces a busy loop and missing completion. --- ...isarm-wait-fence-before-re-execution.patch | 33 +++++++++++++++++++ debian/patches/series | 1 + 2 files changed, 34 insertions(+) create mode 100644 debian/patches/0005-present-disarm-wait-fence-before-re-execution.patch diff --git a/debian/patches/0005-present-disarm-wait-fence-before-re-execution.patch b/debian/patches/0005-present-disarm-wait-fence-before-re-execution.patch new file mode 100644 index 000000000..6d109c069 --- /dev/null +++ b/debian/patches/0005-present-disarm-wait-fence-before-re-execution.patch @@ -0,0 +1,33 @@ +From: KiralyCraft +Subject: present: disarm the wait-fence callback before re-execution + +A fence notification is one-shot, but re_execute() can leave the Present +request alive while it waits for its target MSC. Clear the callback before +re-entering the execution path. If that path must wait again, it installs +a new callback itself. + +This is required when the Sync trigger walker restarts after a callback: +otherwise a still-live request can be called repeatedly in the same scan. +It also prevents a later trigger of a reused fence from executing the old +request again. + +The same callback-lifetime correction is used by Termux:X11: +https://github.com/KiralyCraft/termux-x11/commit/fc534e514d7ad4851d0aca3357495816260e4549 +--- + present/present_execute.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/present/present_execute.c b/present/present_execute.c +--- a/present/present_execute.c ++++ b/present/present_execute.c +@@ -38,6 +38,10 @@ present_wait_fence_triggered(void *param) + ScreenPtr screen = vblank->screen; + present_screen_priv_ptr screen_priv = present_screen_priv(screen); + ++ /* Re-execution may keep this request alive until its target MSC. ++ * Disarm this notification; a new wait will install a new callback. */ ++ present_fence_set_callback(vblank->wait_fence, NULL, NULL); ++ + screen_priv->re_execute(vblank); + } + diff --git a/debian/patches/series b/debian/patches/series index 0232983ed..4811d1b91 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -2,3 +2,4 @@ 0002-xwayland-glamor-add-KGSL-surfaceless-backend-path.patch 0003-xwayland-dri3-support-KGSL-surfaceless-client-render.patch 0004-xwayland-scope-KGSL-frame-callback-recovery-per-window.patch +0005-present-disarm-wait-fence-before-re-execution.patch From e0407dfee040c89c531fb267263245d0bca00501 Mon Sep 17 00:00:00 2001 From: KiralyCraft Date: Fri, 2 Oct 2026 23:20:58 +0300 Subject: [PATCH 2/3] miext/sync: backport callback-safe fence trigger iteration Backport upstream f19ab94ba9c891d801231654267556dc7f32b5e0 through the Debian quilt series, preserving Olivier Fourdan attribution. A fired callback can remove other triggers, so restart from the live list head rather than dereference a saved next pointer. Apply after the Present one-shot callback correction to avoid a restart-scan livelock when re-execution queues the request for a future MSC. The combined ARM64 build passes the protocol controls and completes the full 33-scene glmark2 suite with unchanged baseline Mesa. --- ...-restart-trigger-scan-after-callback.patch | 49 +++++++++++++++++++ debian/patches/series | 1 + 2 files changed, 50 insertions(+) create mode 100644 debian/patches/0006-miext-sync-restart-trigger-scan-after-callback.patch diff --git a/debian/patches/0006-miext-sync-restart-trigger-scan-after-callback.patch b/debian/patches/0006-miext-sync-restart-trigger-scan-after-callback.patch new file mode 100644 index 000000000..daad604a5 --- /dev/null +++ b/debian/patches/0006-miext-sync-restart-trigger-scan-after-callback.patch @@ -0,0 +1,49 @@ +From: Olivier Fourdan +Subject: miext/sync: restart the trigger scan after a callback +Origin: upstream, https://gitlab.freedesktop.org/xorg/xserver/-/commit/f19ab94ba9c891d801231654267556dc7f32b5e0 + +Backport upstream's trigger-list lifetime correction. A callback can remove +other entries as well as its own entry, so saving the next pointer before +calling it is not sufficient. Restart from the current list head instead. + +Apply after the Present one-shot callback correction, so a request that +survives re-execution cannot keep firing in the restart scan. + +Signed-off-by: Olivier Fourdan +Acked-by: Peter Hutterer +--- + miext/sync/misync.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/miext/sync/misync.c b/miext/sync/misync.c +--- a/miext/sync/misync.c ++++ b/miext/sync/misync.c +@@ -131,16 +131,22 @@ miSyncDestroyFence(SyncFence * pFence) + void + miSyncTriggerFence(SyncFence * pFence) + { +- SyncTriggerList *ptl, *pNext; ++ SyncTriggerList *ptl; ++ Bool triggered; + + pFence->funcs.SetTriggered(pFence); + + /* run through triggers to see if any fired */ +- for (ptl = pFence->sync.pTriglist; ptl; ptl = pNext) { +- pNext = ptl->next; +- if ((*ptl->pTrigger->CheckTrigger) (ptl->pTrigger, 0)) +- (*ptl->pTrigger->TriggerFired) (ptl->pTrigger); +- } ++ do { ++ triggered = FALSE; ++ for (ptl = pFence->sync.pTriglist; ptl; ptl = ptl->next) { ++ if ((*ptl->pTrigger->CheckTrigger) (ptl->pTrigger, 0)) { ++ (*ptl->pTrigger->TriggerFired) (ptl->pTrigger); ++ triggered = TRUE; ++ break; ++ } ++ } ++ } while (triggered); + } + + SyncScreenFuncsPtr diff --git a/debian/patches/series b/debian/patches/series index 4811d1b91..ec7d92195 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -3,3 +3,4 @@ 0003-xwayland-dri3-support-KGSL-surfaceless-client-render.patch 0004-xwayland-scope-KGSL-frame-callback-recovery-per-window.patch 0005-present-disarm-wait-fence-before-re-execution.patch +0006-miext-sync-restart-trigger-scan-after-callback.patch From 99f011557ad5b6c4200b6b48a15fb7d5aaa92698 Mon Sep 17 00:00:00 2001 From: KiralyCraft Date: Fri, 2 Oct 2026 23:20:58 +0300 Subject: [PATCH 3/3] xwayland: avoid reacquiring the source after Present COPY After Idle, the client can reuse the pixmap and reset its wait fence for another frame. The old copied request must not acquire that source again when delivering its delayed Complete notification. A second wait can overwrite blocking_event with the copied request and leave subsequent ready frames stranded. Guard the acquisition stage with !event->copy_executed. Keep the original acquisition, asynchronous rendering fences and completion scheduling unchanged. Do not add tracing, extra waits or timer workarounds. On Adreno 710 Anland/KDE, the Mesa-independent Idle/reset/Complete regression test passes five times. The exact combined quilt series completes all 33 glmark2 scenes: unchanged merged-PR96 Mesa scores 418 versus 286 with PR105 on the same server. Original packaged and rebuilt reference servers stall in the first uncapped scene. --- ...and-present-do-not-rewait-after-copy.patch | 37 +++++++++++++++++++ debian/patches/series | 1 + 2 files changed, 38 insertions(+) create mode 100644 debian/patches/0007-xwayland-present-do-not-rewait-after-copy.patch diff --git a/debian/patches/0007-xwayland-present-do-not-rewait-after-copy.patch b/debian/patches/0007-xwayland-present-do-not-rewait-after-copy.patch new file mode 100644 index 000000000..410f6598b --- /dev/null +++ b/debian/patches/0007-xwayland-present-do-not-rewait-after-copy.patch @@ -0,0 +1,37 @@ +From: KiralyCraft +Subject: xwayland: do not reacquire a copied Present pixmap + +After a COPY Present, Idle permits the client to reuse the source pixmap +and reset its wait fence for a later frame. The old request may still be +queued for its delayed Complete notification. Re-executing that request +must not wait for the source again, because that now belongs to a different +presentation. + +Waiting a second time can overwrite blocking_event with the old copied +request. That request subsequently completes without draining blocked_queue, +leaving newer requests blocked indefinitely. + +Only perform acquisition before the copy. The first wait, asynchronous +render fences, and delayed completion scheduling are unchanged. +--- + hw/xwayland/xwayland-present.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c +--- a/hw/xwayland/xwayland-present.c ++++ b/hw/xwayland/xwayland-present.c +@@ -1048,8 +1048,12 @@ xwl_present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc) + } + + retry: +- if (present_execute_wait(vblank, crtc_msc) || +- xwl_present_wait_acquire_fence_avail(xwl_screen, vblank)) { ++ /* The source pixmap is already idle after a copy. Its wait fence may ++ * have been reset for a later Present, so do not acquire it again just ++ * to send this request's delayed completion notification. */ ++ if (!event->copy_executed && ++ (present_execute_wait(vblank, crtc_msc) || ++ xwl_present_wait_acquire_fence_avail(xwl_screen, vblank))) { + if (!notify_only) + /* block execution of subsequent requests until this request is ready */ + xwl_present_window->blocking_event = event->vblank.event_id; diff --git a/debian/patches/series b/debian/patches/series index ec7d92195..a078be54c 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -4,3 +4,4 @@ 0004-xwayland-scope-KGSL-frame-callback-recovery-per-window.patch 0005-present-disarm-wait-fence-before-re-execution.patch 0006-miext-sync-restart-trigger-scan-after-callback.patch +0007-xwayland-present-do-not-rewait-after-copy.patch