From 3509cf6127185d9fed5a63ceca18a003213a20d7 Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:31:00 +0200 Subject: Fix VT-switch resume getting stuck on a stale pending page flip A flip issued right before a VT switch away could still be undelivered when the session resumed - the kernel refuses a new page flip on a CRTC with one already outstanding, which showed up live as a black screen that never recovered across two switch attempts, with a rapid repeating "Device or resource busy" loop in the log. Drain and apply any pending DRM events before reasserting CRTCs and rendering again on resume, so a stale flip from before the switch can't collide with the fresh one. --- crates/wayland/src/udev/session.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/wayland/src/udev/session.rs b/crates/wayland/src/udev/session.rs index 2853f33..fa40b0e 100644 --- a/crates/wayland/src/udev/session.rs +++ b/crates/wayland/src/udev/session.rs @@ -63,9 +63,32 @@ pub(crate) fn register_session_notifier(handle: &LoopHandle<'static, CompState>, SessionEvent::ActivateSession => { log::info!("udev: session resumed (VT switch back)"); udev.active = true; + let card = udev.card.clone(); + + // A flip issued right before the VT switch away may + // never have completed while inactive (nothing was + // scanning out), and its completion event can still be + // sitting undelivered on the DRM fd. The kernel refuses + // a new page flip on a CRTC with one already + // unacknowledged (EBUSY) - drain and apply any such + // events now, before reasserting crtcs, so a stale flip + // from before the switch can't collide with the fresh + // one `render_udev_frame` is about to issue below. + match card.receive_events() { + Ok(events) => { + for event in events { + let DrmEvent::PageFlip(flip) = event else { continue }; + if let Some(head) = udev.heads.iter_mut().find(|h| h.crtc == flip.crtc) { + head.front = 1 - head.front; + head.flip_pending = false; + } + } + } + Err(e) => log::debug!("udev: no pending flip events to drain on resume: {e}"), + } + // Some drivers reset mode-setting state across a VT // switch; reassert every head before rendering again. - let card = udev.card.clone(); for head in &mut udev.heads { let fb = head.buffers[head.front].fb; if let Err(e) = card.set_crtc(head.crtc, Some(fb), (0, 0), &[], None) { -- cgit v1.2.3