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");