From 9b507d3210bfb368df93f3dc6f2e1dd67058e730 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 15 Oct 2025 09:26:36 +0200 Subject: [PATCH] context: add support for rlimit. = Add support for rlimit. = in the context.properties to adjust the rlimits of the process. A value of -1 sets the max limit. This can be used to increase the number of file descriptors in a pipewire process when select() is not used, for example. Other resource limits might be interesting as well maybe. Fixes #4047 --- doc/dox/config/pipewire.conf.5.md | 14 ++++++ src/daemon/pipewire-pulse.conf.in | 1 + src/daemon/pipewire.conf.in | 1 + src/pipewire/context.c | 84 +++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+) diff --git a/doc/dox/config/pipewire.conf.5.md b/doc/dox/config/pipewire.conf.5.md index 57ea8c3de..617f04751 100644 --- a/doc/dox/config/pipewire.conf.5.md +++ b/doc/dox/config/pipewire.conf.5.md @@ -275,6 +275,20 @@ Warn about failures to lock memory. @PAR@ pipewire.conf mem.mlock-all = false Try to mlock all current and future memory by the process. +@PAR@ pipewire.conf rlimit.nofile = 4096 +Try to set the max file descriptor number resource limit of the process. +A value of -1 raises the limit to the system defined hard maximum value. +The file resource limit is usually 1024 and should only be raised if the +program does not use the select() system call. PipeWire does normally not +use select(). + +@PAR@ pipewire.conf rlimit.*resource* = *value* +Set resource limits. *resource* can be one of: as, core, cpu, +data, fsize, locks, memlock, msgqueue, nice, nofile, nproc, rss, rtprio, +rttime, sigpending or stack. See the documentation of setrlimit to get the +meaning of these resources. A value of -1 will set the maximum allowed +limit. + @PAR@ pipewire.conf settings.check-quantum = false Check if the quantum in the settings metadata update is compatible with the configured limits. diff --git a/src/daemon/pipewire-pulse.conf.in b/src/daemon/pipewire-pulse.conf.in index affb993dd..8d018606d 100644 --- a/src/daemon/pipewire-pulse.conf.in +++ b/src/daemon/pipewire-pulse.conf.in @@ -14,6 +14,7 @@ context.properties = { #mem.allow-mlock = true #mem.mlock-all = false #log.level = 2 + #rlimit.nofile = -1 #default.clock.quantum-limit = 8192 } diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in index 14f7c6ea0..c3eb7120f 100644 --- a/src/daemon/pipewire.conf.in +++ b/src/daemon/pipewire.conf.in @@ -21,6 +21,7 @@ context.properties = { #clock.power-of-two-quantum = true #log.level = 2 #cpu.zero.denormals = false + #rlimit.nofile = -1 #loop.rt-prio = -1 # -1 = use module-rt prio, 0 disable rt #loop.class = data.rt diff --git a/src/pipewire/context.c b/src/pipewire/context.c index f1d5e01b4..ddcdd2cba 100644 --- a/src/pipewire/context.c +++ b/src/pipewire/context.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -300,6 +301,88 @@ static void data_loop_stop(struct impl *impl, struct data_loop *loop) loop->started = false; } +static int adjust_rlimit(int resource, const char *name, int value) +{ + struct rlimit rlim, highest, fixed; + + rlim = (struct rlimit) { .rlim_cur = value, .rlim_max = value, }; + + if (setrlimit(resource, &rlim) >= 0) { + pw_log_info("set rlimit %s to %d", name, value); + return 0; + } + if (errno != EPERM) + return -errno; + + /* So we failed to set the desired setrlimit, then let's try + * to get as close as we can */ + if (getrlimit(resource, &highest) < 0) + return -errno; + + /* If the hard limit is unbounded anyway, then the EPERM had other reasons, + * let's propagate the original EPERM then */ + if (highest.rlim_max == RLIM_INFINITY) + return -EPERM; + + fixed = (struct rlimit) { + .rlim_cur = SPA_MIN(rlim.rlim_cur, highest.rlim_max), + .rlim_max = SPA_MIN(rlim.rlim_max, highest.rlim_max), + }; + + /* Shortcut things if we wouldn't change anything. */ + if (fixed.rlim_cur == highest.rlim_cur && + fixed.rlim_max == highest.rlim_max) + return 0; + + pw_log_info("set rlimit %s to %d/%d instead of %d", name, + (int)fixed.rlim_cur, (int)fixed.rlim_max, value); + if (setrlimit(resource, &fixed) < 0) + return -errno; + + return 0; +} + +static int adjust_rlimits(const struct spa_dict *dict) +{ + const struct spa_dict_item *it; + static const char* rlimit_table[] = { + [RLIMIT_AS] = "as", + [RLIMIT_CORE] = "core", + [RLIMIT_CPU] = "cpu", + [RLIMIT_DATA] = "data", + [RLIMIT_FSIZE] = "fsize", + [RLIMIT_LOCKS] = "locks", + [RLIMIT_MEMLOCK] = "memlock", + [RLIMIT_MSGQUEUE] = "msgqueue", + [RLIMIT_NICE] = "nice", + [RLIMIT_NOFILE] = "nofile", + [RLIMIT_NPROC] = "nproc", + [RLIMIT_RSS] = "rss", + [RLIMIT_RTPRIO] = "rtprio", + [RLIMIT_RTTIME] = "rttime", + [RLIMIT_SIGPENDING] = "sigpending", + [RLIMIT_STACK] = "stack", + }; + int res; + spa_dict_for_each(it, dict) { + if (!spa_strstartswith(it->key, "rlimit.")) + continue; + for (size_t i = 0; i < SPA_N_ELEMENTS(rlimit_table); i++) { + const char *name = rlimit_table[i]; + int64_t val; + if (!spa_streq(it->key+7, name)) + continue; + if (!spa_atoi64(it->value, &val, 0)) { + pw_log_warn("invalid number %s", it->value); + } else if ((res = adjust_rlimit(i, name, val)) < 0) + pw_log_warn("can't set rlimit %s to %s: %s", + name, it->value, spa_strerror(res)); + break; + } + } + return 0; +} + /** Create a new context object * * \param main_loop the main loop to use @@ -420,6 +503,7 @@ struct pw_context *pw_context_new(struct pw_loop *main_loop, else pw_log_info("%p: mlockall succeeded", impl); } + adjust_rlimits(&properties->dict); pw_settings_init(this); this->settings = this->defaults;