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); }