From 66bf27bce9625f65ed35e2eae7691a6ae2ab2327 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 14 Mar 2024 14:52:52 +0100 Subject: [PATCH] threadCreate(): check for and handle missing SYS_NICE SYS_NICE is a capability that allows a process to set thread affinity and priority, among other things (see capabilities(7) for more info). In this commit, add a function that allows to determine if the process has this capability, and try to change the thread affinity and priority, if requested. If the capability does not exist, the function will simply not attempt to change the corresponding thread attributes. To determine if the process has SYS_NICE, libcap can be used. However, it might not be installed by default. To avoid requiring another dependency, if we detect that libcap is not present, use a workaround by try to set a real-time scheduling policy; if it's present, or can be changed, we assume that the process has SYS_NICE (and clean up, if relevant). Simplify reading of capabilities --- CMakeLists.txt | 6 +++ common/utils/system.c | 80 +++++++++++++++++++++++++++----------- common/utils/system.h | 2 + executables/nr-softmodem.c | 4 ++ 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28b317fedb..136b2ebd62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -659,6 +659,12 @@ add_library(UTIL ${OPENAIR_DIR}/common/utils/time_meas.c ${OPENAIR_DIR}/common/utils/time_stat.c ) +pkg_check_modules(cap libcap) +if (cap_FOUND) + # see system.c for more info + target_link_libraries(UTIL PRIVATE cap) + target_compile_definitions(UTIL PRIVATE HAVE_LIB_CAP) +endif() target_link_libraries(UTIL PUBLIC ${T_LIB} pthread) set(SECURITY_SRC diff --git a/common/utils/system.c b/common/utils/system.c index 4e332a4ee4..270c3ae321 100644 --- a/common/utils/system.c +++ b/common/utils/system.c @@ -32,6 +32,7 @@ #include "system.h" #include #include +#include #include #include #include @@ -214,38 +215,71 @@ int rt_sleep_ns (uint64_t x) return clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &myTime, NULL); } -void threadCreate(pthread_t* t, void * (*func)(void*), void * param, char* name, int affinity, int priority){ - pthread_attr_t attr; +#ifdef HAVE_LIB_CAP +#include +/* \brief reports if the current thread has capability CAP_SYS_NICE, i.e. */ +bool has_cap_sys_nice(void) +{ + /* get capabilities of calling PID */ + cap_t cap = cap_get_pid(0); + cap_flag_value_t val; + /* check to what CAP_SYS_NICE is currently ("effective capability") set */ + int ret = cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &val); + AssertFatal(ret == 0, "Error in cap_get_flag(): ret %d errno %d\n", ret, errno); + cap_free(cap); + /* return true if CAP_SYS_NICE is currently set */ + return val == CAP_SET; +} +#else +/* libcap has not been detected on this system. We do not need to require it -- + * we can try to read directly via a syscall. This is discouraged, though; from + * the man page: "The portable interfaces are cap_set_proc(3) and + * cap_get_proc(3); if possible, you should use those interfaces in + * applications". */ +#include /* Definition of SYS_* constants */ +#include /* capabilities used below */ +/* \brief reports if the current thread has capability CAP_SYS_NICE, i.e. */ +bool has_cap_sys_nice(void) +{ + struct __user_cap_header_struct hdr = {.version = _LINUX_CAPABILITY_VERSION_3}; + struct __user_cap_data_struct cap[2]; + if (syscall(SYS_capget, &hdr, cap) == -1) + return false; + return (cap[0].effective & (1 << CAP_SYS_NICE)) != 0; +} +#endif + +void threadCreate(pthread_t* t, void * (*func)(void*), void * param, char* name, int affinity, int priority) +{ int ret; - int settingPriority = 1; + bool set_prio = has_cap_sys_nice(); + + pthread_attr_t attr; ret=pthread_attr_init(&attr); AssertFatal(ret == 0, "Error in pthread_attr_init(): ret: %d, errno: %d\n", ret, errno); - LOG_I(UTIL,"Creating thread %s with affinity %d and priority %d\n",name,affinity,priority); - - if (settingPriority) { - ret=pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + if (set_prio) { + ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); AssertFatal(ret == 0, "Error in pthread_attr_setinheritsched(): ret: %d, errno: %d\n", ret, errno); - ret=pthread_attr_setschedpolicy(&attr, SCHED_OAI); + ret = pthread_attr_setschedpolicy(&attr, SCHED_OAI); AssertFatal(ret == 0, "Error in pthread_attr_setschedpolicy(): ret: %d, errno: %d\n", ret, errno); - if(prioritysched_get_priority_max(SCHED_OAI)) { - LOG_E(UTIL,"Prio not possible: %d, min is %d, max: %d, forced in the range\n", - priority, - sched_get_priority_min(SCHED_OAI), - sched_get_priority_max(SCHED_OAI)); - if(prioritysched_get_priority_max(SCHED_OAI)) - priority=sched_get_priority_max(SCHED_OAI); - } - AssertFatal(priority<=sched_get_priority_max(SCHED_OAI),""); - struct sched_param sparam={0}; + AssertFatal(priority >= sched_get_priority_min(SCHED_OAI) && priority <= sched_get_priority_max(SCHED_OAI), + "Scheduling priority %d not possible: must be within [%d, %d]\n", + priority, + sched_get_priority_min(SCHED_OAI), + sched_get_priority_max(SCHED_OAI)); + AssertFatal(priority <= sched_get_priority_max(SCHED_OAI), ""); + struct sched_param sparam = {0}; sparam.sched_priority = priority; - ret=pthread_attr_setschedparam(&attr, &sparam); + ret = pthread_attr_setschedparam(&attr, &sparam); AssertFatal(ret == 0, "Error in pthread_attr_setschedparam(): ret: %d errno: %d\n", ret, errno); + LOG_I(UTIL, "%s() for %s: creating thread with affinity %x, priority %d\n", __func__, name, affinity, priority); + } else { + affinity = -1; + priority = -1; + LOG_I(UTIL, "%s() for %s: creating thread (no affinity, default priority)\n", __func__, name); } - - LOG_I(UTIL,"threadCreate for %s, affinity %x, priority %d\n",name,affinity,priority); + ret=pthread_create(t, &attr, func, param); AssertFatal(ret == 0, "Error in pthread_create(): ret: %d, errno: %d\n", ret, errno); diff --git a/common/utils/system.h b/common/utils/system.h index 1c36f29ea3..2a4b920357 100644 --- a/common/utils/system.h +++ b/common/utils/system.h @@ -23,6 +23,7 @@ #define _SYSTEM_H_OAI_ #include #include +#include #ifdef __cplusplus extern "C" { #endif @@ -44,6 +45,7 @@ void start_background_system(void); void lock_memory_to_ram(void); +bool has_cap_sys_nice(void); void threadCreate(pthread_t *t, void *(*func)(void *), void *param, char *name, int affinity, int priority); #define SCHED_OAI SCHED_RR diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 6bc0b4e621..d880090d99 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -633,6 +633,10 @@ int main( int argc, char **argv ) { exit(-1); } + if (!has_cap_sys_nice()) + LOG_W(UTIL, + "no SYS_NICE capability: cannot set thread priority and affinity, consider running with sudo for optimum performance\n"); + if (get_softmodem_params()->do_ra) AssertFatal(get_softmodem_params()->phy_test == 0,"RA and phy_test are mutually exclusive\n");