From 3e024ae2d30699b948dad81c03b73603d74dee31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 7 Nov 2024 16:16:00 +0100 Subject: [PATCH] thread: Add support for requesting high priority scheduling In contrast to realtime scheduling, this doesn't risk us getting SIGKILL:ed when the kernel is doing busy looping in drmModeAtomicCommit() for some reason, but will according to testing, right now, give us more or less the same benefit when it comes to dispatch lateness and commit lateness. Part-of: --- src/backends/native/meta-kms-impl-device.c | 1 + src/backends/native/meta-kms.c | 3 + src/backends/native/meta-thread.c | 82 +++++++++++++++++++++ src/backends/native/meta-thread.h | 1 + src/tests/dbusmock-templates/rtkit.py | 17 ++++- src/tests/native-thread.c | 84 ++++++++++++++++++---- 6 files changed, 173 insertions(+), 15 deletions(-) diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c index f743b8631..6c4e3b4b2 100644 --- a/src/backends/native/meta-kms-impl-device.c +++ b/src/backends/native/meta-kms-impl-device.c @@ -1701,6 +1701,7 @@ is_using_deadline_timer (MetaKmsImplDevice *impl_device) case META_SCHEDULING_PRIORITY_NORMAL: return FALSE; case META_SCHEDULING_PRIORITY_REALTIME: + case META_SCHEDULING_PRIORITY_HIGH_PRIORITY: return TRUE; } diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c index b22de8c29..fb6da3a4f 100644 --- a/src/backends/native/meta-kms.c +++ b/src/backends/native/meta-kms.c @@ -409,6 +409,9 @@ meta_kms_new (MetaBackend *backend, else if (g_strcmp0 (preferred_scheduling_priority_string, "realtime") == 0) preferred_scheduling_priority = META_SCHEDULING_PRIORITY_REALTIME; + else if (g_strcmp0 (preferred_scheduling_priority_string, + "high-priority") == 0) + preferred_scheduling_priority = META_SCHEDULING_PRIORITY_HIGH_PRIORITY; else g_assert_not_reached (); } diff --git a/src/backends/native/meta-thread.c b/src/backends/native/meta-thread.c index f0edf87c5..152206cfe 100644 --- a/src/backends/native/meta-thread.c +++ b/src/backends/native/meta-thread.c @@ -115,6 +115,8 @@ meta_scheduling_priority_to_string (MetaSchedulingPriority priority) return "normal"; case META_SCHEDULING_PRIORITY_REALTIME: return "realtime"; + case META_SCHEDULING_PRIORITY_HIGH_PRIORITY: + return "high priority"; } g_assert_not_reached (); @@ -249,6 +251,54 @@ ensure_realtime_kit_proxy (MetaThread *thread, return TRUE; } +static gboolean +request_high_priority_scheduling (MetaThread *thread, + GError **error) +{ + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + g_autoptr (GError) local_error = NULL; + uint32_t nice_level; + + if (!ensure_realtime_kit_proxy (thread, error)) + return FALSE; + + nice_level = meta_dbus_realtime_kit1_get_min_nice_level (priv->kernel.rtkit_proxy); + if (nice_level == 0) + { + g_autoptr (GVariant) nice_level_variant = NULL; + + nice_level_variant = get_rtkit_property (priv->kernel.rtkit_proxy, + "MinNiceLevel", + error); + if (!nice_level_variant) + return FALSE; + + nice_level = g_variant_get_int32 (nice_level_variant); + } + + if (nice_level == 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Minimum real time scheduling nice_level is 0"); + return FALSE; + } + + meta_topic (META_DEBUG_BACKEND, "Setting '%s' thread nice level to %d", + priv->name, nice_level); + if (!meta_dbus_realtime_kit1_call_make_thread_high_priority_sync (priv->kernel.rtkit_proxy, + priv->kernel.thread_id, + nice_level, + 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_realtime_scheduling (MetaThread *thread, GError **error) @@ -372,6 +422,23 @@ should_use_realtime_scheduling_in_impl (MetaThread *thread) priv->kernel.realtime_inhibit_count == 0); } +static gboolean +should_use_high_priority_scheduling_in_impl (MetaThread *thread) +{ + MetaThreadPrivate *priv = meta_thread_get_instance_private (thread); + + switch (priv->thread_type) + { + case META_THREAD_TYPE_USER: + return FALSE; + case META_THREAD_TYPE_KERNEL: + return (priv->preferred_scheduling_priority == + META_SCHEDULING_PRIORITY_HIGH_PRIORITY); + } + + g_assert_not_reached (); +} + static void sync_scheduling_priority_in_impl (MetaThread *thread) { @@ -381,6 +448,8 @@ sync_scheduling_priority_in_impl (MetaThread *thread) if (should_use_realtime_scheduling_in_impl (thread)) scheduling_priority = META_SCHEDULING_PRIORITY_REALTIME; + else if (should_use_high_priority_scheduling_in_impl (thread)) + scheduling_priority = META_SCHEDULING_PRIORITY_HIGH_PRIORITY; else scheduling_priority = META_SCHEDULING_PRIORITY_NORMAL; @@ -402,6 +471,19 @@ sync_scheduling_priority_in_impl (MetaThread *thread) priv->kernel.scheduling_priority = scheduling_priority; } break; + case META_SCHEDULING_PRIORITY_HIGH_PRIORITY: + if (!request_high_priority_scheduling (thread, &error)) + { + g_warning ("Failed to make thread '%s' high priority scheduled: %s", + priv->name, error->message); + } + else + { + meta_topic (META_DEBUG_BACKEND, + "Made thread '%s' high priority scheduled", priv->name); + priv->kernel.scheduling_priority = scheduling_priority; + } + break; case META_SCHEDULING_PRIORITY_NORMAL: if (!request_normal_scheduling (thread, &error)) { diff --git a/src/backends/native/meta-thread.h b/src/backends/native/meta-thread.h index b18e83783..5ea948e3a 100644 --- a/src/backends/native/meta-thread.h +++ b/src/backends/native/meta-thread.h @@ -34,6 +34,7 @@ typedef enum _MetaSchedulingPriority { META_SCHEDULING_PRIORITY_NORMAL, META_SCHEDULING_PRIORITY_REALTIME, + META_SCHEDULING_PRIORITY_HIGH_PRIORITY, } MetaSchedulingPriority; #define META_TYPE_THREAD (meta_thread_get_type ()) diff --git a/src/tests/dbusmock-templates/rtkit.py b/src/tests/dbusmock-templates/rtkit.py index 766bd681e..bc70b039e 100644 --- a/src/tests/dbusmock-templates/rtkit.py +++ b/src/tests/dbusmock-templates/rtkit.py @@ -21,18 +21,24 @@ def load(mock, parameters): mock.AddProperty(MAIN_IFACE, 'MaxRealtimePriority', dbus.Int32(20)) mock.AddProperty(MAIN_IFACE, 'MinNiceLevel', dbus.Int32(-15)) mock.priorities = dict() + mock.nice_levels = dict() @dbus.service.method(MAIN_IFACE, in_signature='tu') def MakeThreadRealtime(self, thread, priority): self.priorities[thread] = priority + if thread in self.nice_levels: + del self.nice_levels[thread] -@dbus.service.method(MAIN_IFACE, in_signature='tu') +@dbus.service.method(MAIN_IFACE, in_signature='ti') def MakeThreadHighPriority(self, thread, priority): - self.priorities[thread] = priority + self.nice_levels[thread] = priority + if thread in self.priorities: + del self.priorities[thread] @dbus.service.method(MOCK_IFACE) def Reset(self): self.priorities = dict() + self.nice_levels = dict() @dbus.service.method(MOCK_IFACE, in_signature='t', out_signature='u') def GetThreadPriority(self, thread): @@ -40,3 +46,10 @@ def GetThreadPriority(self, thread): return self.priorities[thread] else: return 0 + +@dbus.service.method(MOCK_IFACE, in_signature='t', out_signature='i') +def GetThreadNiceLevel(self, thread): + if thread in self.nice_levels: + return self.nice_levels[thread] + else: + return 0 diff --git a/src/tests/native-thread.c b/src/tests/native-thread.c index ed2b8c78b..9dfe57c50 100644 --- a/src/tests/native-thread.c +++ b/src/tests/native-thread.c @@ -1141,23 +1141,41 @@ call_rtkit_mock_method (const char *method, return ret; } +static void +assert_thread_levels (uint32_t expected_priority, + int32_t expected_nice_level) +{ + g_autoptr (GVariant) priority_variant = NULL; + g_autoptr (GVariant) nice_level_variant = NULL; + uint32_t priority = UINT32_MAX; + int32_t nice_level = INT32_MAX; + + priority_variant = + call_rtkit_mock_method ("GetThreadPriority", + g_variant_new ("(t)", gettid ())); + + g_variant_get (priority_variant, "(u)", &priority); + g_assert_cmpint (priority, ==, expected_priority); + + nice_level_variant = + call_rtkit_mock_method ("GetThreadNiceLevel", + g_variant_new ("(t)", gettid ())); + + g_variant_get (nice_level_variant, "(i)", &nice_level); + g_assert_cmpint (nice_level, ==, expected_nice_level); +} + static gpointer assert_realtime (MetaThreadImpl *thread_impl, gpointer user_data, GError **error) { - g_autoptr (GVariant) ret = NULL; - uint32_t priority = 0; g_assert_cmpint (meta_thread_impl_get_scheduling_priority (thread_impl), ==, META_SCHEDULING_PRIORITY_REALTIME); - ret = call_rtkit_mock_method ("GetThreadPriority", - g_variant_new ("(t)", gettid ())); - - g_variant_get (ret, "(u)", &priority); - g_assert_cmpint (priority, ==, 20); + assert_thread_levels (20, 0); return NULL; } @@ -1191,23 +1209,61 @@ meta_test_thread_realtime (void) g_assert_null (test_thread); } +static gpointer +assert_high_priority (MetaThreadImpl *thread_impl, + gpointer user_data, + GError **error) +{ + g_assert_cmpint (meta_thread_impl_get_scheduling_priority (thread_impl), + ==, + META_SCHEDULING_PRIORITY_HIGH_PRIORITY); + + assert_thread_levels (0, -15); + + return NULL; +} + +static void +meta_test_thread_high_priority (void) +{ + MetaBackend *backend = meta_context_get_backend (test_context); + MetaThread *thread; + g_autoptr (GError) error = NULL; + g_autoptr (GVariant) ret = NULL; + + ret = call_rtkit_mock_method ("Reset", NULL); + + thread = g_initable_new (META_TYPE_THREAD_TEST, + NULL, &error, + "backend", backend, + "name", "test realtime", + "thread-type", META_THREAD_TYPE_KERNEL, + "preferred-scheduling-priority", META_SCHEDULING_PRIORITY_HIGH_PRIORITY, + NULL); + g_object_add_weak_pointer (G_OBJECT (thread), (gpointer *) &thread); + g_assert_nonnull (thread); + g_assert_null (error); + + meta_thread_post_impl_task (thread, assert_high_priority, NULL, NULL, + NULL, NULL); + + g_object_unref (thread); + g_assert_null (thread); + g_assert_null (test_thread); +} + static gpointer assert_no_realtime (MetaThreadImpl *thread_impl, gpointer user_data, GError **error) { g_autoptr (GVariant) ret = NULL; - uint32_t priority = UINT32_MAX; g_assert_cmpint (meta_thread_impl_get_scheduling_priority (thread_impl), ==, META_SCHEDULING_PRIORITY_NORMAL); - ret = call_rtkit_mock_method ("GetThreadPriority", - g_variant_new ("(t)", gettid ())); - - g_variant_get (ret, "(u)", &priority); - g_assert_cmpint (priority, ==, 0); + assert_thread_levels (0, 0); return NULL; } @@ -1260,6 +1316,8 @@ init_tests (void) meta_test_thread_change_thread_type); g_test_add_func ("/backends/native/thread/realtime", meta_test_thread_realtime); + g_test_add_func ("/backends/native/thread/high-priority", + meta_test_thread_high_priority); g_test_add_func ("/backends/native/thread/no-realtime", meta_test_thread_no_realtime); }