diff options
author | Matias Elo <matias.elo@nokia.com> | 2022-03-23 09:07:38 +0200 |
---|---|---|
committer | Matias Elo <matias.elo@nokia.com> | 2022-04-25 10:43:14 +0300 |
commit | 011482ab24343a64930fe011892866dc0fd3fc44 (patch) | |
tree | ffcfdf5651a577fab297a0e8dca9101482b7997a /platform/linux-dpdk | |
parent | 3679fdbdb596351275c5da855321c47e3015df3c (diff) |
Port d7e5e69be "linux-gen: timer: implement periodic timer"
Port original commit from linux-generic.
Signed-off-by: Matias Elo <matias.elo@nokia.com>
Reviewed-by: Petri Savolainen <petri.savolainen@nokia.com>
Diffstat (limited to 'platform/linux-dpdk')
-rw-r--r-- | platform/linux-dpdk/odp_timer.c | 303 |
1 files changed, 282 insertions, 21 deletions
diff --git a/platform/linux-dpdk/odp_timer.c b/platform/linux-dpdk/odp_timer.c index c9e1e55e5..f2f28f1d6 100644 --- a/platform/linux-dpdk/odp_timer.c +++ b/platform/linux-dpdk/odp_timer.c @@ -7,10 +7,14 @@ #include <odp_posix_extensions.h> +#include <odp/api/queue.h> #include <odp/api/shared_memory.h> +#include <odp/api/std.h> #include <odp/api/thread.h> #include <odp/api/ticketlock.h> +#include <odp/api/time.h> #include <odp/api/timer.h> + #include <odp/api/plat/queue_inlines.h> #include <odp_debug_internal.h> @@ -52,6 +56,9 @@ ODP_STATIC_ASSERT(MAX_TIMERS < MAX_TIMER_RING_SIZE, "MAX_TIMER_RING_SIZE too small"); +/* Special expiration tick used for detecting final periodic timer events */ +#define PERIODIC_CANCELLED ((uint64_t)0xFFFFFFFFFFFFFFFF) + /* Max timeout in capability. One year in nsec (0x0070 09D3 2DA3 0000). */ #define MAX_TMO_NS (365 * 24 * 3600 * ODP_TIME_SEC_IN_NS) @@ -68,15 +75,29 @@ ODP_STATIC_ASSERT(MAX_TIMERS < MAX_TIMER_RING_SIZE, /* Duration of a spin loop */ #define WAIT_SPINS 30 +/* Minimum periodic timer base frequency */ +#define MIN_BASE_HZ 1 + +/* Maximum periodic timer base frequency */ +#define MAX_BASE_HZ MAX_RES_HZ + +/* Maximum periodic timer multiplier */ +#define MAX_MULTIPLIER 1000000 + +/* Maximum number of periodic timers per pool */ +#define MAX_PERIODIC_TIMERS 100 + typedef struct { odp_ticketlock_t lock; - int state; uint64_t tick; const void *user_ptr; odp_queue_t queue; odp_event_t tmo_event; struct timer_pool_s *timer_pool; + int state; uint32_t timer_idx; + /* Period of periodic timer in ticks, includes PERIODIC_CANCELLED flag. */ + uint64_t periodic_ticks; struct rte_timer rte_timer; @@ -101,6 +122,9 @@ typedef struct timer_pool_s { odp_ticketlock_t lock; uint32_t cur_timers; uint32_t hwm_timers; + double base_freq; + uint64_t max_multiplier; + uint8_t periodic; } timer_pool_t; #pragma GCC diagnostic pop @@ -164,9 +188,11 @@ static void timer_cb(struct rte_timer *rte_timer, void *arg ODP_UNUSED) queue = timer->queue; event = timer->tmo_event; - timer->tmo_event = ODP_EVENT_INVALID; timer->state = EXPIRED; + if (!timer->timer_pool->periodic) + timer->tmo_event = ODP_EVENT_INVALID; + odp_ticketlock_unlock(&timer->lock); if (odp_unlikely(odp_queue_enq(queue, event))) { @@ -244,6 +270,20 @@ static inline timer_entry_t *timer_from_hdl(odp_timer_t timer_hdl) return (timer_entry_t *)(uintptr_t)timer_hdl; } +static uint64_t max_multiplier_capa(double freq) +{ + uint64_t mult; + + if (freq < MIN_BASE_HZ) + return 0; + + mult = MAX_BASE_HZ / freq; + if (mult > MAX_MULTIPLIER) + mult = MAX_MULTIPLIER; + + return mult; +} + int _odp_timer_init_global(const odp_init_t *params) { odp_shm_t shm; @@ -420,6 +460,8 @@ int odp_timer_capability(odp_timer_clk_src_t clk_src, capa->max_pools_combined = MAX_TIMER_POOLS; capa->max_pools = MAX_TIMER_POOLS; capa->max_timers = MAX_TIMERS; + capa->periodic.max_pools = MAX_TIMER_POOLS; + capa->periodic.max_timers = MAX_PERIODIC_TIMERS; capa->highest_res_ns = MAX_RES_NS; capa->max_res.res_ns = MAX_RES_NS; capa->max_res.res_hz = MAX_RES_HZ; @@ -432,6 +474,9 @@ int odp_timer_capability(odp_timer_clk_src_t clk_src, capa->queue_type_sched = true; capa->queue_type_plain = true; + capa->periodic.min_base_freq_hz.integer = MIN_BASE_HZ; + capa->periodic.max_base_freq_hz.integer = MAX_BASE_HZ; + return 0; } @@ -462,9 +507,44 @@ int odp_timer_res_capability(odp_timer_clk_src_t clk_src, return 0; } +int odp_timer_periodic_capability(odp_timer_clk_src_t clk_src, + odp_timer_periodic_capability_t *capa) +{ + double freq; + uint64_t multiplier; + + if (clk_src != ODP_CLOCK_DEFAULT) { + ODP_ERR("Only ODP_CLOCK_DEFAULT supported. Requested %i.\n", clk_src); + return -1; + } + + freq = odp_fract_u64_to_dbl(&capa->base_freq_hz); + if (freq < MIN_BASE_HZ || freq > MAX_BASE_HZ) { + ODP_ERR("Base frequency not supported (min: %f, max %f)\n", + (double)MIN_BASE_HZ, (double)MAX_BASE_HZ); + return -1; + } + + multiplier = max_multiplier_capa(freq); + + if (capa->max_multiplier > multiplier) + return -1; + + if (capa->res_ns && capa->res_ns < MAX_RES_NS) + return -1; + + /* Update capa with supported values */ + capa->max_multiplier = multiplier; + capa->res_ns = MAX_RES_NS; + + /* All base frequencies within the range are supported */ + return 1; +} + void odp_timer_pool_param_init(odp_timer_pool_param_t *param) { memset(param, 0, sizeof(odp_timer_pool_param_t)); + param->timer_type = ODP_TIMER_TYPE_SINGLE; param->clk_src = ODP_CLOCK_DEFAULT; param->exp_mode = ODP_TIMER_EXP_AFTER; } @@ -476,12 +556,26 @@ odp_timer_pool_t odp_timer_pool_create(const char *name, timer_entry_t *timer; uint32_t i, num_timers; uint64_t res_ns, nsec_per_scan; + uint64_t max_multiplier = 0; + double base_freq = 0.0; + int periodic = (param->timer_type == ODP_TIMER_TYPE_PERIODIC) ? 1 : 0; if (odp_global_ro.init_param.not_used.feat.timer) { ODP_ERR("Trying to use disabled ODP feature.\n"); return ODP_TIMER_POOL_INVALID; } + if (param->clk_src != ODP_CLOCK_DEFAULT) { + ODP_ERR("Only ODP_CLOCK_DEFAULT supported. Requested %i.\n", param->clk_src); + return ODP_TIMER_POOL_INVALID; + } + + if (param->timer_type != ODP_TIMER_TYPE_SINGLE && + param->timer_type != ODP_TIMER_TYPE_PERIODIC) { + ODP_ERR("Bad timer type %i\n", param->timer_type); + return ODP_TIMER_POOL_INVALID; + } + if ((param->res_ns && param->res_hz) || (param->res_ns == 0 && param->res_hz == 0)) { ODP_ERR("Invalid timeout resolution\n"); @@ -510,6 +604,30 @@ odp_timer_pool_t odp_timer_pool_create(const char *name, else res_ns = GIGA_HZ / param->res_hz; + if (periodic) { + uint64_t max_capa, min_period_ns; + + base_freq = odp_fract_u64_to_dbl(¶m->periodic.base_freq_hz); + max_multiplier = param->periodic.max_multiplier; + + if (base_freq < MIN_BASE_HZ || base_freq > MAX_BASE_HZ) { + ODP_ERR("Bad base frequency: %f\n", base_freq); + return ODP_TIMER_POOL_INVALID; + } + + max_capa = max_multiplier_capa(base_freq); + + if (max_multiplier == 0 || max_multiplier > max_capa) { + ODP_ERR("Bad max multiplier: %" PRIu64 "\n", max_multiplier); + return ODP_TIMER_POOL_INVALID; + } + + min_period_ns = GIGA_HZ / (base_freq * max_multiplier); + + if (res_ns > min_period_ns) + res_ns = min_period_ns; + } + /* Scan timer pool twice during resolution interval */ if (res_ns > ODP_TIME_USEC_IN_NS) nsec_per_scan = res_ns / 2; @@ -560,6 +678,10 @@ odp_timer_pool_t odp_timer_pool_create(const char *name, timer_pool->param = *param; timer_pool->param.res_ns = res_ns; + timer_pool->periodic = periodic; + timer_pool->base_freq = base_freq; + timer_pool->max_multiplier = max_multiplier; + ring_u32_init(&timer_pool->free_timer.ring_hdr); timer_pool->free_timer.ring_mask = num_timers - 1; @@ -916,6 +1038,100 @@ int odp_timer_restart(odp_timer_t timer, const odp_timer_start_t *start_param) return timer_set(timer, start_param->tick, NULL, abs); } +int odp_timer_periodic_start(odp_timer_t timer_hdl, + const odp_timer_periodic_start_t *start_param) +{ + uint64_t period_ns; + uint64_t first_tick; + odp_event_t tmo_ev = start_param->tmo_ev; + timer_entry_t *timer = timer_from_hdl(timer_hdl); + timer_pool_t *tp = timer->timer_pool; + uint64_t multiplier = start_param->freq_multiplier; + double freq = multiplier * tp->base_freq; + int absolute; + int ret; + + if (odp_unlikely(!tp->periodic)) { + ODP_ERR("Not a periodic timer\n"); + return ODP_TIMER_FAIL; + } + + if (odp_unlikely(multiplier == 0 || multiplier > tp->max_multiplier)) { + ODP_ERR("Bad frequency multiplier: %" PRIu64 "\n", multiplier); + return ODP_TIMER_FAIL; + } + + if (odp_unlikely(odp_event_type(tmo_ev) != ODP_EVENT_TIMEOUT)) { + ODP_ERR("Event type is not timeout\n"); + return ODP_TIMER_FAIL; + } + + period_ns = (uint64_t)((double)ODP_TIME_SEC_IN_NS / freq); + if (period_ns == 0) { + ODP_ERR("Too high periodic timer frequency: %f\n", freq); + return ODP_TIMER_FAIL; + } + + timer->periodic_ticks = odp_timer_ns_to_tick(timer_pool_to_hdl(tp), period_ns); + first_tick = timer->periodic_ticks; + absolute = 0; + + if (start_param->first_tick) { + first_tick = start_param->first_tick; + absolute = 1; + } + + ret = timer_set(timer_hdl, first_tick, &tmo_ev, absolute); + if (odp_unlikely(ret != ODP_TIMER_SUCCESS)) + return ret; + + /* Check that timer was not active */ + if (odp_unlikely(tmo_ev != ODP_EVENT_INVALID)) { + ODP_ERR("Timer was active already\n"); + odp_event_free(tmo_ev); + } + + return ODP_TIMER_SUCCESS; +} + +int odp_timer_periodic_ack(odp_timer_t timer_hdl, odp_event_t tmo_ev) +{ + uint64_t abs_tick; + odp_timeout_t tmo = odp_timeout_from_event(tmo_ev); + timer_entry_t *timer = timer_from_hdl(timer_hdl); + odp_timeout_hdr_t *timeout_hdr; + int ret; + + if (odp_unlikely(odp_event_type(tmo_ev) != ODP_EVENT_TIMEOUT)) { + ODP_ERR("Event type is not timeout\n"); + return -1; + } + + abs_tick = timer->periodic_ticks; + + if (odp_unlikely(abs_tick == PERIODIC_CANCELLED)) + return 2; + + timeout_hdr = timeout_to_hdr(tmo); + abs_tick += timeout_hdr->expiration; + timeout_hdr->expiration = abs_tick; + + ret = timer_set(timer_hdl, abs_tick, NULL, 1); + if (odp_likely(ret == ODP_TIMER_SUCCESS)) + return 0; + + /* Send delayed timeout immediately to catch-up */ + if (ret == ODP_TIMER_TOO_NEAR) { + if (odp_unlikely(odp_queue_enq(timer->queue, tmo_ev))) { + ODP_ERR("Failed to enqueue catch-up timeout event\n"); + return -1; + } + return 0; + } + ODP_ERR("Failed to re-arm periodic timer: %d\n", ret); + return -1; +} + int odp_timer_cancel(odp_timer_t timer_hdl, odp_event_t *tmo_ev) { timer_entry_t *timer = timer_from_hdl(timer_hdl); @@ -941,6 +1157,48 @@ int odp_timer_cancel(odp_timer_t timer_hdl, odp_event_t *tmo_ev) return 0; } +int odp_timer_periodic_cancel(odp_timer_t timer_hdl) +{ + timer_pool_t *tp; + timer_entry_t *timer; + odp_event_t event; + int ret; + + if (odp_unlikely(timer_hdl == ODP_TIMER_INVALID)) { + ODP_ERR("Bad timer handle\n"); + return -1; + } + + timer = timer_from_hdl(timer_hdl); + tp = timer->timer_pool; + event = timer->tmo_event; + + if (odp_unlikely(!tp->periodic)) { + ODP_ERR("Not a periodic timer\n"); + return -1; + } + + odp_ticketlock_lock(&timer->lock); + + ret = timer_global->ops.stop(&timer->rte_timer); + + /* Mark timer cancelled, so that a following ack call stops restarting it. */ + timer->periodic_ticks = PERIODIC_CANCELLED; + + /* Timer successfully cancelled, so send the final event manually. */ + if (ret == 0 && timer->state == TICKING) { + timer->state = NOT_TICKING; + if (odp_unlikely(odp_queue_enq(timer->queue, event))) { + ODP_ERR("Failed to enqueue final timeout event\n"); + _odp_event_free(event); + } + } + + odp_ticketlock_unlock(&timer->lock); + + return 0; +} + uint64_t odp_timer_to_u64(odp_timer_t timer_hdl) { return (uint64_t)(uintptr_t)timer_hdl; @@ -966,6 +1224,9 @@ int odp_timeout_fresh(odp_timeout_t tmo) odp_timeout_hdr_t *timeout_hdr = timeout_to_hdr(tmo); timer_entry_t *timer = timer_from_hdl(timeout_hdr->timer); + if (timer->timer_pool->periodic) + return timer->periodic_ticks != PERIODIC_CANCELLED; + /* Check if timer has been reused after timeout sent. */ return timeout_hdr->expiration == timer->tick; } @@ -979,9 +1240,7 @@ odp_timer_t odp_timeout_timer(odp_timeout_t tmo) uint64_t odp_timeout_tick(odp_timeout_t tmo) { - odp_timeout_hdr_t *timeout_hdr = timeout_to_hdr(tmo); - - return timeout_hdr->expiration; + return timeout_to_hdr(tmo)->expiration; } void *odp_timeout_user_ptr(odp_timeout_t tmo) @@ -1032,6 +1291,7 @@ void odp_timer_pool_print(odp_timer_pool_t timer_pool) ODP_PRINT(" num timers %u\n", tp->cur_timers); ODP_PRINT(" hwm timers %u\n", tp->hwm_timers); ODP_PRINT(" num tp %i\n", timer_global->num_timer_pools); + ODP_PRINT(" periodic %" PRIu8 "\n", tp->periodic); ODP_PRINT("\n"); } @@ -1053,36 +1313,37 @@ void odp_timer_print(odp_timer_t timer_hdl) ODP_PRINT(" state %s\n", (timer->state == NOT_TICKING) ? "not ticking" : (timer->state == EXPIRED ? "expired" : "ticking")); + ODP_PRINT(" periodic ticks %" PRIu64 "\n", timer->periodic_ticks); ODP_PRINT("\n"); } void odp_timeout_print(odp_timeout_t tmo) { - const odp_timeout_hdr_t *timeout_hdr = timeout_to_hdr(tmo); - odp_timer_t timer_hdl; - timer_pool_t *tp = NULL; - uint32_t idx = 0; + const odp_timeout_hdr_t *tmo_hdr; + odp_timer_t timer; if (tmo == ODP_TIMEOUT_INVALID) { ODP_ERR("Bad timeout handle\n"); return; } - timer_hdl = timeout_hdr->timer; - - if (timer_hdl != ODP_TIMER_INVALID) { - timer_entry_t *timer = timer_from_hdl(timer_hdl); - - tp = timer->timer_pool; - idx = timer->timer_idx; - } + tmo_hdr = timeout_to_hdr(tmo); + timer = tmo_hdr->timer; ODP_PRINT("\nTimeout info\n"); ODP_PRINT("------------\n"); ODP_PRINT(" tmo handle 0x%" PRIx64 "\n", odp_timeout_to_u64(tmo)); - ODP_PRINT(" timer pool %p\n", (void *)tp); - ODP_PRINT(" timer index %u\n", idx); - ODP_PRINT(" expiration %" PRIu64 "\n", timeout_hdr->expiration); - ODP_PRINT(" user ptr %p\n", timeout_hdr->user_ptr); + ODP_PRINT(" expiration %" PRIu64 "\n", tmo_hdr->expiration); + ODP_PRINT(" user ptr %p\n", tmo_hdr->user_ptr); + + if (timer != ODP_TIMER_INVALID) { + timer_entry_t *timer_entry = timer_from_hdl(timer); + timer_pool_t *tp = timer_entry->timer_pool; + + ODP_PRINT(" timer pool %p\n", (void *)tp); + ODP_PRINT(" timer index %u\n", timer_entry->timer_idx); + ODP_PRINT(" periodic %i\n", tp->periodic); + } + ODP_PRINT("\n"); } |