core: support setting realtime priority using rtkit

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-01-28 20:19:20 +00:00
parent b99c7db73e
commit 7bbf316a7d
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
4 changed files with 277 additions and 9 deletions

View File

@ -66,7 +66,7 @@ endif
if get_option('dbus')
cflags += ['-DCONFIG_DBUS']
deps += [dependency('dbus-1', required: true)]
srcs += [ 'dbus.c' ]
srcs += [ 'dbus.c', 'rtkit.c' ]
endif
if get_option('xrescheck')

View File

@ -68,6 +68,7 @@
#include "file_watch.h"
#include "list.h"
#include "options.h"
#include "rtkit.h"
#include "statistics.h"
#include "uthash_extra.h"
#include "vblank.h"
@ -2613,12 +2614,17 @@ err:
void set_rr_scheduling(void) {
int priority = sched_get_priority_min(SCHED_RR);
int ret;
if (rtkit_make_realtime(0, priority)) {
log_info("Set realtime priority to %d with rtkit.", priority);
return;
}
// Fallback to use pthread_setschedparam
struct sched_param param;
int old_policy;
ret = pthread_getschedparam(pthread_self(), &old_policy, &param);
int ret = pthread_getschedparam(pthread_self(), &old_policy, &param);
if (ret != 0) {
log_debug("Failed to get old scheduling priority");
log_info("Couldn't get old scheduling priority.");
return;
}
@ -2626,14 +2632,11 @@ void set_rr_scheduling(void) {
ret = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
if (ret != 0) {
log_info("Failed to set real-time scheduling priority to %d. Consider "
"giving picom the CAP_SYS_NICE capability or equivalent "
"support.",
priority);
log_info("Couldn't set real-time scheduling priority to %d.", priority);
return;
}
log_info("Set real-time scheduling priority to %d", priority);
log_info("Set real-time scheduling priority to %d.", priority);
}
/**

245
src/rtkit.c Normal file
View File

@ -0,0 +1,245 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright 2009 Lennart Poettering
// Copyright 2010 David Henningsson <diwic@ubuntu.com>
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
// Imported from https://github.com/heftig/rtkit/blob/master/rtkit.c
#include <errno.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <unistd.h>
#if defined(__linux__)
#include <sys/syscall.h>
#elif defined(__NetBSD__)
#include <lwp.h>
#elif defined(__FreeBSD__)
#include <sys/thr.h>
#elif defined(__DragonFly__)
#include <sys/lwp.h>
#endif
#include "log.h"
#include "rtkit.h"
#include "utils.h"
#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1"
#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1"
#define RTKIT_INTERFACE "org.freedesktop.RealtimeKit1"
static inline long compat_gettid(void) {
long ret = -1;
#if defined(__linux__)
ret = (pid_t)syscall(SYS_gettid);
#elif defined(__NetBSD__)
ret = _lwp_self();
#elif defined(__FreeBSD__)
long lwpid;
thr_self(&lwpid);
ret = lwpid;
#elif defined(__DragonFly__)
ret = lwp_gettid();
#endif
return ret;
}
static bool
rtkit_get_int_property(DBusConnection *connection, const char *propname, long long *propval) {
DBusMessage *m = NULL, *r = NULL;
DBusMessageIter iter, subiter;
dbus_int64_t i64;
dbus_int32_t i32;
DBusError error;
int current_type;
int ret = 0;
const char *interfacestr = RTKIT_INTERFACE;
dbus_error_init(&error);
m = dbus_message_new_method_call(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH,
"org.freedesktop.DBus.Properties", "Get");
if (!m) {
ret = -ENOMEM;
goto finish;
}
if (!dbus_message_append_args(m, DBUS_TYPE_STRING, &interfacestr,
DBUS_TYPE_STRING, &propname, DBUS_TYPE_INVALID)) {
ret = -ENOMEM;
goto finish;
}
r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error);
if (!r) {
goto finish;
}
if (dbus_set_error_from_message(&error, r)) {
goto finish;
}
ret = -EBADMSG;
dbus_message_iter_init(r, &iter);
while ((current_type = dbus_message_iter_get_arg_type(&iter)) != DBUS_TYPE_INVALID) {
if (current_type == DBUS_TYPE_VARIANT) {
dbus_message_iter_recurse(&iter, &subiter);
while ((current_type = dbus_message_iter_get_arg_type(&subiter)) !=
DBUS_TYPE_INVALID) {
if (current_type == DBUS_TYPE_INT32) {
dbus_message_iter_get_basic(&subiter, &i32);
*propval = i32;
ret = 0;
}
if (current_type == DBUS_TYPE_INT64) {
dbus_message_iter_get_basic(&subiter, &i64);
*propval = i64;
ret = 0;
}
dbus_message_iter_next(&subiter);
}
}
dbus_message_iter_next(&iter);
}
finish:
if (m) {
dbus_message_unref(m);
}
if (r) {
dbus_message_unref(r);
}
if (dbus_error_is_set(&error)) {
log_debug("Couldn't get property %s from rtkit: (dbus) %s", propname,
error.message);
dbus_error_free(&error);
return false;
}
if (ret != 0) {
log_debug("Couldn't get property %s from rtkit: %s", propname, strerror(-ret));
return false;
}
return true;
}
static bool rtkit_get_rttime_usec_max(DBusConnection *connection, long long *retval) {
return rtkit_get_int_property(connection, "RTTimeUSecMax", retval);
}
static inline void free_dbus_connection(DBusConnection **connection) {
if (*connection) {
dbus_connection_close(*connection);
dbus_connection_unref(*connection);
*connection = NULL;
}
}
static inline void free_dbus_message(DBusMessage **message) {
if (*message) {
dbus_message_unref(*message);
*message = NULL;
}
}
bool rtkit_make_realtime(long thread, int priority) {
cleanup(free_dbus_message) DBusMessage *m = NULL;
cleanup(free_dbus_message) DBusMessage *r = NULL;
dbus_uint64_t u64;
dbus_uint32_t u32;
DBusError error;
int ret = 0;
long long rttime_usec_max = 0;
bool succeeded = true;
dbus_error_init(&error);
cleanup(free_dbus_connection) DBusConnection *connection =
dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
if (dbus_error_is_set(&error)) {
log_info("Couldn't get system bus: %s", error.message);
dbus_error_free(&error);
return false;
}
dbus_connection_set_exit_on_disconnect(connection, false);
if (thread == 0) {
thread = compat_gettid();
}
if (!rtkit_get_rttime_usec_max(connection, &rttime_usec_max)) {
log_debug("Couldn't get RTTimeUSecMax from rtkit.");
return false;
}
if (rttime_usec_max <= 0) {
log_debug("Unreasonable RTTimeUSecMax from rtkit: %lld", rttime_usec_max);
return false;
}
#if defined(RLIMIT_RTTIME)
struct rlimit old_rlim, new_rlim;
// For security reasons, rtkit requires us to set RLIMIT_RTTIME before it will
// give us realtime priority.
if (getrlimit(RLIMIT_RTTIME, &old_rlim) != 0) {
log_debug("Couldn't get RLIMIT_RTTIME.");
return false;
}
new_rlim = old_rlim;
new_rlim.rlim_cur = min3(new_rlim.rlim_max, (rlim_t)rttime_usec_max, 100000); // 100ms
new_rlim.rlim_max = new_rlim.rlim_cur;
if (setrlimit(RLIMIT_RTTIME, &new_rlim) != 0) {
log_debug("Couldn't set RLIMIT_RTTIME.");
return false;
}
#endif
m = dbus_message_new_method_call(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH,
RTKIT_INTERFACE, "MakeThreadRealtime");
if (!m) {
ret = -ENOMEM;
goto finish;
}
u64 = (dbus_uint64_t)thread;
u32 = (dbus_uint32_t)priority;
if (!dbus_message_append_args(m, DBUS_TYPE_UINT64, &u64, DBUS_TYPE_UINT32, &u32,
DBUS_TYPE_INVALID)) {
ret = -ENOMEM;
goto finish;
}
r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error);
if (!r) {
goto finish;
}
if (dbus_set_error_from_message(&error, r)) {
goto finish;
}
ret = 0;
finish:
if (dbus_error_is_set(&error)) {
log_info("Couldn't make thread realtime with rtkit: (dbus) %s", error.message);
dbus_error_free(&error);
succeeded = false;
} else if (ret != 0) {
log_info("Couldn't make thread realtime with rtkit: %s", strerror(-ret));
succeeded = false;
}
#if defined(RLIMIT_RTTIME)
if (!succeeded) {
// Restore RLIMIT_RTTIME
setrlimit(RLIMIT_RTTIME, &old_rlim);
}
#endif
return succeeded;
}

20
src/rtkit.h Normal file
View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <stdbool.h>
#include <sys/types.h>
#ifdef CONFIG_DBUS
#include <dbus/dbus.h>
bool rtkit_make_realtime(long thread, int priority);
#else
static inline bool rtkit_make_realtime(pid_t thread attr_unused, int priority attr_unused) {
return false;
}
#endif