/* * 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 */ #include #include #include #include #include #include #include #include #include "assertions.h" #include "intertask_interface.h" #include "s1ap_eNB_timer.h" #include "common/utils/LOG/log.h" #include "queue.h" #include "s1ap_common.h" struct s1ap_timer_elm_s { task_id_t task_id; ///< Task ID which has requested the timer int32_t instance; ///< Instance of the task which has requested the timer uint32_t timer_kind; ///< Instance of the task which has requested the timer timer_t timer; ///< Unique timer id timer_type_t type; ///< Timer type void *timer_arg; ///< Optional argument that will be passed when timer expires STAILQ_ENTRY(s1ap_timer_elm_s) entries; ///< Pointer to next element }; typedef struct timer_desc_s { STAILQ_HEAD(timer_list_head, s1ap_timer_elm_s) timer_queue; pthread_mutex_t timer_list_mutex; struct timespec timeout; } timer_desc_t; static timer_desc_t timer_desc; #define TIMER_SEARCH(vAR, tIMERfIELD, tIMERvALUE, tIMERqUEUE) \ do { \ STAILQ_FOREACH(vAR, tIMERqUEUE, entries) { \ if (((vAR)->tIMERfIELD == tIMERvALUE)) \ break; \ } \ } while(0) int s1ap_timer_timeout(sigval_t info) { struct s1ap_timer_elm_s *timer_p; MessageDef *message_p; s1ap_timer_has_expired_t *timer_expired_p; task_id_t task_id; int32_t instance; uint32_t timer_kind; timer_kind = info.sival_int; if( pthread_mutex_lock(&timer_desc.timer_list_mutex) != 0 ) { S1AP_ERROR("Failed to mutex lock timeout=%x\n", timer_kind); return -1; } TIMER_SEARCH(timer_p, timer_kind, timer_kind, &timer_desc.timer_queue); if( pthread_mutex_unlock(&timer_desc.timer_list_mutex) != 0 ) { S1AP_ERROR("Failed to mutex unlock timeout=0x%x\n", timer_kind); } if (timer_p == NULL) { S1AP_ERROR("Didn't find timer 0x%x in list\n", timer_kind); return -1; } S1AP_DEBUG("Timer kind 0x%x has expired\n", timer_kind); task_id = timer_p->task_id; instance = timer_p->instance; message_p = itti_alloc_new_message(TASK_UNKNOWN, TIMER_HAS_EXPIRED); timer_expired_p = (s1ap_timer_has_expired_t *)(&message_p->ittiMsg.timer_has_expired); timer_expired_p->timer_id = (long)timer_p->timer; timer_expired_p->timer_kind = timer_p->timer_kind; timer_expired_p->arg = timer_p->timer_arg; /* Timer is a one shot timer, remove it */ if( (int)timer_p->type == (int)S1AP_TIMER_ONE_SHOT ) { if( s1ap_timer_remove((long)timer_p->timer) != 0 ) { S1AP_DEBUG("Failed to delete timer 0x%lx\n", (long)timer_p->timer); } } /* Notify task of timer expiry */ if( itti_send_msg_to_task(task_id, instance, message_p) < 0 ) { S1AP_ERROR("Failed to send msg TIMER_HAS_EXPIRED to task %u\n", task_id); free(message_p); return -1; } return 0; } int s1ap_timer_setup( uint32_t interval_sec, uint32_t interval_us, task_id_t task_id, int32_t instance, uint32_t timer_kind, timer_type_t type, void *timer_arg, long *timer_id) { struct sigevent evp; struct itimerspec its; struct s1ap_timer_elm_s *timer_p; timer_t timer; if( timer_id == NULL ) { S1AP_ERROR("Invalid timer_id\n"); return -1; } if( (int)type >= (int)S1AP_TIMER_TYPE_MAX ) { S1AP_ERROR("Invalid timer type (%d/%d)!\n", type, TIMER_TYPE_MAX); return -1; } if( *timer_id != S1AP_TIMERID_INIT ) { if( s1ap_timer_remove(*timer_id) != 0 ) { S1AP_ERROR("Failed to delete timer when the timer start 0x%lx\n", *timer_id); } } /* Allocate new timer list element */ timer_p = malloc(sizeof(struct s1ap_timer_elm_s)); if( timer_p == NULL ) { S1AP_ERROR("Failed to create new timer element\n"); return -1; } memset(&timer, 0, sizeof(timer_t)); memset(&evp, 0, sizeof(evp)); timer_p->task_id = task_id; timer_p->instance = instance; timer_p->timer_kind = timer_kind; timer_p->type = type; timer_p->timer_arg = timer_arg; evp.sigev_notify = (int)SIGEV_THREAD; evp.sigev_notify_function = (void *)s1ap_timer_timeout; evp.sigev_signo = SIGRTMIN; evp.sigev_notify_attributes = NULL; evp.sigev_value.sival_int = timer_kind; /* At the timer creation, the timer structure will be filled in with timer_id, * which is unique for this process. This id is allocated by kernel and the * value might be used to distinguish timers. */ if( timer_create(CLOCK_REALTIME, &evp, &timer) < 0 ) { S1AP_ERROR("Failed to create timer: (%s:%d)\n", strerror(errno), errno); free(timer_p); return -1; } /* Fill in the first expiration value. */ its.it_value.tv_sec = interval_sec; its.it_value.tv_nsec = interval_us * 1000; if( (int)type == (int)S1AP_TIMER_PERIODIC ) { /* Asked for periodic timer. We set the interval time */ its.it_interval.tv_sec = interval_sec; its.it_interval.tv_nsec = interval_us * 1000; } else { /* Asked for one-shot timer. Do not set the interval field */ its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; } if( timer_settime(timer, 0, &its, NULL) ) { S1AP_ERROR("Failed to Settimer: (%s:%d)\n", strerror(errno), errno); free(timer_p); return -1; } /* Simply set the timer_id argument. so it can be used by caller */ *timer_id = (long)timer; timer_p->timer = timer; /* Lock the queue and insert the timer at the tail */ if( pthread_mutex_lock(&timer_desc.timer_list_mutex) != 0 ) { S1AP_ERROR("Failed to mutex lock\n"); if( timer_delete(timer_p->timer) < 0 ) { S1AP_ERROR("Failed to delete timer 0x%lx\n", (long)timer_p->timer); } free(timer_p); timer_p = NULL; return -1; } STAILQ_INSERT_TAIL(&timer_desc.timer_queue, timer_p, entries); if( pthread_mutex_unlock(&timer_desc.timer_list_mutex) != 0 ) { S1AP_ERROR("Failed to mutex unlock\n"); } return 0; } int s1ap_timer_remove(long timer_id) { int rc = 0; struct s1ap_timer_elm_s *timer_p; if( pthread_mutex_lock(&timer_desc.timer_list_mutex) != 0 ) { S1AP_ERROR("Failed to mutex lock\n"); if( timer_delete((timer_t)timer_id) < 0 ) { S1AP_ERROR("Failed to delete timer 0x%lx\n", (long)timer_id); } return -1; } TIMER_SEARCH(timer_p, timer, ((timer_t)timer_id), &timer_desc.timer_queue); /* We didn't find the timer in list */ if (timer_p == NULL) { S1AP_ERROR("Didn't find timer 0x%lx in list\n", timer_id); if( pthread_mutex_unlock(&timer_desc.timer_list_mutex) != 0 ) { S1AP_ERROR("Failed to mutex unlock\n"); } return -1; } timer_delete(timer_p->timer); STAILQ_REMOVE(&timer_desc.timer_queue, timer_p, s1ap_timer_elm_s, entries); if( pthread_mutex_unlock(&timer_desc.timer_list_mutex) != 0 ) { S1AP_ERROR("Failed to mutex unlock\n"); } free(timer_p); timer_p = NULL; return rc; } int s1ap_timer_init(void) { memset(&timer_desc, 0, sizeof(timer_desc_t)); STAILQ_INIT(&timer_desc.timer_queue); pthread_mutex_init(&timer_desc.timer_list_mutex, NULL); return 0; }