context: add support for rlimit.<resource> = <value>

Add support for rlimit.<resource> = <value> 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
This commit is contained in:
Wim Taymans 2025-10-15 09:26:36 +02:00
parent 9f2d873760
commit 9b507d3210
4 changed files with 100 additions and 0 deletions

View file

@ -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.

View file

@ -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
}

View file

@ -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

View file

@ -9,6 +9,7 @@
#include <regex.h>
#include <limits.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <fnmatch.h>
#include <pipewire/log.h>
@ -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;