From c436e7cb1715d6112be303c7c5c6500d2f97fa3d Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Wed, 14 Aug 2024 15:50:48 +0800 Subject: [PATCH] kms/impl-device: Add/remove deadline timer as required On a hybrid machine with i915 primary and nvidia-drm (470) secondary, `meta_render_device_egl_stream_initable_init` calls `meta_kms_inhibit_kernel_thread` to change from the default 'kernel' thread type to 'user'. And soon after that it would `meta_render_device_egl_stream_finalize` because I'm not actually using that GPU, and calls `meta_kms_uninhibit_kernel_thread`. So during startup Mutter would default to a realtime kernel thread, switch to a user thread (which doesn't support realtime), and then switch back to a realtime kernel thread. In the middle of all that, while the thread type was 'user' and realtime disabled, something was invoking `ensure_crtc_frame` which created a `CrtcFrame` without a deadline timer. Soon after that the thread type changed back to 'kernel' with deadline timers expected, but our existing `CrtcFrame` has no deadline timer associated with it. And so it would never fire, causing the cursor to freeze whenever the primary plane isn't changing. And the problem was permanent, not just the first frame because each `CrtcFrame` gets repeatedly reused (maybe shouldn't be called a "Frame"?). Now we adapt to switching between kernel and user thread types by adding and removing the deadline timer as required. Close: https://gitlab.gnome.org/GNOME/mutter/-/issues/3464 Part-of: --- src/backends/native/meta-kms-impl-device.c | 29 ++++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c index 4bbb9bc45..1ae9abd06 100644 --- a/src/backends/native/meta-kms-impl-device.c +++ b/src/backends/native/meta-kms-impl-device.c @@ -1664,18 +1664,22 @@ ensure_crtc_frame (MetaKmsImplDevice *impl_device, MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device); MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl); CrtcFrame *crtc_frame; + gboolean want_deadline_timer, have_deadline_timer; crtc_frame = get_crtc_frame (impl_device, latch_crtc); - if (crtc_frame) - return crtc_frame; + if (!crtc_frame) + { + crtc_frame = g_new0 (CrtcFrame, 1); + crtc_frame->impl_device = impl_device; + crtc_frame->crtc = latch_crtc; + crtc_frame->deadline.timer_fd = -1; + crtc_frame->await_flush = TRUE; + g_hash_table_insert (priv->crtc_frames, latch_crtc, crtc_frame); + } - crtc_frame = g_new0 (CrtcFrame, 1); - crtc_frame->impl_device = impl_device; - crtc_frame->crtc = latch_crtc; - crtc_frame->deadline.timer_fd = -1; - crtc_frame->await_flush = TRUE; - - if (is_using_deadline_timer (impl_device)) + want_deadline_timer = is_using_deadline_timer (impl_device); + have_deadline_timer = crtc_frame->deadline.timer_fd >= 0; + if (want_deadline_timer && !have_deadline_timer) { int timer_fd; GSource *source; @@ -1700,8 +1704,11 @@ ensure_crtc_frame (MetaKmsImplDevice *impl_device, g_source_unref (source); } - - g_hash_table_insert (priv->crtc_frames, latch_crtc, crtc_frame); + else if (!want_deadline_timer && have_deadline_timer) + { + g_clear_fd (&crtc_frame->deadline.timer_fd, NULL); + g_clear_pointer (&crtc_frame->deadline.source, g_source_destroy); + } return crtc_frame; }