aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatias Elo <matias.elo@nokia.com>2023-06-06 09:09:31 +0300
committerMatias Elo <matias.elo@nokia.com>2023-08-08 09:54:40 +0300
commit29e1538494bfd653b3193098ec67447cb3a7b39b (patch)
tree2efadc30ea8be69355181954e13a1a651a005e82
parentc6b21e7a635dc4f7ee7c36500ccac8a8f1b7f402 (diff)
Port 2118ba7c9 "linux-gen: timer: fix periodic timer drift"
Port original commit from linux-generic. Signed-off-by: Matias Elo <matias.elo@nokia.com> Reviewed-by: Tuomas Taipale <tuomas.taipale@nokia.com>
-rw-r--r--platform/linux-dpdk/odp_timer.c27
1 files changed, 25 insertions, 2 deletions
diff --git a/platform/linux-dpdk/odp_timer.c b/platform/linux-dpdk/odp_timer.c
index eb5eed88a..0fe18a95b 100644
--- a/platform/linux-dpdk/odp_timer.c
+++ b/platform/linux-dpdk/odp_timer.c
@@ -93,6 +93,9 @@ ODP_STATIC_ASSERT(MAX_TIMERS < MAX_TIMER_RING_SIZE,
/* Maximum number of periodic timers per pool */
#define MAX_PERIODIC_TIMERS 100
+/* Periodic tick fractional part accumulator size */
+#define ACC_SIZE (1ull << 32)
+
typedef struct {
odp_ticketlock_t lock;
uint64_t tick;
@@ -102,8 +105,13 @@ typedef struct {
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;
+ /* Periodic ticks fractional part. */
+ uint32_t periodic_ticks_frac;
+ /* Periodic ticks fractional part accumulator. */
+ uint32_t periodic_ticks_frac_acc;
struct rte_timer rte_timer;
@@ -1065,6 +1073,7 @@ int odp_timer_periodic_start(odp_timer_t timer_hdl,
timer_pool_t *tp = timer->timer_pool;
uint64_t multiplier = start_param->freq_multiplier;
double freq = multiplier * tp->base_freq;
+ double period_ns_dbl;
int absolute;
int ret;
@@ -1083,13 +1092,18 @@ int odp_timer_periodic_start(odp_timer_t timer_hdl,
return ODP_TIMER_FAIL;
}
- period_ns = (uint64_t)((double)ODP_TIME_SEC_IN_NS / freq);
+ period_ns_dbl = (double)ODP_TIME_SEC_IN_NS / freq;
+ period_ns = period_ns_dbl;
+
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);
+ timer->periodic_ticks_frac = (period_ns_dbl - period_ns) * ACC_SIZE;
+ timer->periodic_ticks_frac_acc = 0;
+
first_tick = timer->periodic_ticks;
absolute = 0;
@@ -1113,7 +1127,7 @@ int odp_timer_periodic_start(odp_timer_t timer_hdl,
int odp_timer_periodic_ack(odp_timer_t timer_hdl, odp_event_t tmo_ev)
{
- uint64_t abs_tick;
+ uint64_t abs_tick, acc;
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;
@@ -1129,6 +1143,15 @@ int odp_timer_periodic_ack(odp_timer_t timer_hdl, odp_event_t tmo_ev)
if (odp_unlikely(abs_tick == PERIODIC_CANCELLED))
return 2;
+ acc = (uint64_t)timer->periodic_ticks_frac_acc + (uint64_t)timer->periodic_ticks_frac;
+
+ if (acc >= ACC_SIZE) {
+ abs_tick++;
+ acc -= ACC_SIZE;
+ }
+
+ timer->periodic_ticks_frac_acc = acc;
+
timeout_hdr = timeout_to_hdr(tmo);
abs_tick += timeout_hdr->expiration;
timeout_hdr->expiration = abs_tick;