From ad317198d90ec1060ec11402695152e5c71a3546 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 17 Jul 2026 16:41:07 +0200 Subject: [PATCH] init: revert "gain real-time through rtkit (#15411)" This reverts commit 83cd85b3d57c9da1bcec80a46b7eec3514a344a9. --- .github/actions/setup_base/action.yml | 1 - CMakeLists.txt | 9 -- nix/default.nix | 2 - src/init/initHelpers.cpp | 182 ++------------------------ 4 files changed, 10 insertions(+), 184 deletions(-) diff --git a/.github/actions/setup_base/action.yml b/.github/actions/setup_base/action.yml index af988f71e..06af3a447 100644 --- a/.github/actions/setup_base/action.yml +++ b/.github/actions/setup_base/action.yml @@ -56,7 +56,6 @@ runs: pkgconf \ pugixml \ scdoc \ - sdbus-cpp \ seatd \ systemd \ tomlplusplus \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 23828dbac..d7dc57e62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,15 +375,6 @@ if(NOT HAS_INOTIFY AND inotify_FOUND) target_link_libraries(hyprland_lib PUBLIC PkgConfig::inotify) endif() -# sdbus-c++ is used to ask rtkit for realtime scheduling which is Linux-only -if(NO_RTKIT OR NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") - message(STATUS "rtkit realtime scheduling is disabled!") -else() - pkg_check_modules(sdbus_dep REQUIRED IMPORTED_TARGET sdbus-c++>=2.0.0) - target_link_libraries(hyprland_lib PUBLIC PkgConfig::sdbus_dep) - add_compile_definitions(HAS_RTKIT) -endif() - if(NO_XWAYLAND) message(STATUS "Using the NO_XWAYLAND flag, disabling XWayland!") add_compile_definitions(NO_XWAYLAND) diff --git a/nix/default.nix b/nix/default.nix index 7261d3b41..dea2c4e2e 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -43,7 +43,6 @@ pciutils, python3, re2, - sdbus-cpp_2, systemd, tomlplusplus, udis86-hyprland, @@ -203,7 +202,6 @@ customStdenv.mkDerivation (finalAttrs: { pango pciutils re2 - sdbus-cpp_2 tomlplusplus udis86-hyprland wayland diff --git a/src/init/initHelpers.cpp b/src/init/initHelpers.cpp index 9d2eced52..5d4d0b259 100644 --- a/src/init/initHelpers.cpp +++ b/src/init/initHelpers.cpp @@ -3,182 +3,24 @@ #include "initHelpers.hpp" -#ifdef HAS_RTKIT -#include -#include -#include -#include -#include -#include - -#include -#include -#endif - bool NInit::isSudo() { return getuid() != geteuid() || !geteuid(); } -// Asks the kernel directly to put the calling thread on SCHED_RR. Only succeeds if -// the process is privileged to go realtime on its own: CAP_SYS_NICE (e.g. granted via -// setcap or a security wrapper) or a nonzero RLIMIT_RTPRIO (e.g. a "realtime" group -// entry in limits.conf). -static bool trySchedDirect(int prio) { -#ifdef HAS_RTKIT - // SCHED_RESET_ON_FORK keeps children from inheriting realtime, the same kernel mechanism - // rtkit's grants use. Raw syscall because not every libc exposes the per-thread Linux - // semantics of sched_setscheduler (musl returns ENOSYS). - const struct sched_param param = {.sched_priority = prio}; - return syscall(SYS_sched_setscheduler, gettid(), SCHED_RR | SCHED_RESET_ON_FORK, ¶m) == 0; -#else - int oldPolicy = 0; - struct sched_param param = {}; - - if (pthread_getschedparam(pthread_self(), &oldPolicy, ¶m)) { - Log::logger->log(Log::WARN, "Failed to get old pthread scheduling priority"); - return false; - } - - param.sched_priority = prio; - - return pthread_setschedparam(pthread_self(), SCHED_RR, ¶m) == 0; -#endif -} - -#ifdef HAS_RTKIT - -// Cached for the SIGXCPU handler: it must demote the RT thread specifically, but a -// process-directed signal can be delivered to any thread. -static pid_t mainThreadTid = 0; - -// Demotes the main thread back to SCHED_OTHER instead of letting the process die. With a finite -// RLIMIT_RTTIME, an RT thread that overruns the soft limit without making a blocking syscall gets -// SIGXCPU (fatal by default), then SIGKILL at the hard limit. The counter resets every time the -// thread blocks (i.e. every frame), so this only fires on a genuinely runaway compositor. -// Only async-signal-safe calls in here: raw syscalls and write(). -static void handleSigxcpu(int /* signo */) { - // realtime was granted with SCHED_RESET_ON_FORK set (on both paths), keep it as clearing it - // needs CAP_SYS_NICE and the kernel rejects the whole call with EPERM otherwise. - const struct sched_param param = {.sched_priority = 0}; - if (syscall(SYS_sched_setscheduler, mainThreadTid, SCHED_OTHER | SCHED_RESET_ON_FORK, ¶m) == 0) { - constexpr char MSG[] = "Realtime budget exceeded (SIGXCPU), dropping realtime scheduling\n"; - std::ignore = write(STDERR_FILENO, MSG, sizeof(MSG) - 1); - } else { - constexpr char MSG[] = "Realtime budget exceeded (SIGXCPU), failed to drop realtime scheduling\n"; - std::ignore = write(STDERR_FILENO, MSG, sizeof(MSG) - 1); - } -} - -// Installs handleSigxcpu. Must happen before the thread can go realtime, and stays installed for -// the lifetime of the process: SIGXCPU is not delivered to well-behaved non-realtime processes. -static bool installSigxcpuHandler() { - mainThreadTid = gettid(); - - struct sigaction action = {}; - sigemptyset(&action.sa_mask); - action.sa_flags = SA_RESTART; - action.sa_handler = handleSigxcpu; - - if (sigaction(SIGXCPU, &action, nullptr)) { - Log::logger->log(Log::WARN, "Failed to install the SIGXCPU handler: {}, not attempting realtime", strerror(errno)); - return false; - } - - return true; -} - -constexpr const char* RTKIT_SERVICE = "org.freedesktop.RealtimeKit1"; -constexpr const char* RTKIT_OBJECT = "/org/freedesktop/RealtimeKit1"; -constexpr const char* RTKIT_INTERFACE = "org.freedesktop.RealtimeKit1"; -constexpr auto RTKIT_DBUS_TIMEOUT = std::chrono::seconds(2); // D-Bus defaults to 25s, a wedged rtkit must not stall startup - -// Asks rtkit (org.freedesktop.RealtimeKit1 on the system bus) to put the calling thread on -// SCHED_RR. rtkit hands out realtime scheduling to unprivileged processes, mediated by polkit -// (granted to active sessions only). It addresses threads by kernel tid (gettid), not pthread_t, -// and grants SCHED_RR with SCHED_RESET_ON_FORK set, so fork children reset to SCHED_OTHER -// kernel-side. -static bool tryRtkit(int prio) { - try { - auto connection = sdbus::createSystemBusConnection(); - auto proxy = sdbus::createProxy(*connection, sdbus::ServiceName{RTKIT_SERVICE}, sdbus::ObjectPath{RTKIT_OBJECT}); - - // read RTTimeUSecMax, the largest RLIMIT_RTTIME rtkit grants under. - // Also probes rtkit's liveness before touching RLIMIT_RTTIME: lowering - // the hard limit must not happen unless rtkit is actually there to - // grant us realtime in exchange - sdbus::Variant maxVar; - proxy->callMethod("Get") - .onInterface("org.freedesktop.DBus.Properties") - .withTimeout(RTKIT_DBUS_TIMEOUT) - .withArguments(RTKIT_INTERFACE, "RTTimeUSecMax") - .storeResultsTo(maxVar); - - const auto maxUs = maxVar.get(); - if (maxUs <= 0) { - Log::logger->log(Log::DEBUG, "rtkit: no usable RTTimeUSecMax, not requesting realtime"); - return false; - } - - // rtkit refuses to grant realtime unless the caller has a finite RLIMIT_RTTIME at or - // below its RTTimeUSecMax. Lowering the hard limit is irreversible without - // CAP_SYS_RESOURCE and the new limit is inherited by forked children: apps launched from - // Hyprland that gain realtime through their own privileges will live under this budget - // too. - rlimit current = {}; - if (getrlimit(RLIMIT_RTTIME, ¤t)) { - Log::logger->log(Log::WARN, "rtkit: failed to get RLIMIT_RTTIME: {}", strerror(errno)); - return false; - } - - // an unprivileged process cannot raise a finite hard limit, so stay under a pre-existing - // one (RLIM_INFINITY compares greater than any finite limit). Keep soft below hard: an - // overrun then raises a catchable SIGXCPU (which our handler turns into a demotion) - // before the uncatchable SIGKILL at the hard limit. - const rlim_t hard = std::min(current.rlim_max, sc(maxUs)); - const rlimit newLimit = {.rlim_cur = std::min(hard / 4 * 3, current.rlim_cur), .rlim_max = hard}; - - if (setrlimit(RLIMIT_RTTIME, &newLimit)) { - Log::logger->log(Log::WARN, "rtkit: failed to set RLIMIT_RTTIME: {}", strerror(errno)); - return false; - } - - // fails with a polkit denial for sessions that aren't active locally (e.g. ssh), - // which is fine because realtime is best-effort. the armed RLIMIT_RTTIME must stay, - // hard limits cannot be raised back. - proxy->callMethod("MakeThreadRealtime").onInterface(RTKIT_INTERFACE).withTimeout(RTKIT_DBUS_TIMEOUT).withArguments(sc(gettid()), sc(prio)); - - return true; - } catch (const sdbus::Error& e) { - Log::logger->log(Log::DEBUG, "rtkit: {}", e.what()); - return false; - } -} - -#endif - -// Puts the main thread on SCHED_RR so a busy session cannot starve the compositor of CPU. -// Going realtime directly needs privilege and is tried first, rtkit is the standard -// unprivileged fallback on desktop systems. Best-effort: on failure we just run as SCHED_OTHER. void NInit::gainRealTime() { - const int minPrio = sched_get_priority_min(SCHED_RR); - bool gained = false; + const int minPrio = sched_get_priority_min(SCHED_RR); + int old_policy; + struct sched_param param; -#ifdef HAS_RTKIT - if (!installSigxcpuHandler()) + if (pthread_getschedparam(pthread_self(), &old_policy, ¶m)) { + Log::logger->log(Log::WARN, "Failed to get old pthread scheduling priority"); return; -#endif + } - gained = trySchedDirect(minPrio); - if (gained) - Log::logger->log(Log::DEBUG, "Gained realtime scheduling directly"); + param.sched_priority = minPrio; -#ifdef HAS_RTKIT - if (!gained && (gained = tryRtkit(minPrio))) - Log::logger->log(Log::DEBUG, "Gained realtime scheduling via rtkit"); -#endif - - if (!gained) { - Log::logger->log(Log::WARN, "Failed to gain realtime scheduling"); + if (pthread_setschedparam(pthread_self(), SCHED_RR, ¶m)) { + Log::logger->log(Log::WARN, "Failed to change process scheduling strategy"); return; } @@ -186,13 +28,9 @@ void NInit::gainRealTime() { // CAP_SYS_NICE due to how the security wrapper works. prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, CAP_SYS_NICE, 0, 0); -#ifndef HAS_RTKIT - // spawned children must not inherit RT, on Linux the kernel handles this via - // SCHED_RESET_ON_FORK on both promotion paths pthread_atfork(nullptr, nullptr, []() { const struct sched_param param = {.sched_priority = 0}; if (pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m)) Log::logger->log(Log::WARN, "Failed to reset process scheduling strategy"); }); -#endif -} +} \ No newline at end of file