// SPDX-License-Identifier: BSD-2-Clause // author: Max Kellermann #include "Util.hxx" #include "system/Error.hxx" #ifdef __linux__ #include #include #include #include #include #include #include #include "lib/fmt/RuntimeError.hxx" #elif defined(_WIN32) #include #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 = 20; 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; dbus_error_init(&error); system_bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error); if (dbus_error_is_set(&error)) { throw FmtRuntimeError("could not connect to dbus: %s", error.message); } dbus_error_free(&error); 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) { sched_param.sched_priority = max_realtime_priority; /* dbus_connection_unref(system_bus); */ /* throw MakeErrno("maximum realtime priority is below 40"); */ } if (rtkit_make_realtime(system_bus, 0, 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__ }