199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 299a2dd95SBruce Richardson * Copyright(c) 2010-2014 Intel Corporation 399a2dd95SBruce Richardson */ 499a2dd95SBruce Richardson #include <stdio.h> 599a2dd95SBruce Richardson #include <stdint.h> 699a2dd95SBruce Richardson #include <signal.h> 799a2dd95SBruce Richardson #include <errno.h> 899a2dd95SBruce Richardson #include <string.h> 999a2dd95SBruce Richardson #include <sys/queue.h> 1099a2dd95SBruce Richardson #include <sys/time.h> 1199a2dd95SBruce Richardson #include <sys/timerfd.h> 1299a2dd95SBruce Richardson 1399a2dd95SBruce Richardson #include <rte_memory.h> 1499a2dd95SBruce Richardson #include <rte_interrupts.h> 1599a2dd95SBruce Richardson #include <rte_alarm.h> 1699a2dd95SBruce Richardson #include <rte_common.h> 1799a2dd95SBruce Richardson #include <rte_per_lcore.h> 1899a2dd95SBruce Richardson #include <rte_eal.h> 1999a2dd95SBruce Richardson #include <rte_launch.h> 2099a2dd95SBruce Richardson #include <rte_lcore.h> 2199a2dd95SBruce Richardson #include <rte_errno.h> 2299a2dd95SBruce Richardson #include <rte_spinlock.h> 2399a2dd95SBruce Richardson #include <rte_eal_trace.h> 2499a2dd95SBruce Richardson 2599a2dd95SBruce Richardson #include <eal_private.h> 2699a2dd95SBruce Richardson 2799a2dd95SBruce Richardson #ifndef TFD_NONBLOCK 2899a2dd95SBruce Richardson #include <fcntl.h> 2999a2dd95SBruce Richardson #define TFD_NONBLOCK O_NONBLOCK 3099a2dd95SBruce Richardson #endif 3199a2dd95SBruce Richardson 3299a2dd95SBruce Richardson #define NS_PER_US 1000 3399a2dd95SBruce Richardson #define US_PER_MS 1000 3499a2dd95SBruce Richardson #define MS_PER_S 1000 3599a2dd95SBruce Richardson #ifndef US_PER_S 3699a2dd95SBruce Richardson #define US_PER_S (US_PER_MS * MS_PER_S) 3799a2dd95SBruce Richardson #endif 3899a2dd95SBruce Richardson 3999a2dd95SBruce Richardson #ifdef CLOCK_MONOTONIC_RAW /* Defined in glibc bits/time.h */ 4099a2dd95SBruce Richardson #define CLOCK_TYPE_ID CLOCK_MONOTONIC_RAW 4199a2dd95SBruce Richardson #else 4299a2dd95SBruce Richardson #define CLOCK_TYPE_ID CLOCK_MONOTONIC 4399a2dd95SBruce Richardson #endif 4499a2dd95SBruce Richardson 4599a2dd95SBruce Richardson struct alarm_entry { 4699a2dd95SBruce Richardson LIST_ENTRY(alarm_entry) next; 4799a2dd95SBruce Richardson struct timeval time; 4899a2dd95SBruce Richardson rte_eal_alarm_callback cb_fn; 4999a2dd95SBruce Richardson void *cb_arg; 5099a2dd95SBruce Richardson volatile uint8_t executing; 5199a2dd95SBruce Richardson volatile pthread_t executing_id; 5299a2dd95SBruce Richardson }; 5399a2dd95SBruce Richardson 5499a2dd95SBruce Richardson static LIST_HEAD(alarm_list, alarm_entry) alarm_list = LIST_HEAD_INITIALIZER(); 5599a2dd95SBruce Richardson static rte_spinlock_t alarm_list_lk = RTE_SPINLOCK_INITIALIZER; 5699a2dd95SBruce Richardson 57*90b13ab8SHarman Kalra static struct rte_intr_handle *intr_handle; 5899a2dd95SBruce Richardson static int handler_registered = 0; 5999a2dd95SBruce Richardson static void eal_alarm_callback(void *arg); 6099a2dd95SBruce Richardson 61*90b13ab8SHarman Kalra void 62*90b13ab8SHarman Kalra rte_eal_alarm_cleanup(void) 63*90b13ab8SHarman Kalra { 64*90b13ab8SHarman Kalra rte_intr_instance_free(intr_handle); 65*90b13ab8SHarman Kalra } 66*90b13ab8SHarman Kalra 6799a2dd95SBruce Richardson int 6899a2dd95SBruce Richardson rte_eal_alarm_init(void) 6999a2dd95SBruce Richardson { 70*90b13ab8SHarman Kalra 71*90b13ab8SHarman Kalra intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE); 72*90b13ab8SHarman Kalra if (intr_handle == NULL) { 73*90b13ab8SHarman Kalra RTE_LOG(ERR, EAL, "Fail to allocate intr_handle\n"); 74*90b13ab8SHarman Kalra goto error; 75*90b13ab8SHarman Kalra } 76*90b13ab8SHarman Kalra 77*90b13ab8SHarman Kalra if (rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_ALARM)) 7899a2dd95SBruce Richardson goto error; 7999a2dd95SBruce Richardson 80*90b13ab8SHarman Kalra /* create a timerfd file descriptor */ 81*90b13ab8SHarman Kalra if (rte_intr_fd_set(intr_handle, 82*90b13ab8SHarman Kalra timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK))) 83*90b13ab8SHarman Kalra goto error; 84*90b13ab8SHarman Kalra 85*90b13ab8SHarman Kalra if (rte_intr_fd_get(intr_handle) == -1) 86*90b13ab8SHarman Kalra goto error; 8799a2dd95SBruce Richardson return 0; 8899a2dd95SBruce Richardson 8999a2dd95SBruce Richardson error: 90*90b13ab8SHarman Kalra rte_intr_instance_free(intr_handle); 9199a2dd95SBruce Richardson rte_errno = errno; 9299a2dd95SBruce Richardson return -1; 9399a2dd95SBruce Richardson } 9499a2dd95SBruce Richardson 9599a2dd95SBruce Richardson static void 9699a2dd95SBruce Richardson eal_alarm_callback(void *arg __rte_unused) 9799a2dd95SBruce Richardson { 9899a2dd95SBruce Richardson struct timespec now; 9999a2dd95SBruce Richardson struct alarm_entry *ap; 10099a2dd95SBruce Richardson 10199a2dd95SBruce Richardson rte_spinlock_lock(&alarm_list_lk); 10299a2dd95SBruce Richardson while ((ap = LIST_FIRST(&alarm_list)) !=NULL && 10399a2dd95SBruce Richardson clock_gettime(CLOCK_TYPE_ID, &now) == 0 && 10499a2dd95SBruce Richardson (ap->time.tv_sec < now.tv_sec || (ap->time.tv_sec == now.tv_sec && 10599a2dd95SBruce Richardson (ap->time.tv_usec * NS_PER_US) <= now.tv_nsec))) { 10699a2dd95SBruce Richardson ap->executing = 1; 10799a2dd95SBruce Richardson ap->executing_id = pthread_self(); 10899a2dd95SBruce Richardson rte_spinlock_unlock(&alarm_list_lk); 10999a2dd95SBruce Richardson 11099a2dd95SBruce Richardson ap->cb_fn(ap->cb_arg); 11199a2dd95SBruce Richardson 11299a2dd95SBruce Richardson rte_spinlock_lock(&alarm_list_lk); 11399a2dd95SBruce Richardson 11499a2dd95SBruce Richardson LIST_REMOVE(ap, next); 11599a2dd95SBruce Richardson free(ap); 11699a2dd95SBruce Richardson } 11799a2dd95SBruce Richardson 11899a2dd95SBruce Richardson if (!LIST_EMPTY(&alarm_list)) { 11999a2dd95SBruce Richardson struct itimerspec atime = { .it_interval = { 0, 0 } }; 12099a2dd95SBruce Richardson 12199a2dd95SBruce Richardson ap = LIST_FIRST(&alarm_list); 12299a2dd95SBruce Richardson atime.it_value.tv_sec = ap->time.tv_sec; 12399a2dd95SBruce Richardson atime.it_value.tv_nsec = ap->time.tv_usec * NS_PER_US; 12499a2dd95SBruce Richardson /* perform borrow for subtraction if necessary */ 12599a2dd95SBruce Richardson if (now.tv_nsec > (ap->time.tv_usec * NS_PER_US)) 12699a2dd95SBruce Richardson atime.it_value.tv_sec--, atime.it_value.tv_nsec += US_PER_S * NS_PER_US; 12799a2dd95SBruce Richardson 12899a2dd95SBruce Richardson atime.it_value.tv_sec -= now.tv_sec; 12999a2dd95SBruce Richardson atime.it_value.tv_nsec -= now.tv_nsec; 130*90b13ab8SHarman Kalra timerfd_settime(rte_intr_fd_get(intr_handle), 0, &atime, NULL); 13199a2dd95SBruce Richardson } 13299a2dd95SBruce Richardson rte_spinlock_unlock(&alarm_list_lk); 13399a2dd95SBruce Richardson } 13499a2dd95SBruce Richardson 13599a2dd95SBruce Richardson int 13699a2dd95SBruce Richardson rte_eal_alarm_set(uint64_t us, rte_eal_alarm_callback cb_fn, void *cb_arg) 13799a2dd95SBruce Richardson { 13899a2dd95SBruce Richardson struct timespec now; 13999a2dd95SBruce Richardson int ret = 0; 14099a2dd95SBruce Richardson struct alarm_entry *ap, *new_alarm; 14199a2dd95SBruce Richardson 14299a2dd95SBruce Richardson /* Check parameters, including that us won't cause a uint64_t overflow */ 14399a2dd95SBruce Richardson if (us < 1 || us > (UINT64_MAX - US_PER_S) || cb_fn == NULL) 14499a2dd95SBruce Richardson return -EINVAL; 14599a2dd95SBruce Richardson 14699a2dd95SBruce Richardson new_alarm = calloc(1, sizeof(*new_alarm)); 14799a2dd95SBruce Richardson if (new_alarm == NULL) 14899a2dd95SBruce Richardson return -ENOMEM; 14999a2dd95SBruce Richardson 15099a2dd95SBruce Richardson /* use current time to calculate absolute time of alarm */ 15199a2dd95SBruce Richardson clock_gettime(CLOCK_TYPE_ID, &now); 15299a2dd95SBruce Richardson 15399a2dd95SBruce Richardson new_alarm->cb_fn = cb_fn; 15499a2dd95SBruce Richardson new_alarm->cb_arg = cb_arg; 15599a2dd95SBruce Richardson new_alarm->time.tv_usec = ((now.tv_nsec / NS_PER_US) + us) % US_PER_S; 15699a2dd95SBruce Richardson new_alarm->time.tv_sec = now.tv_sec + (((now.tv_nsec / NS_PER_US) + us) / US_PER_S); 15799a2dd95SBruce Richardson 15899a2dd95SBruce Richardson rte_spinlock_lock(&alarm_list_lk); 15999a2dd95SBruce Richardson if (!handler_registered) { 16099a2dd95SBruce Richardson /* registration can fail, callback can be registered later */ 161*90b13ab8SHarman Kalra if (rte_intr_callback_register(intr_handle, 16299a2dd95SBruce Richardson eal_alarm_callback, NULL) == 0) 16399a2dd95SBruce Richardson handler_registered = 1; 16499a2dd95SBruce Richardson } 16599a2dd95SBruce Richardson 16699a2dd95SBruce Richardson if (LIST_EMPTY(&alarm_list)) 16799a2dd95SBruce Richardson LIST_INSERT_HEAD(&alarm_list, new_alarm, next); 16899a2dd95SBruce Richardson else { 16999a2dd95SBruce Richardson LIST_FOREACH(ap, &alarm_list, next) { 17099a2dd95SBruce Richardson if (ap->time.tv_sec > new_alarm->time.tv_sec || 17199a2dd95SBruce Richardson (ap->time.tv_sec == new_alarm->time.tv_sec && 17299a2dd95SBruce Richardson ap->time.tv_usec > new_alarm->time.tv_usec)){ 17399a2dd95SBruce Richardson LIST_INSERT_BEFORE(ap, new_alarm, next); 17499a2dd95SBruce Richardson break; 17599a2dd95SBruce Richardson } 17699a2dd95SBruce Richardson if (LIST_NEXT(ap, next) == NULL) { 17799a2dd95SBruce Richardson LIST_INSERT_AFTER(ap, new_alarm, next); 17899a2dd95SBruce Richardson break; 17999a2dd95SBruce Richardson } 18099a2dd95SBruce Richardson } 18199a2dd95SBruce Richardson } 18299a2dd95SBruce Richardson 18399a2dd95SBruce Richardson if (LIST_FIRST(&alarm_list) == new_alarm) { 18499a2dd95SBruce Richardson struct itimerspec alarm_time = { 18599a2dd95SBruce Richardson .it_interval = {0, 0}, 18699a2dd95SBruce Richardson .it_value = { 18799a2dd95SBruce Richardson .tv_sec = us / US_PER_S, 18899a2dd95SBruce Richardson .tv_nsec = (us % US_PER_S) * NS_PER_US, 18999a2dd95SBruce Richardson }, 19099a2dd95SBruce Richardson }; 191*90b13ab8SHarman Kalra ret |= timerfd_settime(rte_intr_fd_get(intr_handle), 0, &alarm_time, NULL); 19299a2dd95SBruce Richardson } 19399a2dd95SBruce Richardson rte_spinlock_unlock(&alarm_list_lk); 19499a2dd95SBruce Richardson 19599a2dd95SBruce Richardson rte_eal_trace_alarm_set(us, cb_fn, cb_arg, ret); 19699a2dd95SBruce Richardson return ret; 19799a2dd95SBruce Richardson } 19899a2dd95SBruce Richardson 19999a2dd95SBruce Richardson int 20099a2dd95SBruce Richardson rte_eal_alarm_cancel(rte_eal_alarm_callback cb_fn, void *cb_arg) 20199a2dd95SBruce Richardson { 20299a2dd95SBruce Richardson struct alarm_entry *ap, *ap_prev; 20399a2dd95SBruce Richardson int count = 0; 20499a2dd95SBruce Richardson int err = 0; 20599a2dd95SBruce Richardson int executing; 20699a2dd95SBruce Richardson 20799a2dd95SBruce Richardson if (!cb_fn) { 20899a2dd95SBruce Richardson rte_errno = EINVAL; 20999a2dd95SBruce Richardson return -1; 21099a2dd95SBruce Richardson } 21199a2dd95SBruce Richardson 21299a2dd95SBruce Richardson do { 21399a2dd95SBruce Richardson executing = 0; 21499a2dd95SBruce Richardson rte_spinlock_lock(&alarm_list_lk); 21599a2dd95SBruce Richardson /* remove any matches at the start of the list */ 21699a2dd95SBruce Richardson while ((ap = LIST_FIRST(&alarm_list)) != NULL && 21799a2dd95SBruce Richardson cb_fn == ap->cb_fn && 21899a2dd95SBruce Richardson (cb_arg == (void *)-1 || cb_arg == ap->cb_arg)) { 21999a2dd95SBruce Richardson 22099a2dd95SBruce Richardson if (ap->executing == 0) { 22199a2dd95SBruce Richardson LIST_REMOVE(ap, next); 22299a2dd95SBruce Richardson free(ap); 22399a2dd95SBruce Richardson count++; 22499a2dd95SBruce Richardson } else { 22599a2dd95SBruce Richardson /* If calling from other context, mark that alarm is executing 22699a2dd95SBruce Richardson * so loop can spin till it finish. Otherwise we are trying to 22799a2dd95SBruce Richardson * cancel our self - mark it by EINPROGRESS */ 22899a2dd95SBruce Richardson if (pthread_equal(ap->executing_id, pthread_self()) == 0) 22999a2dd95SBruce Richardson executing++; 23099a2dd95SBruce Richardson else 23199a2dd95SBruce Richardson err = EINPROGRESS; 23299a2dd95SBruce Richardson 23399a2dd95SBruce Richardson break; 23499a2dd95SBruce Richardson } 23599a2dd95SBruce Richardson } 23699a2dd95SBruce Richardson ap_prev = ap; 23799a2dd95SBruce Richardson 23899a2dd95SBruce Richardson /* now go through list, removing entries not at start */ 23999a2dd95SBruce Richardson LIST_FOREACH(ap, &alarm_list, next) { 24099a2dd95SBruce Richardson /* this won't be true first time through */ 24199a2dd95SBruce Richardson if (cb_fn == ap->cb_fn && 24299a2dd95SBruce Richardson (cb_arg == (void *)-1 || cb_arg == ap->cb_arg)) { 24399a2dd95SBruce Richardson 24499a2dd95SBruce Richardson if (ap->executing == 0) { 24599a2dd95SBruce Richardson LIST_REMOVE(ap, next); 24699a2dd95SBruce Richardson free(ap); 24799a2dd95SBruce Richardson count++; 24899a2dd95SBruce Richardson ap = ap_prev; 24999a2dd95SBruce Richardson } else if (pthread_equal(ap->executing_id, pthread_self()) == 0) 25099a2dd95SBruce Richardson executing++; 25199a2dd95SBruce Richardson else 25299a2dd95SBruce Richardson err = EINPROGRESS; 25399a2dd95SBruce Richardson } 25499a2dd95SBruce Richardson ap_prev = ap; 25599a2dd95SBruce Richardson } 25699a2dd95SBruce Richardson rte_spinlock_unlock(&alarm_list_lk); 25799a2dd95SBruce Richardson } while (executing != 0); 25899a2dd95SBruce Richardson 25999a2dd95SBruce Richardson if (count == 0 && err == 0) 26099a2dd95SBruce Richardson rte_errno = ENOENT; 26199a2dd95SBruce Richardson else if (err) 26299a2dd95SBruce Richardson rte_errno = err; 26399a2dd95SBruce Richardson 26499a2dd95SBruce Richardson rte_eal_trace_alarm_cancel(cb_fn, cb_arg, count); 26599a2dd95SBruce Richardson return count; 26699a2dd95SBruce Richardson } 267