316 lines
8.3 KiB
C++
316 lines
8.3 KiB
C++
// SPDX-License-Identifier: BSD-2-Clause
|
|
// author: Max Kellermann <max.kellermann@gmail.com>
|
|
|
|
#include "Util.hxx"
|
|
#include "system/Error.hxx"
|
|
|
|
#ifdef __linux__
|
|
#include <sched.h>
|
|
#include <sys/syscall.h>
|
|
#include <unistd.h>
|
|
#include <dbus/dbus.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
#elif defined(_WIN32)
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
|
|
#ifdef __linux__
|
|
const char* RTKIT_SERVICE_NAME = "org.freedesktop.RealtimeKit1";
|
|
const char* RTKIT_OBJECT_PATH = "/org/freedesktop/RealtimeKit1";
|
|
|
|
// static int
|
|
// rtkit_get_max_realtime_priority(DBusConnection &connection)
|
|
// {
|
|
// // TODO: Implement this function
|
|
// DBusError error;
|
|
// }
|
|
|
|
// static void
|
|
// rtkit_make_realtime(DBusConnection &connection, pid_t thread, int priority)
|
|
// {
|
|
// // TODO: Implement this function
|
|
|
|
// }
|
|
#endif
|
|
|
|
// TODO: minimize and drop
|
|
#ifdef __linux__
|
|
|
|
#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1"
|
|
#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1"
|
|
|
|
static pid_t _gettid(void) {
|
|
return (pid_t) syscall(SYS_gettid);
|
|
}
|
|
|
|
static int translate_error(const char *name) {
|
|
if (strcmp(name, DBUS_ERROR_NO_MEMORY) == 0)
|
|
return -ENOMEM;
|
|
if (strcmp(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 ||
|
|
strcmp(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0)
|
|
return -ENOENT;
|
|
if (strcmp(name, DBUS_ERROR_ACCESS_DENIED) == 0 ||
|
|
strcmp(name, DBUS_ERROR_AUTH_FAILED) == 0)
|
|
return -EACCES;
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static long long 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;
|
|
long long ret;
|
|
const char * interfacestr = "org.freedesktop.RealtimeKit1";
|
|
|
|
dbus_error_init(&error);
|
|
|
|
if (!(m = dbus_message_new_method_call(
|
|
RTKIT_SERVICE_NAME,
|
|
RTKIT_OBJECT_PATH,
|
|
"org.freedesktop.DBus.Properties",
|
|
"Get"))) {
|
|
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;
|
|
}
|
|
|
|
if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
|
|
ret = translate_error(error.name);
|
|
goto finish;
|
|
}
|
|
|
|
if (dbus_set_error_from_message(&error, r)) {
|
|
ret = translate_error(error.name);
|
|
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);
|
|
|
|
dbus_error_free(&error);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rtkit_get_max_realtime_priority(DBusConnection *connection) {
|
|
long long retval;
|
|
int err;
|
|
|
|
err = rtkit_get_int_property(connection, "MaxRealtimePriority", &retval);
|
|
return err < 0 ? err : retval;
|
|
}
|
|
|
|
static int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) {
|
|
DBusMessage *m = NULL, *r = NULL;
|
|
dbus_uint64_t u64;
|
|
dbus_uint32_t u32;
|
|
DBusError error;
|
|
int ret;
|
|
|
|
dbus_error_init(&error);
|
|
|
|
if (thread == 0)
|
|
thread = _gettid();
|
|
|
|
if (!(m = dbus_message_new_method_call(
|
|
RTKIT_SERVICE_NAME,
|
|
RTKIT_OBJECT_PATH,
|
|
"org.freedesktop.RealtimeKit1",
|
|
"MakeThreadRealtime"))) {
|
|
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;
|
|
}
|
|
|
|
if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
|
|
ret = translate_error(error.name);
|
|
goto finish;
|
|
}
|
|
|
|
|
|
if (dbus_set_error_from_message(&error, r)) {
|
|
ret = translate_error(error.name);
|
|
goto finish;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
finish:
|
|
|
|
if (m)
|
|
dbus_message_unref(m);
|
|
|
|
if (r)
|
|
dbus_message_unref(r);
|
|
|
|
dbus_error_free(&error);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
|
|
#ifndef ANDROID
|
|
|
|
static int
|
|
linux_ioprio_set(int which, int who, int ioprio) noexcept
|
|
{
|
|
return syscall(__NR_ioprio_set, which, who, ioprio);
|
|
}
|
|
|
|
static void
|
|
ioprio_set_idle() noexcept
|
|
{
|
|
static constexpr int _IOPRIO_WHO_PROCESS = 1;
|
|
static constexpr int _IOPRIO_CLASS_IDLE = 3;
|
|
static constexpr int _IOPRIO_CLASS_SHIFT = 13;
|
|
static constexpr int _IOPRIO_IDLE =
|
|
(_IOPRIO_CLASS_IDLE << _IOPRIO_CLASS_SHIFT) | 7;
|
|
|
|
linux_ioprio_set(_IOPRIO_WHO_PROCESS, 0, _IOPRIO_IDLE);
|
|
}
|
|
|
|
#endif /* !ANDROID */
|
|
|
|
/**
|
|
* Wrapper for the "sched_setscheduler" system call. We don't use the
|
|
* one from the C library because Musl has an intentionally broken
|
|
* implementation.
|
|
*/
|
|
static int
|
|
linux_sched_setscheduler(pid_t pid, int sched,
|
|
const struct sched_param *param) noexcept
|
|
{
|
|
return syscall(__NR_sched_setscheduler, pid, sched, param);
|
|
}
|
|
|
|
#endif
|
|
|
|
void
|
|
SetThreadIdlePriority() noexcept
|
|
{
|
|
#ifdef __linux__
|
|
#ifdef SCHED_IDLE
|
|
static struct sched_param sched_param;
|
|
linux_sched_setscheduler(0, SCHED_IDLE, &sched_param);
|
|
#endif
|
|
|
|
#ifndef ANDROID
|
|
/* this system call is forbidden via seccomp on Android 8 and
|
|
leads to crash (SIGSYS) */
|
|
ioprio_set_idle();
|
|
#endif
|
|
|
|
#elif defined(_WIN32)
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
SetThreadRealtime()
|
|
{
|
|
#ifdef __linux__
|
|
struct sched_param sched_param;
|
|
sched_param.sched_priority = 40;
|
|
|
|
int policy = SCHED_FIFO;
|
|
#ifdef SCHED_RESET_ON_FORK
|
|
policy |= SCHED_RESET_ON_FORK;
|
|
#endif
|
|
|
|
int res = linux_sched_setscheduler(0, policy, &sched_param);
|
|
|
|
if (res < 0 && errno == EPERM) {
|
|
DBusError error;
|
|
DBusConnection *system_bus;
|
|
|
|
if (!(system_bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
|
|
throw MakeErrno("could not connect to dbus");
|
|
}
|
|
|
|
int max_realtime_priority;
|
|
if ((max_realtime_priority = rtkit_get_max_realtime_priority(system_bus)) < 0) {
|
|
dbus_connection_unref(system_bus);
|
|
throw MakeErrno("could not retrieve maximum realtime priority allowed");
|
|
}
|
|
|
|
if (max_realtime_priority < sched_param.sched_priority) {
|
|
dbus_connection_unref(system_bus);
|
|
throw MakeErrno("maximum realtime priority is below 40");
|
|
}
|
|
|
|
pid_t pid = getpid();
|
|
if (rtkit_make_realtime(system_bus, pid, sched_param.sched_priority) < 0) {
|
|
dbus_connection_unref(system_bus);
|
|
throw MakeErrno("failed to request realtime scheduling");
|
|
}
|
|
|
|
dbus_connection_unref(system_bus);
|
|
res = 0;
|
|
}
|
|
|
|
// if (res < 0) {
|
|
// throw MakeErrno("sched_setscheduler failed");
|
|
// }
|
|
#endif // __linux__
|
|
} |