diff --git a/0001-thread-Fix-preexisting-uncrustify-problem.patch b/0001-thread-Fix-preexisting-uncrustify-problem.patch new file mode 100644 index 0000000..d6bb25f --- /dev/null +++ b/0001-thread-Fix-preexisting-uncrustify-problem.patch @@ -0,0 +1,57 @@ +From 372a2ecda4c06170a85936e3b88c7fdd681cdca1 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 10 Oct 2023 16:24:43 -0400 +Subject: [PATCH 1/4] thread: Fix preexisting uncrustify problem + +uncrustify seems to want a little more indentation, so this +commit gives it what it's asking for. +--- + src/backends/native/meta-thread.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/backends/native/meta-thread.h b/src/backends/native/meta-thread.h +index f6c5c94f5..9fc3588b9 100644 +--- a/src/backends/native/meta-thread.h ++++ b/src/backends/native/meta-thread.h +@@ -72,35 +72,35 @@ void meta_thread_queue_callback (MetaThread *thread, + + META_EXPORT_TEST + void meta_thread_flush_callbacks (MetaThread *thread); + + META_EXPORT_TEST + gpointer meta_thread_run_impl_task_sync (MetaThread *thread, + MetaThreadTaskFunc func, + gpointer user_data, + GError **error); + + META_EXPORT_TEST + void meta_thread_post_impl_task (MetaThread *thread, + MetaThreadTaskFunc func, + gpointer user_data, + GDestroyNotify user_data_destroy, + MetaThreadTaskFeedbackFunc feedback_func, + gpointer feedback_user_data); + + META_EXPORT_TEST + MetaBackend * meta_thread_get_backend (MetaThread *thread); + + META_EXPORT_TEST + const char * meta_thread_get_name (MetaThread *thread); + + META_EXPORT_TEST + gboolean meta_thread_is_in_impl_task (MetaThread *thread); + + gboolean meta_thread_is_waiting_for_impl_task (MetaThread *thread); + + #define meta_assert_in_thread_impl(thread) \ +- g_assert (meta_thread_is_in_impl_task (thread)) ++ g_assert (meta_thread_is_in_impl_task (thread)) + #define meta_assert_not_in_thread_impl(thread) \ +- g_assert (!meta_thread_is_in_impl_task (thread)) ++ g_assert (!meta_thread_is_in_impl_task (thread)) + #define meta_assert_is_waiting_for_thread_impl_task(thread) \ +- g_assert (meta_thread_is_waiting_for_impl_task (thread)) ++ g_assert (meta_thread_is_waiting_for_impl_task (thread)) +-- +2.41.0 + diff --git a/0002-thread-For-consistency-s-real_time-realtime.patch b/0002-thread-For-consistency-s-real_time-realtime.patch new file mode 100644 index 0000000..73a86a8 --- /dev/null +++ b/0002-thread-For-consistency-s-real_time-realtime.patch @@ -0,0 +1,149 @@ +From 39f55e7af76ee6a6339115a386a4b26f8318fed4 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 10 Oct 2023 14:35:46 -0400 +Subject: [PATCH 2/4] thread: For consistency, s/real_time/realtime/ + +Most of the code writes "real-time" as "realtime" not "real_time". + +The only exception is one function `request_real_time_scheduling`. + +This commit changes that function for consistency. +--- + src/backends/native/meta-thread.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/backends/native/meta-thread.c b/src/backends/native/meta-thread.c +index 08d01144d..93a84a8a5 100644 +--- a/src/backends/native/meta-thread.c ++++ b/src/backends/native/meta-thread.c +@@ -175,62 +175,62 @@ get_rtkit_property (MetaDBusRealtimeKit1 *rtkit_proxy, + { + GDBusConnection *connection; + g_autoptr (GVariant) prop_value = NULL; + g_autoptr (GVariant) property_variant = NULL; + + /* The following is a fall back path for a RTKit daemon that doesn't support + * org.freedesktop.DBus.Properties.GetAll. See + * . + */ + connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (rtkit_proxy)); + prop_value = + g_dbus_connection_call_sync (connection, + "org.freedesktop.RealtimeKit1", + "/org/freedesktop/RealtimeKit1", + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", + "org.freedesktop.RealtimeKit1", + property_name), + G_VARIANT_TYPE ("(v)"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, error); + if (!prop_value) + return NULL; + + g_variant_get (prop_value, "(v)", &property_variant); + return g_steal_pointer (&property_variant); + } + + static gboolean +-request_real_time_scheduling (MetaThread *thread, +- GError **error) ++request_realtime_scheduling (MetaThread *thread, ++ GError **error) + { + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + g_autoptr (MetaDBusRealtimeKit1) rtkit_proxy = NULL; + g_autoptr (GError) local_error = NULL; + int64_t rttime; + struct rlimit rl; + uint32_t priority; + + rtkit_proxy = + meta_dbus_realtime_kit1_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + "org.freedesktop.RealtimeKit1", + "/org/freedesktop/RealtimeKit1", + NULL, + &local_error); + if (!rtkit_proxy) + { + g_dbus_error_strip_remote_error (local_error); + g_propagate_prefixed_error (error, g_steal_pointer (&local_error), + "Failed to acquire RTKit D-Bus proxy: "); + return FALSE; + } + + priority = meta_dbus_realtime_kit1_get_max_realtime_priority (rtkit_proxy); + if (priority == 0) + { + g_autoptr (GVariant) priority_variant = NULL; + + priority_variant = get_rtkit_property (rtkit_proxy, +@@ -286,61 +286,61 @@ request_real_time_scheduling (MetaThread *thread, + + return TRUE; + } + + static gpointer + thread_impl_func (gpointer user_data) + { + MetaThread *thread = META_THREAD (user_data); + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + MetaThreadImpl *impl = priv->impl; + MetaThreadImplRunFlags run_flags = META_THREAD_IMPL_RUN_FLAG_NONE; + GMainContext *thread_context = meta_thread_impl_get_main_context (impl); + #ifdef HAVE_PROFILER + MetaContext *context = meta_backend_get_context (priv->backend); + MetaProfiler *profiler = meta_context_get_profiler (context); + #endif + + g_mutex_lock (&priv->kernel.init_mutex); + g_mutex_unlock (&priv->kernel.init_mutex); + + g_main_context_push_thread_default (thread_context); + + #ifdef HAVE_PROFILER + meta_profiler_register_thread (profiler, thread_context, priv->name); + #endif + + if (priv->wants_realtime) + { + g_autoptr (GError) error = NULL; + +- if (!request_real_time_scheduling (thread, &error)) ++ if (!request_realtime_scheduling (thread, &error)) + { + g_warning ("Failed to make thread '%s' realtime scheduled: %s", + priv->name, error->message); + } + else + { + g_message ("Made thread '%s' realtime scheduled", priv->name); + run_flags |= META_THREAD_IMPL_RUN_FLAG_REALTIME; + } + } + + meta_thread_impl_run (impl, run_flags); + + #ifdef HAVE_PROFILER + meta_profiler_unregister_thread (profiler, thread_context); + #endif + + g_main_context_pop_thread_default (thread_context); + + return GINT_TO_POINTER (TRUE); + } + + typedef struct _WrapperSource + { + GSource base; + + GMainContext *thread_main_context; + + GPollFD fds[256]; + gpointer fd_tags[256]; +-- +2.41.0 + diff --git a/0003-thread-Allow-turnning-off-rt-scheduling-for-running-.patch b/0003-thread-Allow-turnning-off-rt-scheduling-for-running-.patch new file mode 100644 index 0000000..c6b11d2 --- /dev/null +++ b/0003-thread-Allow-turnning-off-rt-scheduling-for-running-.patch @@ -0,0 +1,630 @@ +From c38064c9614b70e92a38e98df3ebd6b232ea2f55 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 10 Oct 2023 14:39:13 -0400 +Subject: [PATCH 3/4] thread: Allow turnning off rt scheduling for running + thread + +At the moment if a thread is made real-time there's no going back, +it stays real-time for the duration of its life. + +That's suboptimal because real-time threads are expected by RTKit to +have an rlimit on their CPU time and certain GPU drivers in the kernel +can exceed that CPU time during certain operations like DPMS off. + +This commit adds two new ref counted functions: + + meta_thread_{un,}inhibit_realtime_in_impl + +that allow turning a thread real-time or normally scheduled. At the same +time, this commit stores the RTKit proxy as private data on the thread +so that it can be reused by the above apis. + +A subsequent commit will use the new APIs. +--- + src/backends/native/meta-thread.c | 174 ++++++++++++++++++++++++++---- + src/backends/native/meta-thread.h | 6 ++ + 2 files changed, 157 insertions(+), 23 deletions(-) + +diff --git a/src/backends/native/meta-thread.c b/src/backends/native/meta-thread.c +index 93a84a8a5..f291c0b4a 100644 +--- a/src/backends/native/meta-thread.c ++++ b/src/backends/native/meta-thread.c +@@ -56,62 +56,65 @@ typedef struct _MetaThreadCallbackSource + + GMutex mutex; + GCond cond; + + MetaThread *thread; + GMainContext *main_context; + GList *callbacks; + gboolean needs_flush; + } MetaThreadCallbackSource; + + typedef struct _MetaThreadPrivate + { + MetaBackend *backend; + char *name; + + GMainContext *main_context; + + MetaThreadImpl *impl; + gboolean wants_realtime; + gboolean waiting_for_impl_task; + GSource *wrapper_source; + + GMutex callbacks_mutex; + GHashTable *callback_sources; + + MetaThreadType thread_type; + + GThread *main_thread; + + struct { ++ MetaDBusRealtimeKit1 *rtkit_proxy; + GThread *thread; ++ pid_t thread_id; + GMutex init_mutex; ++ int realtime_inhibit_count; + } kernel; + } MetaThreadPrivate; + + typedef struct _MetaThreadClassPrivate + { + GType impl_type; + } MetaThreadClassPrivate; + + static void initable_iface_init (GInitableIface *initable_iface); + + G_DEFINE_TYPE_WITH_CODE (MetaThread, meta_thread, G_TYPE_OBJECT, + G_ADD_PRIVATE (MetaThread) + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + initable_iface_init) + g_type_add_class_private (g_define_type_id, + sizeof (MetaThreadClassPrivate))) + + static void + meta_thread_callback_data_free (MetaThreadCallbackData *callback_data) + { + if (callback_data->user_data_destroy) + callback_data->user_data_destroy (callback_data->user_data); + g_free (callback_data); + } + + static void + meta_thread_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +@@ -175,181 +178,238 @@ get_rtkit_property (MetaDBusRealtimeKit1 *rtkit_proxy, + { + GDBusConnection *connection; + g_autoptr (GVariant) prop_value = NULL; + g_autoptr (GVariant) property_variant = NULL; + + /* The following is a fall back path for a RTKit daemon that doesn't support + * org.freedesktop.DBus.Properties.GetAll. See + * . + */ + connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (rtkit_proxy)); + prop_value = + g_dbus_connection_call_sync (connection, + "org.freedesktop.RealtimeKit1", + "/org/freedesktop/RealtimeKit1", + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", + "org.freedesktop.RealtimeKit1", + property_name), + G_VARIANT_TYPE ("(v)"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, error); + if (!prop_value) + return NULL; + + g_variant_get (prop_value, "(v)", &property_variant); + return g_steal_pointer (&property_variant); + } + + static gboolean +-request_realtime_scheduling (MetaThread *thread, +- GError **error) ++ensure_realtime_kit_proxy (MetaThread *thread, ++ GError **error) + { + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + g_autoptr (MetaDBusRealtimeKit1) rtkit_proxy = NULL; + g_autoptr (GError) local_error = NULL; +- int64_t rttime; +- struct rlimit rl; +- uint32_t priority; ++ ++ if (priv->kernel.rtkit_proxy) ++ return TRUE; + + rtkit_proxy = + meta_dbus_realtime_kit1_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + "org.freedesktop.RealtimeKit1", + "/org/freedesktop/RealtimeKit1", + NULL, + &local_error); + if (!rtkit_proxy) + { + g_dbus_error_strip_remote_error (local_error); + g_propagate_prefixed_error (error, g_steal_pointer (&local_error), + "Failed to acquire RTKit D-Bus proxy: "); + return FALSE; + } + +- priority = meta_dbus_realtime_kit1_get_max_realtime_priority (rtkit_proxy); ++ priv->kernel.rtkit_proxy = g_steal_pointer (&rtkit_proxy); ++ return TRUE; ++} ++ ++static gboolean ++request_realtime_scheduling (MetaThread *thread, ++ GError **error) ++{ ++ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); ++ g_autoptr (GError) local_error = NULL; ++ int64_t rttime; ++ struct rlimit rl; ++ uint32_t priority; ++ ++ if (!ensure_realtime_kit_proxy (thread, error)) ++ return FALSE; ++ ++ priority = meta_dbus_realtime_kit1_get_max_realtime_priority (priv->kernel.rtkit_proxy); + if (priority == 0) + { + g_autoptr (GVariant) priority_variant = NULL; + +- priority_variant = get_rtkit_property (rtkit_proxy, ++ priority_variant = get_rtkit_property (priv->kernel.rtkit_proxy, + "MaxRealtimePriority", + error); + if (!priority_variant) + return FALSE; + + priority = g_variant_get_int32 (priority_variant); + } + + if (priority == 0) + g_warning ("Maximum real time scheduling priority is 0"); + +- rttime = meta_dbus_realtime_kit1_get_rttime_usec_max (rtkit_proxy); ++ rttime = meta_dbus_realtime_kit1_get_rttime_usec_max (priv->kernel.rtkit_proxy); + if (rttime == 0) + { + g_autoptr (GVariant) rttime_variant = NULL; + +- rttime_variant = get_rtkit_property (rtkit_proxy, ++ rttime_variant = get_rtkit_property (priv->kernel.rtkit_proxy, + "RTTimeUSecMax", + error); + if (!rttime_variant) + return FALSE; + + rttime = g_variant_get_int64 (rttime_variant); + } + + meta_topic (META_DEBUG_BACKEND, + "Setting soft and hard RLIMIT_RTTIME limit to %lu", rttime); + rl.rlim_cur = rttime; + rl.rlim_max = rttime; + + if (setrlimit (RLIMIT_RTTIME, &rl) != 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), + "Failed to set RLIMIT_RTTIME: %s", g_strerror (errno)); + return FALSE; + } + + meta_topic (META_DEBUG_BACKEND, "Setting '%s' thread real time priority to %d", + priv->name, priority); +- if (!meta_dbus_realtime_kit1_call_make_thread_realtime_sync (rtkit_proxy, +- gettid (), ++ if (!meta_dbus_realtime_kit1_call_make_thread_realtime_sync (priv->kernel.rtkit_proxy, ++ priv->kernel.thread_id, + priority, + NULL, + &local_error)) + { + g_dbus_error_strip_remote_error (local_error); + g_propagate_error (error, g_steal_pointer (&local_error)); + return FALSE; + } + + return TRUE; + } + ++static gboolean ++request_normal_scheduling (MetaThread *thread, ++ GError **error) ++{ ++ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); ++ g_autoptr (GError) local_error = NULL; ++ ++ if (!ensure_realtime_kit_proxy (thread, error)) ++ return FALSE; ++ ++ meta_topic (META_DEBUG_BACKEND, "Setting '%s' thread to normal priority", priv->name); ++ if (!meta_dbus_realtime_kit1_call_make_thread_high_priority_sync (priv->kernel.rtkit_proxy, ++ priv->kernel.thread_id, ++ 0 /* "normal" nice value */, ++ NULL, ++ &local_error)) ++ { ++ g_dbus_error_strip_remote_error (local_error); ++ g_propagate_error (error, g_steal_pointer (&local_error)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++meta_thread_uses_realtime_scheduling (MetaThread *thread) ++{ ++ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); ++ gboolean uses_realtime_scheduling = FALSE; ++ ++ switch (priv->thread_type) ++ { ++ case META_THREAD_TYPE_USER: ++ break; ++ case META_THREAD_TYPE_KERNEL: ++ if (priv->kernel.realtime_inhibit_count == 0) ++ uses_realtime_scheduling = TRUE; ++ break; ++ } ++ ++ return uses_realtime_scheduling; ++} ++ + static gpointer + thread_impl_func (gpointer user_data) + { + MetaThread *thread = META_THREAD (user_data); + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + MetaThreadImpl *impl = priv->impl; + MetaThreadImplRunFlags run_flags = META_THREAD_IMPL_RUN_FLAG_NONE; + GMainContext *thread_context = meta_thread_impl_get_main_context (impl); + #ifdef HAVE_PROFILER + MetaContext *context = meta_backend_get_context (priv->backend); + MetaProfiler *profiler = meta_context_get_profiler (context); + #endif + + g_mutex_lock (&priv->kernel.init_mutex); + g_mutex_unlock (&priv->kernel.init_mutex); + + g_main_context_push_thread_default (thread_context); + + #ifdef HAVE_PROFILER + meta_profiler_register_thread (profiler, thread_context, priv->name); + #endif + ++ priv->kernel.thread_id = gettid (); ++ priv->kernel.realtime_inhibit_count = 1; ++ + if (priv->wants_realtime) +- { +- g_autoptr (GError) error = NULL; ++ meta_thread_uninhibit_realtime_in_impl (thread); + +- if (!request_realtime_scheduling (thread, &error)) +- { +- g_warning ("Failed to make thread '%s' realtime scheduled: %s", +- priv->name, error->message); +- } +- else +- { +- g_message ("Made thread '%s' realtime scheduled", priv->name); +- run_flags |= META_THREAD_IMPL_RUN_FLAG_REALTIME; +- } ++ if (meta_thread_uses_realtime_scheduling (thread)) ++ { ++ g_message ("Made thread '%s' realtime scheduled", priv->name); ++ run_flags |= META_THREAD_IMPL_RUN_FLAG_REALTIME; + } + + meta_thread_impl_run (impl, run_flags); + + #ifdef HAVE_PROFILER + meta_profiler_unregister_thread (profiler, thread_context); + #endif + + g_main_context_pop_thread_default (thread_context); + + return GINT_TO_POINTER (TRUE); + } + + typedef struct _WrapperSource + { + GSource base; + + GMainContext *thread_main_context; + + GPollFD fds[256]; + gpointer fd_tags[256]; + int n_fds; + int priority; + } WrapperSource; + + static gboolean + wrapper_source_prepare (GSource *source, + int *timeout) + { + WrapperSource *wrapper_source = (WrapperSource *) source; +@@ -522,60 +582,66 @@ meta_thread_initable_init (GInitable *initable, + + start_thread (thread); + + return TRUE; + } + + static void + initable_iface_init (GInitableIface *initable_iface) + { + initable_iface->init = meta_thread_initable_init; + } + + static void + finalize_thread_user (MetaThread *thread) + { + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + + meta_thread_impl_terminate (priv->impl); + while (meta_thread_impl_dispatch (priv->impl) > 0); + unwrap_main_context (thread, meta_thread_impl_get_main_context (priv->impl)); + } + + static void + finalize_thread_kernel (MetaThread *thread) + { + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + + meta_thread_impl_terminate (priv->impl); + g_thread_join (priv->kernel.thread); + priv->kernel.thread = NULL; ++ priv->kernel.thread_id = 0; ++ ++ priv->kernel.realtime_inhibit_count = -1; ++ ++ g_clear_object (&priv->kernel.rtkit_proxy); ++ + g_mutex_clear (&priv->kernel.init_mutex); + } + + static void + tear_down_thread (MetaThread *thread) + { + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + + switch (priv->thread_type) + { + case META_THREAD_TYPE_USER: + finalize_thread_user (thread); + break; + case META_THREAD_TYPE_KERNEL: + finalize_thread_kernel (thread); + break; + } + + meta_thread_flush_callbacks (thread); + } + + static void + meta_thread_finalize (GObject *object) + { + MetaThread *thread = META_THREAD (object); + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + + tear_down_thread (thread); + + meta_thread_unregister_callback_context (thread, priv->main_context); +@@ -618,60 +684,62 @@ meta_thread_class_init (MetaThreadClass *klass) + + obj_props[PROP_THREAD_TYPE] = + g_param_spec_enum ("thread-type", + "thread-type", + "Type of thread", + META_TYPE_THREAD_TYPE, + META_THREAD_TYPE_KERNEL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + obj_props[PROP_WANTS_REALTIME] = + g_param_spec_boolean ("wants-realtime", + "wants-realtime", + "Wants real-time thread scheduling", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, obj_props); + } + + static void + meta_thread_init (MetaThread *thread) + { + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + + g_mutex_init (&priv->callbacks_mutex); + priv->main_thread = g_thread_self (); ++ ++ priv->kernel.realtime_inhibit_count = -1; + } + + void + meta_thread_class_register_impl_type (MetaThreadClass *thread_class, + GType impl_type) + { + MetaThreadClassPrivate *class_priv = + G_TYPE_CLASS_GET_PRIVATE (thread_class, META_TYPE_THREAD, + MetaThreadClassPrivate); + + g_assert (class_priv->impl_type == G_TYPE_INVALID); + class_priv->impl_type = impl_type; + } + + void + meta_thread_reset_thread_type (MetaThread *thread, + MetaThreadType thread_type) + { + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + g_autoptr (GMainContext) thread_context = NULL; + + if (priv->thread_type == thread_type) + return; + + tear_down_thread (thread); + g_assert (!priv->wrapper_source); + + priv->thread_type = thread_type; + + start_thread (thread); +@@ -1134,30 +1202,90 @@ meta_thread_get_thread_type (MetaThread *thread) + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + + return priv->thread_type; + } + + GThread * + meta_thread_get_thread (MetaThread *thread) + { + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + + g_assert (priv->thread_type == META_THREAD_TYPE_KERNEL); + + return priv->kernel.thread; + } + + gboolean + meta_thread_is_in_impl_task (MetaThread *thread) + { + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + + return meta_thread_impl_is_in_impl (priv->impl); + } + + gboolean + meta_thread_is_waiting_for_impl_task (MetaThread *thread) + { + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + + return priv->waiting_for_impl_task; + } ++ ++void ++meta_thread_inhibit_realtime_in_impl (MetaThread *thread) ++{ ++ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); ++ g_autoptr (GError) error = NULL; ++ ++ switch (priv->thread_type) ++ { ++ case META_THREAD_TYPE_KERNEL: ++ priv->kernel.realtime_inhibit_count++; ++ ++ if (priv->kernel.realtime_inhibit_count == 1) ++ { ++ if (!request_normal_scheduling (thread, &error)) ++ { ++ g_warning ("Failed to make thread '%s' normally scheduled: %s", ++ priv->name, error->message); ++ priv->kernel.realtime_inhibit_count--; ++ } ++ else ++ { ++ meta_topic (META_DEBUG_BACKEND, "Made thread '%s' normally scheduled", priv->name); ++ } ++ } ++ break; ++ case META_THREAD_TYPE_USER: ++ break; ++ } ++} ++ ++void ++meta_thread_uninhibit_realtime_in_impl (MetaThread *thread) ++{ ++ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); ++ g_autoptr (GError) error = NULL; ++ ++ switch (priv->thread_type) ++ { ++ case META_THREAD_TYPE_KERNEL: ++ priv->kernel.realtime_inhibit_count--; ++ ++ if (priv->kernel.realtime_inhibit_count == 0) ++ { ++ if (!request_realtime_scheduling (thread, &error)) ++ { ++ g_warning ("Failed to make thread '%s' realtime scheduled: %s", ++ priv->name, error->message); ++ priv->kernel.realtime_inhibit_count++; ++ } ++ else ++ { ++ meta_topic (META_DEBUG_BACKEND, "Made thread '%s' realtime scheduled", priv->name); ++ } ++ } ++ break; ++ case META_THREAD_TYPE_USER: ++ break; ++ } ++} +diff --git a/src/backends/native/meta-thread.h b/src/backends/native/meta-thread.h +index 9fc3588b9..96b79b586 100644 +--- a/src/backends/native/meta-thread.h ++++ b/src/backends/native/meta-thread.h +@@ -71,36 +71,42 @@ void meta_thread_queue_callback (MetaThread *thread, + GDestroyNotify user_data_destroy); + + META_EXPORT_TEST + void meta_thread_flush_callbacks (MetaThread *thread); + + META_EXPORT_TEST + gpointer meta_thread_run_impl_task_sync (MetaThread *thread, + MetaThreadTaskFunc func, + gpointer user_data, + GError **error); + + META_EXPORT_TEST + void meta_thread_post_impl_task (MetaThread *thread, + MetaThreadTaskFunc func, + gpointer user_data, + GDestroyNotify user_data_destroy, + MetaThreadTaskFeedbackFunc feedback_func, + gpointer feedback_user_data); + + META_EXPORT_TEST + MetaBackend * meta_thread_get_backend (MetaThread *thread); + + META_EXPORT_TEST + const char * meta_thread_get_name (MetaThread *thread); + + META_EXPORT_TEST + gboolean meta_thread_is_in_impl_task (MetaThread *thread); + + gboolean meta_thread_is_waiting_for_impl_task (MetaThread *thread); + ++META_EXPORT_TEST ++void meta_thread_inhibit_realtime_in_impl (MetaThread *thread); ++ ++META_EXPORT_TEST ++void meta_thread_uninhibit_realtime_in_impl (MetaThread *thread); ++ + #define meta_assert_in_thread_impl(thread) \ + g_assert (meta_thread_is_in_impl_task (thread)) + #define meta_assert_not_in_thread_impl(thread) \ + g_assert (!meta_thread_is_in_impl_task (thread)) + #define meta_assert_is_waiting_for_thread_impl_task(thread) \ + g_assert (meta_thread_is_waiting_for_impl_task (thread)) +-- +2.41.0 + diff --git a/0004-kms-impl-device-Inhibit-real-time-scheduling-when-mo.patch b/0004-kms-impl-device-Inhibit-real-time-scheduling-when-mo.patch new file mode 100644 index 0000000..6ef06d8 --- /dev/null +++ b/0004-kms-impl-device-Inhibit-real-time-scheduling-when-mo.patch @@ -0,0 +1,183 @@ +From 99be4ed7c3be19969930ce826f48c444e5e9da41 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 10 Oct 2023 14:48:55 -0400 +Subject: [PATCH 4/4] kms/impl-device: Inhibit real-time scheduling when mode + setting + +Certain kernel drivers can take an unreasonably long time to +complete mode setting operations. That excessive CPU time is charged +to the process's rlimits which can lead to the process getting killed +if the thread is a real-time thread. + +This commit inhibits real-time scheduling around mode setting +commits, since those commits are the ones currently presenting as +excessively slow. + +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3037 +--- + src/backends/native/meta-kms-impl-device.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c +index da372383d..d53552704 100644 +--- a/src/backends/native/meta-kms-impl-device.c ++++ b/src/backends/native/meta-kms-impl-device.c +@@ -1554,141 +1554,154 @@ meta_kms_impl_device_schedule_process (MetaKmsImplDevice *impl_device, + + if (crtc_frame->await_flush) + return; + + if (!is_using_deadline_timer (impl_device)) + goto needs_flush; + + if (crtc_frame->pending_page_flip) + return; + + if (ensure_deadline_timer_armed (impl_device, crtc_frame, &error)) + return; + + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + g_warning ("Failed to determine deadline: %s", error->message); + + priv = meta_kms_impl_device_get_instance_private (impl_device); + priv->deadline_timer_inhibited = TRUE; + + needs_flush: + meta_kms_device_set_needs_flush (meta_kms_crtc_get_device (crtc), crtc); + } + + static MetaKmsFeedback * + process_mode_set_update (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags) + { + MetaKmsImplDevicePrivate *priv = + meta_kms_impl_device_get_instance_private (impl_device); ++ MetaKmsImpl *kms_impl = meta_kms_impl_device_get_impl (impl_device); ++ MetaThreadImpl *thread_impl = META_THREAD_IMPL (kms_impl); ++ MetaThread *thread = meta_thread_impl_get_thread (thread_impl); ++ MetaKmsFeedback *feedback; + CrtcFrame *crtc_frame; + GList *l; + GHashTableIter iter; + + for (l = meta_kms_update_get_mode_sets (update); l; l = l->next) + { + MetaKmsModeSet *mode_set = l->data; + MetaKmsCrtc *crtc = mode_set->crtc; + + crtc_frame = get_crtc_frame (impl_device, crtc); + if (!crtc_frame) + continue; + + if (!crtc_frame->pending_update) + continue; + + meta_kms_update_merge_from (update, crtc_frame->pending_update); + g_clear_pointer (&crtc_frame->pending_update, meta_kms_update_free); + } + + g_hash_table_iter_init (&iter, priv->crtc_frames); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &crtc_frame)) + { + crtc_frame->deadline.is_deadline_page_flip = FALSE; + crtc_frame->await_flush = FALSE; + crtc_frame->pending_page_flip = FALSE; + g_clear_pointer (&crtc_frame->pending_update, meta_kms_update_free); + disarm_crtc_frame_deadline_timer (crtc_frame); + } + +- return do_process (impl_device, NULL, update, flags); ++ meta_thread_inhibit_realtime_in_impl (thread); ++ feedback = do_process (impl_device, NULL, update, flags); ++ meta_thread_uninhibit_realtime_in_impl (thread); ++ ++ return feedback; + } + + MetaKmsFeedback * + meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags) + { + g_autoptr (GError) error = NULL; + + if (!ensure_device_file (impl_device, &error)) + { + MetaKmsFeedback *feedback = NULL; + + feedback = meta_kms_feedback_new_failed (NULL, g_steal_pointer (&error)); + queue_result_feedback (impl_device, update, feedback); + + meta_kms_update_free (update); + return feedback; + } + + meta_kms_update_realize (update, impl_device); + + if (flags & META_KMS_UPDATE_FLAG_TEST_ONLY) + { + return do_process (impl_device, + meta_kms_update_get_latch_crtc (update), + update, flags); + } + else if (flags & META_KMS_UPDATE_FLAG_MODE_SET) + { + return process_mode_set_update (impl_device, update, flags); + } + else + { + g_assert_not_reached (); + } + } + + void + meta_kms_impl_device_disable (MetaKmsImplDevice *impl_device) + { + MetaKmsImplDevicePrivate *priv = + meta_kms_impl_device_get_instance_private (impl_device); ++ MetaKmsImpl *kms_impl = meta_kms_impl_device_get_impl (impl_device); ++ MetaThreadImpl *thread_impl = META_THREAD_IMPL (kms_impl); ++ MetaThread *thread = meta_thread_impl_get_thread (thread_impl); + MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); + + if (!priv->device_file) + return; + + meta_kms_impl_device_hold_fd (impl_device); ++ meta_thread_inhibit_realtime_in_impl (thread); + klass->disable (impl_device); ++ meta_thread_uninhibit_realtime_in_impl (thread); + g_list_foreach (priv->crtcs, + (GFunc) meta_kms_crtc_disable_in_impl, NULL); + g_list_foreach (priv->connectors, + (GFunc) meta_kms_connector_disable_in_impl, NULL); + meta_kms_impl_device_unhold_fd (impl_device); + } + + void + meta_kms_impl_device_handle_page_flip_callback (MetaKmsImplDevice *impl_device, + MetaKmsPageFlipData *page_flip_data) + { + MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); + + klass->handle_page_flip_callback (impl_device, page_flip_data); + } + + void + meta_kms_impl_device_discard_pending_page_flips (MetaKmsImplDevice *impl_device) + { + MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); + + klass->discard_pending_page_flips (impl_device); + } + + void + meta_kms_impl_device_hold_fd (MetaKmsImplDevice *impl_device) + { + MetaKmsImplDevicePrivate *priv = + meta_kms_impl_device_get_instance_private (impl_device); + MetaKms *kms = meta_kms_device_get_kms (priv->device); +-- +2.41.0 + diff --git a/mutter.spec b/mutter.spec index 0583d9d..990bd84 100644 --- a/mutter.spec +++ b/mutter.spec @@ -48,6 +48,12 @@ Patch: 3306.patch # to fix *both* problems Patch: 0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3324 +Patch: 0001-thread-Fix-preexisting-uncrustify-problem.patch +Patch: 0002-thread-For-consistency-s-real_time-realtime.patch +Patch: 0003-thread-Allow-turnning-off-rt-scheduling-for-running-.patch +Patch: 0004-kms-impl-device-Inhibit-real-time-scheduling-when-mo.patch + BuildRequires: pkgconfig(gobject-introspection-1.0) >= 1.41.0 BuildRequires: pkgconfig(polkit-gobject-1) BuildRequires: pkgconfig(sm)