modules: Add module-rt

Add a module to set thread priorities without using rtkit.
This commit is contained in:
Jonas Holmberg 2021-05-19 17:22:39 +02:00
parent fb55cc4786
commit b1610d37fe
4 changed files with 270 additions and 0 deletions

View file

@ -73,6 +73,17 @@ context.modules = [
flags = [ ifexists nofail ]
}
# Set thread priorities without using RTKit.
#{ name = libpipewire-module-rt
# args = {
# nice.level = -11
# rt.prio = 88
# rt.time.soft = 200000
# rt.time.hard = 200000
# }
# flags = [ ifexists nofail ]
#}
# The native communication protocol.
{ name = libpipewire-module-protocol-native }

View file

@ -22,6 +22,7 @@ RestrictNamespaces=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
Type=simple
AmbientCapabilities=CAP_SYS_NICE
ExecStart=@PW_BINARY@
Restart=on-failure
RuntimeDirectory=pipewire

View file

@ -58,6 +58,15 @@ pipewire_module_profiler = shared_library('pipewire-module-profiler',
dependencies : [mathlib, dl_lib, pipewire_dep],
)
pipewire_module_rt = shared_library('pipewire-module-rt', [ 'module-rt.c' ],
c_args : pipewire_module_c_args,
include_directories : [configinc, spa_inc],
install : true,
install_dir : modules_install_dir,
install_rpath: modules_install_dir,
dependencies : [dl_lib, pipewire_dep],
)
if dbus_dep.found()
pipewire_module_rtkit = shared_library('pipewire-module-rtkit', [ 'module-rtkit.c' ],
c_args : pipewire_module_c_args,

249
src/modules/module-rt.c Normal file
View file

@ -0,0 +1,249 @@
/* PipeWire
*
* Copyright © 2021 Axis Communications AB
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
#include <sched.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <pipewire/impl.h>
#include <spa/utils/dict.h>
#include "config.h"
#define DEFAULT_NICE_LEVEL -11
#define DEFAULT_RT_PRIO 88
#define DEFAULT_RT_TIME_SOFT 200000
#define DEFAULT_RT_TIME_HARD 200000
#define MODULE_USAGE \
"[nice.level=<priority: default " SPA_STRINGIFY(DEFAULT_NICE_LEVEL) ">] " \
"[rt.prio=<priority: default " SPA_STRINGIFY(DEFAULT_RT_PRIO) ">] " \
"[rt.time.soft=<in usec: default " SPA_STRINGIFY(DEFAULT_RT_TIME_SOFT)"] " \
"[rt.time.hard=<in usec: default " SPA_STRINGIFY(DEFAULT_RT_TIME_HARD)"] "
static const struct spa_dict_item module_props[] = {
{ PW_KEY_MODULE_AUTHOR, "Jonas Holmberg <jonashg@axis.com>" },
{ PW_KEY_MODULE_DESCRIPTION, "Set thread priorities" },
{ PW_KEY_MODULE_USAGE, MODULE_USAGE },
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
};
struct impl {
struct pw_context *context;
struct spa_loop *loop;
struct spa_system *system;
struct spa_source source;
int rt_prio;
rlim_t rt_time_soft;
rlim_t rt_time_hard;
struct spa_hook module_listener;
};
static int do_remove_source(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
{
struct spa_source *source = user_data;
spa_loop_remove_source(loop, source);
return 0;
}
static void module_destroy(void *data)
{
struct impl *impl = data;
spa_hook_remove(&impl->module_listener);
if (impl->source.fd != -1) {
spa_loop_invoke(impl->loop, do_remove_source, SPA_ID_INVALID, NULL, 0, true, &impl->source);
spa_system_close(impl->system, impl->source.fd);
impl->source.fd = -1;
}
free(impl);
}
static const struct pw_impl_module_events module_events = {
PW_VERSION_IMPL_MODULE_EVENTS,
.destroy = module_destroy,
};
static void idle_func(struct spa_source *source)
{
struct impl *impl = source->data;
uint64_t count;
int policy = SCHED_RR;
int rtprio = impl->rt_prio;
struct rlimit rl;
struct sched_param sp;
if (SPA_UNLIKELY(spa_system_eventfd_read(impl->system, impl->source.fd, &count) < 0))
pw_log_warn("read failed: %m");
if (rtprio < sched_get_priority_min(policy) ||
rtprio > sched_get_priority_max(policy)) {
pw_log_warn("invalid priority %d for policy %d", rtprio, policy);
return;
}
rl.rlim_cur = impl->rt_time_soft;
rl.rlim_max = impl->rt_time_hard;
if (setrlimit(RLIMIT_RTTIME, &rl) < 0)
pw_log_warn("could not set rlimit: %m");
else
pw_log_debug("rt.prio %d, rt.time.soft %"PRIi64", rt.time.hard %"PRIi64,
rtprio, (int64_t)rl.rlim_cur, (int64_t)rl.rlim_max);
spa_zero(sp);
sp.sched_priority = rtprio;
if (sched_setscheduler(0, policy | SCHED_RESET_ON_FORK, &sp) < 0) {
pw_log_warn("could not make thread realtime: %m");
return;
}
pw_log_info("processing thread has realtime priority %d", rtprio);
}
static void set_nice(struct impl *impl, int nice_level)
{
long tid;
int res;
tid = syscall(SYS_gettid);
if (tid < 0) {
pw_log_warn("could not get main thread id: %m");
tid = 0; /* means current thread in setpriority() on linux */
}
res = setpriority(PRIO_PROCESS, (id_t)tid, nice_level);
if (res < 0)
pw_log_warn("could not set nice-level to %d: %m", nice_level);
else
pw_log_info("main thread nice level set to %d", nice_level);
}
static int get_default_int(struct pw_properties *properties, const char *name, int def)
{
const char *str;
int val;
bool set_default = true;
if ((str = pw_properties_get(properties, name)) != NULL) {
char *endptr;
val = (int)strtol(str, &endptr, 10);
if (*endptr == '\0')
set_default = false;
else
pw_log_warn("invalid integer value '%s' of property %s, using default (%d) instead", str, name, def);
}
if (set_default) {
val = def;
pw_properties_setf(properties, name, "%d", val);
}
return val;
}
SPA_EXPORT
int pipewire__module_init(struct pw_impl_module *module, const char *args)
{
struct pw_context *context = pw_impl_module_get_context(module);
struct impl *impl;
struct spa_loop *loop;
struct spa_system *system;
const struct spa_support *support;
uint32_t n_support;
struct pw_properties *props;
int nice_level;
int res;
support = pw_context_get_support(context, &n_support);
loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
if (loop == NULL)
return -ENOTSUP;
system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataSystem);
if (system == NULL)
return -ENOTSUP;
impl = calloc(1, sizeof(struct impl));
if (impl == NULL)
return -ENOMEM;
pw_log_debug("module %p: new %s", impl, args);
impl->context = context;
impl->loop = loop;
impl->system = system;
props = args ? pw_properties_new_string(args) : pw_properties_new(NULL, NULL);
if (props == NULL) {
res = -errno;
goto error;
}
nice_level = get_default_int(props, "nice.level", DEFAULT_NICE_LEVEL);
set_nice(impl, nice_level);
impl->rt_prio = get_default_int(props, "rt.prio", DEFAULT_RT_PRIO);
impl->rt_time_soft = get_default_int(props, "rt.time.soft", DEFAULT_RT_TIME_SOFT);
impl->rt_time_hard = get_default_int(props, "rt.time.hard", DEFAULT_RT_TIME_HARD);
impl->source.loop = loop;
impl->source.func = idle_func;
impl->source.data = impl;
impl->source.fd = spa_system_eventfd_create(system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
impl->source.mask = SPA_IO_IN;
if (impl->source.fd == -1) {
res = -errno;
goto error;
}
spa_loop_add_source(impl->loop, &impl->source);
if (SPA_UNLIKELY(spa_system_eventfd_write(system, impl->source.fd, 1) < 0))
pw_log_warn("write failed: %m");
pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
pw_impl_module_update_properties(module, &props->dict);
pw_properties_free(props);
return 0;
error:
free(impl);
return res;
}