set_latency_target() attempts to set keep "low latency" by - writing a specific latency to /dev/cpu_dma_latency - setting manually the minimum CPU processor frequency to be the maximum There is no functionality to undo this after stopping the softmodem(s), and most users are probably not even aware that OAI does this. It is generally preferable to set this beforehand using a governor or by disabling sleep states (as outlined in the tutorial, and in a follow-up commit in the performance tuning docs). The previous mlockall() call, to lock memory to RAM, is retained in a new function. There were additional mlockall() calls, which have been replaced with lock_memory_to_ram(), where necessary.
336 lines
10 KiB
C
336 lines
10 KiB
C
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.openairinterface.org/?page_id=698
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
/* This module provides a separate process to run system().
|
|
* The communication between this process and the main processing
|
|
* is done through unix pipes.
|
|
*
|
|
* Motivation: the UE sets its IP address using system() and
|
|
* that disrupts realtime processing in some cases. Having a
|
|
* separate process solves this problem.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include "system.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <common/utils/assertions.h>
|
|
#include <common/utils/LOG/log.h>
|
|
#define MAX_COMMAND 4096
|
|
|
|
static int command_pipe_read;
|
|
static int command_pipe_write;
|
|
static int result_pipe_read;
|
|
static int result_pipe_write;
|
|
|
|
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static int module_initialized = 0;
|
|
|
|
/********************************************************************/
|
|
/* util functions */
|
|
/********************************************************************/
|
|
|
|
static void lock_system(void) {
|
|
if (pthread_mutex_lock(&lock) != 0) {
|
|
printf("pthread_mutex_lock fails\n");
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static void unlock_system(void) {
|
|
if (pthread_mutex_unlock(&lock) != 0) {
|
|
printf("pthread_mutex_unlock fails\n");
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static void write_pipe(int p, char *b, int size) {
|
|
while (size) {
|
|
int ret = write(p, b, size);
|
|
|
|
if (ret <= 0) exit(0);
|
|
|
|
b += ret;
|
|
size -= ret;
|
|
}
|
|
}
|
|
|
|
static void read_pipe(int p, char *b, int size) {
|
|
while (size) {
|
|
int ret = read(p, b, size);
|
|
|
|
if (ret <= 0) exit(0);
|
|
|
|
b += ret;
|
|
size -= ret;
|
|
}
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* background process */
|
|
/********************************************************************/
|
|
|
|
/* This function is run by background process. It waits for a command,
|
|
* runs it, and reports status back. It exits (in normal situations)
|
|
* when the main process exits, because then a "read" on the pipe
|
|
* will return 0, in which case "read_pipe" exits.
|
|
*/
|
|
static void background_system_process(void) {
|
|
int len;
|
|
int ret;
|
|
char command[MAX_COMMAND+1];
|
|
|
|
while (1) {
|
|
read_pipe(command_pipe_read, (char *)&len, sizeof(int));
|
|
read_pipe(command_pipe_read, command, len);
|
|
ret = system(command);
|
|
write_pipe(result_pipe_write, (char *)&ret, sizeof(int));
|
|
}
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* background_system() */
|
|
/* return -1 on error, 0 on success */
|
|
/********************************************************************/
|
|
|
|
int background_system(char *command) {
|
|
int res;
|
|
int len;
|
|
|
|
if (module_initialized == 0) {
|
|
printf("FATAL: calling 'background_system' but 'start_background_system' was not called\n");
|
|
abort();
|
|
}
|
|
|
|
len = strlen(command)+1;
|
|
|
|
if (len > MAX_COMMAND) {
|
|
printf("FATAL: command too long. Increase MAX_COMMAND (%d).\n", MAX_COMMAND);
|
|
printf("command was: '%s'\n", command);
|
|
abort();
|
|
}
|
|
|
|
/* only one command can run at a time, so let's lock/unlock */
|
|
lock_system();
|
|
write_pipe(command_pipe_write, (char *)&len, sizeof(int));
|
|
write_pipe(command_pipe_write, command, len);
|
|
read_pipe(result_pipe_read, (char *)&res, sizeof(int));
|
|
unlock_system();
|
|
|
|
if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* start_background_system() */
|
|
/* initializes the "background system" module */
|
|
/* to be called very early by the main processing */
|
|
/********************************************************************/
|
|
|
|
void start_background_system(void) {
|
|
int p[2];
|
|
pid_t son;
|
|
|
|
if (module_initialized == 1)
|
|
return;
|
|
|
|
module_initialized = 1;
|
|
|
|
if (pipe(p) == -1) {
|
|
perror("pipe");
|
|
exit(1);
|
|
}
|
|
|
|
command_pipe_read = p[0];
|
|
command_pipe_write = p[1];
|
|
|
|
if (pipe(p) == -1) {
|
|
perror("pipe");
|
|
exit(1);
|
|
}
|
|
|
|
result_pipe_read = p[0];
|
|
result_pipe_write = p[1];
|
|
son = fork();
|
|
|
|
if (son == -1) {
|
|
perror("fork");
|
|
exit(1);
|
|
}
|
|
|
|
if (son) {
|
|
close(result_pipe_write);
|
|
close(command_pipe_read);
|
|
return;
|
|
}
|
|
|
|
close(result_pipe_read);
|
|
close(command_pipe_write);
|
|
background_system_process();
|
|
}
|
|
|
|
int rt_sleep_ns (uint64_t x)
|
|
{
|
|
struct timespec myTime;
|
|
clock_gettime(CLOCK_MONOTONIC, &myTime);
|
|
myTime.tv_sec += x/1000000000ULL;
|
|
myTime.tv_nsec = x%1000000000ULL;
|
|
if (myTime.tv_nsec>=1000000000) {
|
|
myTime.tv_nsec -= 1000000000;
|
|
myTime.tv_sec++;
|
|
}
|
|
|
|
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;
|
|
int ret;
|
|
int settingPriority = 1;
|
|
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);
|
|
AssertFatal(ret == 0, "Error in pthread_attr_setinheritsched(): ret: %d, errno: %d\n", ret, errno);
|
|
ret=pthread_attr_setschedpolicy(&attr, SCHED_OAI);
|
|
AssertFatal(ret == 0, "Error in pthread_attr_setschedpolicy(): ret: %d, errno: %d\n", ret, errno);
|
|
if(priority<sched_get_priority_min(SCHED_OAI) || priority>sched_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(priority<sched_get_priority_min(SCHED_OAI))
|
|
priority=sched_get_priority_min(SCHED_OAI);
|
|
if(priority>sched_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};
|
|
sparam.sched_priority = priority;
|
|
ret=pthread_attr_setschedparam(&attr, &sparam);
|
|
AssertFatal(ret == 0, "Error in pthread_attr_setschedparam(): ret: %d errno: %d\n", ret, errno);
|
|
}
|
|
|
|
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);
|
|
|
|
pthread_setname_np(*t, name);
|
|
if (affinity != -1 ) {
|
|
cpu_set_t cpuset;
|
|
CPU_ZERO(&cpuset);
|
|
CPU_SET(affinity, &cpuset);
|
|
ret = pthread_setaffinity_np(*t, sizeof(cpu_set_t), &cpuset);
|
|
AssertFatal(ret == 0, "Error in pthread_getaffinity_np(): ret: %d, errno: %d", ret, errno);
|
|
}
|
|
pthread_attr_destroy(&attr);
|
|
}
|
|
|
|
// Legacy, pthread_create + thread_top_init() should be replaced by threadCreate
|
|
// threadCreate encapsulates the posix pthread api
|
|
void thread_top_init(char *thread_name,
|
|
int affinity,
|
|
uint64_t runtime,
|
|
uint64_t deadline,
|
|
uint64_t period) {
|
|
|
|
int policy, s, j;
|
|
struct sched_param sparam;
|
|
char cpu_affinity[1024];
|
|
cpu_set_t cpuset;
|
|
int settingPriority = 1;
|
|
|
|
/* Set affinity mask to include CPUs 2 to MAX_CPUS */
|
|
/* CPU 0 is reserved for UHD threads */
|
|
/* CPU 1 is reserved for all RX_TX threads */
|
|
/* Enable CPU Affinity only if number of CPUs > 2 */
|
|
CPU_ZERO(&cpuset);
|
|
|
|
/* Check the actual affinity mask assigned to the thread */
|
|
s = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
|
|
if (s != 0)
|
|
{
|
|
perror( "pthread_getaffinity_np");
|
|
exit_fun("Error getting processor affinity ");
|
|
}
|
|
memset(cpu_affinity,0,sizeof(cpu_affinity));
|
|
for (j = 0; j < 1024; j++)
|
|
{
|
|
if (CPU_ISSET(j, &cpuset))
|
|
{
|
|
char temp[1024];
|
|
sprintf (temp, " CPU_%d", j);
|
|
strcat(cpu_affinity, temp);
|
|
}
|
|
}
|
|
|
|
if (settingPriority) {
|
|
memset(&sparam, 0, sizeof(sparam));
|
|
sparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
|
policy = SCHED_FIFO;
|
|
|
|
s = pthread_setschedparam(pthread_self(), policy, &sparam);
|
|
if (s != 0) {
|
|
perror("pthread_setschedparam : ");
|
|
exit_fun("Error setting thread priority");
|
|
}
|
|
|
|
s = pthread_getschedparam(pthread_self(), &policy, &sparam);
|
|
if (s != 0) {
|
|
perror("pthread_getschedparam : ");
|
|
exit_fun("Error getting thread priority");
|
|
}
|
|
|
|
pthread_setname_np(pthread_self(), thread_name);
|
|
|
|
LOG_I(HW, "[SCHED][eNB] %s started on CPU %d, sched_policy = %s , priority = %d, CPU Affinity=%s \n",thread_name,sched_getcpu(),
|
|
(policy == SCHED_FIFO) ? "SCHED_FIFO" :
|
|
(policy == SCHED_RR) ? "SCHED_RR" :
|
|
(policy == SCHED_OTHER) ? "SCHED_OTHER" :
|
|
"???",
|
|
sparam.sched_priority, cpu_affinity );
|
|
}
|
|
}
|
|
|
|
/* \brief lock memory to RAM to avoid delays */
|
|
void lock_memory_to_ram(void)
|
|
{
|
|
int rc = mlockall(MCL_CURRENT | MCL_FUTURE);
|
|
if (rc != 0)
|
|
LOG_W(UTIL, "mlockall() failed: %d, %s\n", errno, strerror(errno));
|
|
}
|