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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4124>
This commit is contained in:
Jonas Ådahl 2024-11-07 16:16:00 +01:00 committed by Marge Bot
parent 1a6c0ea3d0
commit 3e024ae2d3
6 changed files with 173 additions and 15 deletions

View File

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

View File

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

View File

@ -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))
{

View File

@ -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 ())

View File

@ -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

View File

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