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:
parent
1a6c0ea3d0
commit
3e024ae2d3
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 ();
|
||||
}
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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 ())
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user