FlexRAN: reimplement timers without ITTI
The timer code used to trigger periodic statistics message sending used ITTI. Together with a high frequency reporting (e.g. 1ms), this resulted in high CPU usage. This commit reimplements this: * using a thread that is synchronized to the RU (if there is no MAC, we assume there is no RU generating a tick so we create the tick source ourselves) * no RB trees but plain arrays * smaller interface for users * slight user interface changes * remove stopped timer state: such timer might be simply removed * remove one-shot timers
This commit is contained in:
@@ -21,175 +21,266 @@
|
||||
|
||||
/*! \file flexran_agent_timer.c
|
||||
* \brief FlexRAN Timer
|
||||
* \author shahab SHARIAT BAGHERI
|
||||
* \date 2017
|
||||
* \version 0.1
|
||||
*/
|
||||
|
||||
/*
|
||||
* timer primitives
|
||||
* \author Robert Schmidt
|
||||
* \date 2019
|
||||
* \version 0.2
|
||||
*/
|
||||
|
||||
#include "flexran_agent_timer.h"
|
||||
#include "flexran_agent_extern.h"
|
||||
#include <pthread.h>
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
//struct flexran_agent_map agent_map;
|
||||
flexran_agent_timer_instance_t timer_instance;
|
||||
int agent_timer_init = 0;
|
||||
err_code_t flexran_agent_init_timer(void) {
|
||||
LOG_I(FLEXRAN_AGENT, "init RB tree\n");
|
||||
#define MAX_NUM_TIMERS 10
|
||||
|
||||
if (!agent_timer_init) {
|
||||
RB_INIT(&timer_instance.flexran_agent_head);
|
||||
agent_timer_init = 1;
|
||||
typedef struct flexran_agent_timer_element_s {
|
||||
mid_t mod_id;
|
||||
xid_t xid; /* The id of the task as received by the controller message*/
|
||||
uint32_t sf;
|
||||
uint64_t next;
|
||||
flexran_agent_timer_callback_t cb;
|
||||
Protocol__FlexranMessage *msg;
|
||||
} flexran_agent_timer_element_t;
|
||||
|
||||
typedef struct flexran_agent_timer_extern_source_s {
|
||||
pthread_mutex_t mutex_sync;
|
||||
pthread_cond_t cond;
|
||||
int wait;
|
||||
} flexran_agent_timer_extern_source_t;
|
||||
|
||||
struct timesync {
|
||||
void *param_sync;
|
||||
void (*fsync)(void *);
|
||||
pthread_t thread;
|
||||
|
||||
uint64_t current;
|
||||
uint64_t next;
|
||||
|
||||
int timer_num;
|
||||
flexran_agent_timer_element_t *timer[MAX_NUM_TIMERS];
|
||||
pthread_mutex_t mutex_timer;
|
||||
|
||||
int exit;
|
||||
};
|
||||
struct timesync timesync[NUM_MAX_ENB];
|
||||
|
||||
int flexran_agent_timer_signal_init(struct timesync *sync);
|
||||
int flexran_agent_timer_source_setup(struct timesync *sync);
|
||||
void *flexran_agent_timer_thread(void *args);
|
||||
void flexran_agent_timer_remove_internal(struct timesync *sync, int index);
|
||||
void flexran_agent_timer_process(flexran_agent_timer_element_t *t);
|
||||
|
||||
|
||||
err_code_t flexran_agent_timer_init(mid_t mod_id) {
|
||||
struct timesync *sync = ×ync[mod_id];
|
||||
sync->current = 0;
|
||||
sync->next = 0;
|
||||
sync->timer_num = 0;
|
||||
for (int i = 0; i < MAX_NUM_TIMERS; ++i)
|
||||
sync->timer[i] = NULL;
|
||||
pthread_mutex_init(&sync->mutex_timer, NULL);
|
||||
sync->exit = 0;
|
||||
|
||||
/* if there is a MAC, we assume we can have a tick from the MAC interface
|
||||
* (external tick source). Otherwise, generate a tick internally via the
|
||||
* timerfd linux library. The init functions set everything up and the thread
|
||||
* will use whatever is available. */
|
||||
int (*init)(struct timesync *) = flexran_agent_get_mac_xface(mod_id) ?
|
||||
flexran_agent_timer_signal_init : flexran_agent_timer_source_setup;
|
||||
if (init(timesync) < 0
|
||||
|| pthread_create(&sync->thread, NULL, flexran_agent_timer_thread, sync) != 0) {
|
||||
sync->thread = 0;
|
||||
if (sync->param_sync)
|
||||
free(sync->param_sync);
|
||||
LOG_E(FLEXRAN_AGENT, "could not start timer thread\n");
|
||||
return TIMER_SETUP_FAILED;
|
||||
}
|
||||
|
||||
return PROTOCOL__FLEXRAN_ERR__NO_ERR;
|
||||
}
|
||||
|
||||
RB_GENERATE(flexran_agent_map, flexran_agent_timer_element_s, entry, flexran_agent_compare_timer);
|
||||
void flexran_agent_timer_exit(mid_t mod_id) {
|
||||
timesync[mod_id].exit = 1;
|
||||
}
|
||||
|
||||
/* The timer_id might not be the best choice for the comparison */
|
||||
int flexran_agent_compare_timer(struct flexran_agent_timer_element_s *a, struct flexran_agent_timer_element_s *b) {
|
||||
if (a->timer_id < b->timer_id) return -1;
|
||||
void flexran_agent_timer_source_sync(void *sync) {
|
||||
int fd = *(int *)sync;
|
||||
uint64_t occ;
|
||||
int rc __attribute__((unused)) = read(fd, &occ, sizeof(occ));
|
||||
}
|
||||
|
||||
if (a->timer_id > b->timer_id) return 1;
|
||||
int flexran_agent_timer_source_setup(struct timesync *sync) {
|
||||
sync->param_sync = malloc(sizeof(int));
|
||||
if (!sync->param_sync)
|
||||
return -1;
|
||||
int fd = timerfd_create(CLOCK_MONOTONIC, 0);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
// equal timers
|
||||
const uint64_t period_ns = 1000000; // 1ms
|
||||
struct itimerspec t;
|
||||
t.it_interval.tv_sec = period_ns / 1000000000;
|
||||
t.it_interval.tv_nsec = period_ns % 1000000000;
|
||||
t.it_value.tv_sec = period_ns / 1000000000;
|
||||
t.it_value.tv_nsec = period_ns % 1000000000;
|
||||
int rc = timerfd_settime(fd, 0, &t, NULL);
|
||||
if (rc != 0)
|
||||
return -1;
|
||||
*(int *)sync->param_sync = fd;
|
||||
sync->fsync = flexran_agent_timer_source_sync;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void flexran_agent_timer_signal_sync(void *param_sync) {
|
||||
flexran_agent_timer_extern_source_t *sync = param_sync;
|
||||
pthread_mutex_lock(&sync->mutex_sync);
|
||||
while (sync->wait) {
|
||||
pthread_cond_wait(&sync->cond, &sync->mutex_sync);
|
||||
}
|
||||
sync->wait = 1;
|
||||
pthread_mutex_unlock(&sync->mutex_sync);
|
||||
}
|
||||
|
||||
int flexran_agent_timer_signal_init(struct timesync *sync) {
|
||||
flexran_agent_timer_extern_source_t *s = malloc(sizeof(flexran_agent_timer_extern_source_t));
|
||||
if (!s)
|
||||
return TIMER_SETUP_FAILED;
|
||||
if (pthread_mutex_init(&s->mutex_sync, NULL) < 0)
|
||||
return TIMER_SETUP_FAILED;
|
||||
if (pthread_cond_init(&s->cond, NULL) < 0)
|
||||
return TIMER_SETUP_FAILED;
|
||||
s->wait = 1;
|
||||
sync->param_sync = s;
|
||||
sync->fsync = flexran_agent_timer_signal_sync;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void flexran_agent_timer_signal(mid_t mod_id) {
|
||||
flexran_agent_timer_extern_source_t *sync = timesync[mod_id].param_sync;
|
||||
pthread_mutex_lock(&sync->mutex_sync);
|
||||
sync->wait = 0;
|
||||
pthread_mutex_unlock(&sync->mutex_sync);
|
||||
pthread_cond_signal(&sync->cond);
|
||||
}
|
||||
|
||||
void *flexran_agent_timer_thread(void *args) {
|
||||
struct timesync *sync = args;
|
||||
while (1) {
|
||||
sync->fsync(sync->param_sync);
|
||||
if (sync->exit)
|
||||
break;
|
||||
|
||||
pthread_mutex_lock(&sync->mutex_timer);
|
||||
sync->current++;
|
||||
|
||||
if (sync->current < sync->next) {
|
||||
pthread_mutex_unlock(&sync->mutex_timer);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sync->timer_num; ++i) {
|
||||
flexran_agent_timer_element_t *t = sync->timer[i];
|
||||
if (sync->current == t->next) {
|
||||
flexran_agent_timer_process(t);
|
||||
t->next += t->sf;
|
||||
}
|
||||
if (sync->next == sync->current || t->next < sync->next)
|
||||
sync->next = t->next;
|
||||
}
|
||||
pthread_mutex_unlock(&sync->mutex_timer);
|
||||
}
|
||||
LOG_W(FLEXRAN_AGENT, "terminated timer thread\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void flexran_agent_timer_process(flexran_agent_timer_element_t *t) {
|
||||
Protocol__FlexranMessage *msg = t->cb(t->mod_id, t->msg);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
int size = 0;
|
||||
void *data = flexran_agent_pack_message(msg, &size);
|
||||
if (flexran_agent_msg_send(t->mod_id, FLEXRAN_AGENT_DEFAULT, data, size, 0) < 0)
|
||||
LOG_E(FLEXRAN_AGENT, "error while sending message for timer xid %d\n", t->xid);
|
||||
}
|
||||
|
||||
|
||||
err_code_t flexran_agent_create_timer(mid_t mod_id,
|
||||
uint32_t sf,
|
||||
uint32_t timer_type,
|
||||
flexran_agent_timer_type_t timer_type,
|
||||
xid_t xid,
|
||||
flexran_agent_timer_callback_t cb,
|
||||
void *timer_args) {
|
||||
int ret=-1;
|
||||
|
||||
if (sf <= 0)
|
||||
Protocol__FlexranMessage *msg) {
|
||||
if (sf == 0)
|
||||
return TIMER_NULL;
|
||||
if (cb == NULL)
|
||||
return TIMER_SETUP_FAILED;
|
||||
AssertFatal(timer_type != FLEXRAN_AGENT_TIMER_TYPE_ONESHOT,
|
||||
"one shot timer not yet implemented\n");
|
||||
|
||||
if (timer_type >= FLEXRAN_AGENT_TIMER_TYPE_MAX)
|
||||
return TIMER_TYPE_INVALIDE;
|
||||
|
||||
uint32_t interval_usec = sf * 1000;
|
||||
uint32_t interval_sec = 0;
|
||||
if (interval_usec >= 1000 * 1000) {
|
||||
interval_sec = interval_usec / (1000 * 1000);
|
||||
interval_usec = interval_usec % (1000 * 1000);
|
||||
}
|
||||
struct flexran_agent_timer_element_s *e = calloc(1, sizeof(*e));
|
||||
DevAssert(e != NULL);
|
||||
long timer_id = 0;
|
||||
AssertFatal(e, "cannot allocate memory for FlexRAN timer!\n");
|
||||
if (timer_type == FLEXRAN_AGENT_TIMER_TYPE_ONESHOT) {
|
||||
ret = timer_setup(interval_sec,
|
||||
interval_usec,
|
||||
TASK_FLEXRAN_AGENT,
|
||||
mod_id,
|
||||
TIMER_ONE_SHOT,
|
||||
timer_args,
|
||||
&timer_id);
|
||||
|
||||
e->type = TIMER_ONE_SHOT;
|
||||
} else if (timer_type == FLEXRAN_AGENT_TIMER_TYPE_PERIODIC ) {
|
||||
ret = timer_setup(interval_sec,
|
||||
interval_usec,
|
||||
TASK_FLEXRAN_AGENT,
|
||||
mod_id,
|
||||
TIMER_PERIODIC,
|
||||
timer_args,
|
||||
&timer_id);
|
||||
e->type = TIMER_PERIODIC;
|
||||
}
|
||||
|
||||
if (ret < 0 ) {
|
||||
free(e);
|
||||
flexran_agent_timer_element_t *t = malloc(sizeof(flexran_agent_timer_element_t));
|
||||
if (!t) {
|
||||
LOG_E(FLEXRAN_AGENT, "no memory for new timer %d\n", xid);
|
||||
return TIMER_SETUP_FAILED;
|
||||
}
|
||||
t->mod_id = mod_id;
|
||||
t->xid = xid;
|
||||
t->sf = sf;
|
||||
t->cb = cb;
|
||||
t->msg = msg;
|
||||
|
||||
e->agent_id = mod_id;
|
||||
e->instance = mod_id;
|
||||
e->state = FLEXRAN_AGENT_TIMER_STATE_ACTIVE;
|
||||
e->timer_id = timer_id;
|
||||
e->xid = xid;
|
||||
e->timer_args = timer_args;
|
||||
e->cb = cb;
|
||||
/*element should be a real pointer*/
|
||||
RB_INSERT(flexran_agent_map, &timer_instance.flexran_agent_head, e);
|
||||
LOG_I(FLEXRAN_AGENT,"Created a new timer with id 0x%lx for agent %d, instance %d \n",
|
||||
e->timer_id, e->agent_id, e->instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err_code_t flexran_agent_destroy_timer(long timer_id) {
|
||||
struct flexran_agent_timer_element_s *e = get_timer_entry(timer_id);
|
||||
|
||||
if (e != NULL ) {
|
||||
RB_REMOVE(flexran_agent_map, &timer_instance.flexran_agent_head, e);
|
||||
flexran_agent_destroy_flexran_message(e->timer_args->msg);
|
||||
free(e);
|
||||
struct timesync *sync = ×ync[mod_id];
|
||||
pthread_mutex_lock(&sync->mutex_timer);
|
||||
if (sync->timer_num >= MAX_NUM_TIMERS) {
|
||||
pthread_mutex_unlock(&sync->mutex_timer);
|
||||
LOG_E(FLEXRAN_AGENT, "maximum number of timers (%d) reached while adding timer %d\n",
|
||||
sync->timer_num, xid);
|
||||
free(t);
|
||||
return TIMER_SETUP_FAILED;
|
||||
}
|
||||
|
||||
if (timer_remove(timer_id) < 0 )
|
||||
goto error;
|
||||
|
||||
/* TODO check that xid does not exist? */
|
||||
t->next = sync->current + 1;
|
||||
if (sync->next <= sync->current || t->next < sync->next)
|
||||
sync->next = t->next;
|
||||
sync->timer[sync->timer_num] = t;
|
||||
sync->timer_num++;
|
||||
pthread_mutex_unlock(&sync->mutex_timer);
|
||||
LOG_D(FLEXRAN_AGENT, "added new timer xid %d for agent %d\n", xid, mod_id);
|
||||
return 0;
|
||||
error:
|
||||
LOG_E(FLEXRAN_AGENT, "timer can't be removed\n");
|
||||
return TIMER_REMOVED_FAILED ;
|
||||
}
|
||||
|
||||
err_code_t flexran_agent_destroy_timer_by_task_id(xid_t xid) {
|
||||
struct flexran_agent_timer_element_s *e = NULL;
|
||||
long timer_id;
|
||||
RB_FOREACH(e, flexran_agent_map, &timer_instance.flexran_agent_head) {
|
||||
if (e->xid == xid) {
|
||||
timer_id = e->timer_id;
|
||||
RB_REMOVE(flexran_agent_map, &timer_instance.flexran_agent_head, e);
|
||||
flexran_agent_destroy_flexran_message(e->timer_args->msg);
|
||||
free(e);
|
||||
err_code_t flexran_agent_destroy_timers(mid_t mod_id) {
|
||||
struct timesync *sync = ×ync[mod_id];
|
||||
pthread_mutex_lock(&sync->mutex_timer);
|
||||
for (int i = sync->timer_num - 1; i < 0; --i) {
|
||||
flexran_agent_timer_remove_internal(sync, i);
|
||||
}
|
||||
pthread_mutex_unlock(&sync->mutex_timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (timer_remove(timer_id) < 0 ) {
|
||||
goto error;
|
||||
}
|
||||
err_code_t flexran_agent_destroy_timer(mid_t mod_id, xid_t xid) {
|
||||
struct timesync *sync = ×ync[mod_id];
|
||||
pthread_mutex_lock(&sync->mutex_timer);
|
||||
for (int i = 0; i < sync->timer_num; ++i) {
|
||||
if (sync->timer[i]->xid == xid) {
|
||||
flexran_agent_timer_remove_internal(sync, i);
|
||||
pthread_mutex_unlock(&sync->mutex_timer);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
error:
|
||||
LOG_E(FLEXRAN_AGENT, "timer can't be removed\n");
|
||||
return TIMER_REMOVED_FAILED ;
|
||||
pthread_mutex_unlock(&sync->mutex_timer);
|
||||
LOG_E(FLEXRAN_AGENT, "could not find timer %d\n", xid);
|
||||
return TIMER_ELEMENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
err_code_t flexran_agent_destroy_timers(void) {
|
||||
struct flexran_agent_timer_element_s *e = NULL;
|
||||
RB_FOREACH(e, flexran_agent_map, &timer_instance.flexran_agent_head) {
|
||||
RB_REMOVE(flexran_agent_map, &timer_instance.flexran_agent_head, e);
|
||||
timer_remove(e->timer_id);
|
||||
flexran_agent_destroy_flexran_message(e->timer_args->msg);
|
||||
free(e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
err_code_t flexran_agent_stop_timer(long timer_id) {
|
||||
struct flexran_agent_timer_element_s *e=NULL;
|
||||
struct flexran_agent_timer_element_s search;
|
||||
memset(&search, 0, sizeof(struct flexran_agent_timer_element_s));
|
||||
search.timer_id = timer_id;
|
||||
e = RB_FIND(flexran_agent_map, &timer_instance.flexran_agent_head, &search);
|
||||
|
||||
if (e != NULL ) {
|
||||
e->state = FLEXRAN_AGENT_TIMER_STATE_STOPPED;
|
||||
}
|
||||
|
||||
timer_remove(timer_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct flexran_agent_timer_element_s *get_timer_entry(long timer_id) {
|
||||
struct flexran_agent_timer_element_s search;
|
||||
memset(&search, 0, sizeof(struct flexran_agent_timer_element_s));
|
||||
search.timer_id = timer_id;
|
||||
return RB_FIND(flexran_agent_map, &timer_instance.flexran_agent_head, &search);
|
||||
/* this function assumes that the timer lock is held */
|
||||
void flexran_agent_timer_remove_internal(struct timesync *sync, int index) {
|
||||
LOG_I(FLEXRAN_AGENT, "remove timer xid %d (index %d) for agent %d\n",
|
||||
sync->timer[index]->xid, index, sync->timer[index]->mod_id);
|
||||
flexran_agent_destroy_flexran_message(sync->timer[index]->msg);
|
||||
free(sync->timer[index]);
|
||||
for (int i = index + 1; i < sync->timer_num; ++i)
|
||||
sync->timer[i - 1] = sync->timer[i];
|
||||
sync->timer_num--;
|
||||
sync->timer[sync->timer_num] = NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user