From 3aeae631f6618ee54aa663cf6b4a8903793c8fb7 Mon Sep 17 00:00:00 2001 From: DatCaptainHorse Date: Fri, 16 Oct 2026 18:38:51 +0300 Subject: [PATCH] fix(nesinit): grant the audio graph a nice level too, or the server stays non-RT With RLIMIT_RTPRIO alone, neswire's client loop went SCHED_FIFO 83 and the PipeWire server's graph loop stayed SCHED_OTHER. The server's module-rt asks for nice -11 first, and when that is refused (Max nice priority 0) it hands off to rtkit, which grants nothing in a box. The client config asks for no nice level, which is why only neswire got it. RLIMIT_NICE 39 (nice down to -19) next to rtprio 95, as PipeWire's own 25-pw-rlimits.conf grants. Co-Authored-By: Claude Opus 5.5 --- apps/nesinit/src/services.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/apps/nesinit/src/services.rs b/apps/nesinit/src/services.rs index 065f9c17..4c396edd 100644 --- a/apps/nesinit/src/services.rs +++ b/apps/nesinit/src/services.rs @@ -120,10 +120,10 @@ pub struct Service { /// re-declares that module loads it twice. pub umask: Option, /// Whether this service may give its own threads a realtime class, up to - /// [`REALTIME_PRIORITY`]. + /// [`REALTIME_PRIORITY`], and a nice level down to [`REALTIME_NICE`]. /// /// For the two processes that carry the audio graph's 5 ms cycle. Granted - /// as `RLIMIT_RTPRIO` at exec, which PipeWire's `module-rt` checks first + /// as `RLIMIT_RTPRIO` and `RLIMIT_NICE` at exec, which PipeWire's `module-rt` checks first /// and uses to set `SCHED_FIFO` itself. The grant used to come from rtkit, /// asked over the system bus with polkit's approval, and in a box that /// chain granted nothing. Measured 2026-10-16: every PipeWire, wireplumber @@ -204,6 +204,18 @@ pub const RUNTIME_DIR: &str = "/run/user/1000"; /// what it asks for. pub const REALTIME_PRIORITY: libc::rlim_t = 95; +/// How far below 0 a [`Service::realtime`] service may lower its nice level, +/// as `RLIMIT_NICE` counts it: `20 - nice`, so 39 allows nice -19. +/// +/// **Not optional alongside the priority.** The server's `module-rt` asks for +/// nice -11 before it asks for a realtime class, and when the nice level is +/// refused it gives up on doing it itself and asks rtkit, which in a box +/// grants nothing. Measured 2026-10-16: with only `RLIMIT_RTPRIO`, neswire's +/// client loop went `SCHED_FIFO` 83 (its config asks for no nice level) and +/// the server's graph loop stayed `SCHED_OTHER`. -19 is what PipeWire's own +/// packaging grants beside rtprio 95. +pub const REALTIME_NICE: libc::rlim_t = 39; + /// Somewhere every service may write. /// /// # The service user's home is on a read-only root @@ -552,12 +564,17 @@ impl Stack { // Before the uid drops: raising a hard limit takes root, // and once dropped the child could never get it back. if realtime { - let limit = libc::rlimit { - rlim_cur: REALTIME_PRIORITY, - rlim_max: REALTIME_PRIORITY, - }; - if libc::setrlimit(libc::RLIMIT_RTPRIO, &limit) != 0 { - return Err(std::io::Error::last_os_error()); + for (resource, value) in [ + (libc::RLIMIT_RTPRIO, REALTIME_PRIORITY), + (libc::RLIMIT_NICE, REALTIME_NICE), + ] { + let limit = libc::rlimit { + rlim_cur: value, + rlim_max: value, + }; + if libc::setrlimit(resource, &limit) != 0 { + return Err(std::io::Error::last_os_error()); + } } } // gid first: dropping the uid first would lose the