> -----Original Message----- > From: lng-odp-boun...@lists.linaro.org [mailto:lng-odp- > boun...@lists.linaro.org] On Behalf Of ext Ola Liljedahl > Sent: Monday, January 05, 2015 8:24 PM > To: lng-odp@lists.linaro.org > Subject: [lng-odp] [PATCHv3 2/3] api: odp_timer.h: updated API, lock-less > implementation > > Signed-off-by: Ola Liljedahl <ola.liljed...@linaro.org> > > (This document/code contribution attached is provided under the terms of > agreement LES-LTM-21309) > The timer API is updated according to > https://docs.google.com/a/linaro.org/document/d/1bfY_J8ecLJPsFTmYftb0NVmGn > B9qkEc_NpcJ87yfaD8 > A major change is that timers are allocated and freed separately from > timeouts being set and cancelled. The life-length of a timer normally > corresponds to the life-length of the associated stateful flow while > the life-length of a timeout corresponds to individual packets being > transmitted and received. > The reference timer implementation is lock-less for platforms with > support for 128-bit (16-byte) atomic exchange and CAS operations. > Otherwise a lock-based implementation (using as many locks as desired) > is used but some operations (e.g. reset reusing existing timeout buffer) > may still be lock-less. > Updated the example example/timer/odp_timer_test.c according to the > updated API. > Updated the API according to Petri's review comments. > --- > example/timer/odp_timer_test.c | 177 ++-- > platform/linux-generic/include/api/odp_timer.h | 318 ++++-- > .../linux-generic/include/odp_timer_internal.h | 59 +- > platform/linux-generic/odp_timer.c | 1064 ++++++++++++++- > ----- > 4 files changed, 1139 insertions(+), 479 deletions(-) > > diff --git a/example/timer/odp_timer_test.c > b/example/timer/odp_timer_test.c > index 2acf2fc..71f72b4 100644 > --- a/example/timer/odp_timer_test.c > +++ b/example/timer/odp_timer_test.c > @@ -26,7 +26,6 @@ > > > #define MAX_WORKERS 32 /**< Max worker threads */ > -#define MSG_POOL_SIZE (4*1024*1024) /**< Message pool size */ > #define MSG_NUM_BUFS 10000 /**< Number of timers */ > > > @@ -44,69 +43,119 @@ typedef struct { > /** @private Barrier for test synchronisation */ > static odp_barrier_t test_barrier; > > -/** @private Timer handle*/ > -static odp_timer_t test_timer; > +/** @private Buffer pool handle */ > +static odp_buffer_pool_t pool; > > +/** @private Timer pool handle */ > +static odp_timer_pool_t tp; > + > +/** @private Number of timeouts to receive */ > +static odp_atomic_u32_t remain; > + > +/** @private Timer set status ASCII strings */ > +static const char *timerset2str(odp_timer_set_t val) > +{ > + switch (val) { > + case ODP_TIMER_SET_SUCCESS: > + return "success"; > + case ODP_TIMER_SET_TOOEARLY: > + return "too early"; > + case ODP_TIMER_SET_TOOLATE: > + return "too late"; > + case ODP_TIMER_SET_NOBUF: > + return "no buffer"; > + default: > + return "?"; > + } > +}; > + > +/** @private Helper struct for timers */ > +struct test_timer { > + odp_timer_t tim; > + odp_buffer_t buf; > +}; > + > +/** @private Array of all timer helper structs */ > +static struct test_timer tt[256]; > > /** @private test timeout */ > static void test_abs_timeouts(int thr, test_args_t *args) > { > - uint64_t tick; > uint64_t period; > uint64_t period_ns; > odp_queue_t queue; > - odp_buffer_t buf; > - int num; > + uint64_t tick; > + struct test_timer *ttp; > > EXAMPLE_DBG(" [%i] test_timeouts\n", thr); > > queue = odp_queue_lookup("timer_queue"); > > period_ns = args->period_us*ODP_TIME_USEC; > - period = odp_timer_ns_to_tick(test_timer, period_ns); > + period = odp_timer_ns_to_tick(tp, period_ns); > > EXAMPLE_DBG(" [%i] period %"PRIu64" ticks, %"PRIu64" ns\n", thr, > period, period_ns); > > - tick = odp_timer_current_tick(test_timer); > - > - EXAMPLE_DBG(" [%i] current tick %"PRIu64"\n", thr, tick); > + EXAMPLE_DBG(" [%i] current tick %"PRIu64"\n", thr, > + odp_timer_current_tick(tp)); > > - tick += period; > - > - if (odp_timer_absolute_tmo(test_timer, tick, queue, > ODP_BUFFER_INVALID) > - == ODP_TIMER_TMO_INVALID){ > - EXAMPLE_DBG("Timeout request failed\n"); > + ttp = &tt[thr - 1]; /* Thread starts at 1 */ > + ttp->tim = odp_timer_alloc(tp, queue, ttp); > + if (ttp->tim == ODP_TIMER_INVALID) { > + EXAMPLE_ERR("Failed to allocate timer\n"); > return; > } > + ttp->buf = odp_buffer_alloc(pool); > + if (ttp->buf == ODP_BUFFER_INVALID) { > + EXAMPLE_ERR("Failed to allocate buffer\n"); > + return; > + } > + tick = odp_timer_current_tick(tp); > > - num = args->tmo_count; > - > - while (1) { > - odp_timeout_t tmo; > + while ((int)odp_atomic_load_u32(&remain) > 0) { > + odp_buffer_t buf; > + odp_timer_set_t rc; > > - buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); > + tick += period; > + rc = odp_timer_set_abs(ttp->tim, tick, &ttp->buf); > + if (odp_unlikely(rc != ODP_TIMER_SET_SUCCESS)) { > + /* Too early or too late timeout requested */ > + EXAMPLE_ABORT("odp_timer_set_abs() failed: %s\n", > + timerset2str(rc)); > + } > > - tmo = odp_timeout_from_buffer(buf); > + /* Get the next expired timeout */ > + buf = odp_schedule(&queue, ODP_SCHED_WAIT);
It's possible that scheduler gives last N tmos to single thread. In that case N other threads are stuck here waiting for ever. You should either poll "remain" (with some odp_schedule() wait period) or otherwise ensure that all threads are signaled when the test is finished. > + if (odp_buffer_type(buf) != ODP_BUFFER_TYPE_TIMEOUT) { > + /* Not a default timeout buffer */ > + EXAMPLE_ABORT("Unexpected buffer type (%u) received\n", > + odp_buffer_type(buf)); > + } > + odp_timeout_t tmo = odp_timeout_from_buf(buf); > tick = odp_timeout_tick(tmo); > - > + ttp = odp_timeout_user_ptr(tmo); > + ttp->buf = buf; > + if (!odp_timeout_fresh(tmo)) { > + /* Not the expected expiration tick, timer has > + * been reset or cancelled or freed */ > + EXAMPLE_ABORT("Unexpected timeout received (timer %x, > tick %"PRIu64")\n", > + ttp->tim, tick); > + } > EXAMPLE_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, tick); > > - odp_buffer_free(buf); > - > - num--; > - > - if (num == 0) > - break; > - > - tick += period; > - > - odp_timer_absolute_tmo(test_timer, tick, > - queue, ODP_BUFFER_INVALID); > + odp_atomic_dec_u32(&remain); > } > > - if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) > - odp_schedule_release_atomic(); > + /* Cancel and free last timer used */ > + (void)odp_timer_cancel(ttp->tim, &ttp->buf); > + if (ttp->buf != ODP_BUFFER_INVALID) > + odp_buffer_free(ttp->buf); > + else > + EXAMPLE_ERR("Lost timeout buffer at timer cancel\n"); > + /* Since we have cancelled the timer, there is no timeout buffer to > + * return from odp_timer_free() */ > + (void)odp_timer_free(ttp->tim); > } > ... > > diff --git a/platform/linux-generic/include/api/odp_timer.h > b/platform/linux-generic/include/api/odp_timer.h > index 6cca27c..6961e81 100644 > --- a/platform/linux-generic/include/api/odp_timer.h > +++ b/platform/linux-generic/include/api/odp_timer.h > @@ -8,7 +8,7 @@ > /** > * @file > * > - * ODP timer > + * ODP timer service > */ > > #ifndef ODP_TIMER_H_ > @@ -18,6 +18,7 @@ > extern "C" { > #endif > > +#include <stdlib.h> > #include <odp_std_types.h> > #include <odp_buffer.h> > #include <odp_buffer_pool.h> > @@ -27,140 +28,335 @@ extern "C" { > * @{ > */ > > +struct odp_timer_pool_s; /**< Forward declaration */ > + > +/** > +* ODP timer pool handle (platform dependent) > +*/ > +typedef struct odp_timer_pool_s *odp_timer_pool_t; > + > /** > - * ODP timer handle > + * Invalid timer pool handle (platform dependent). > */ > +#define ODP_TIMER_POOL_INVALID NULL > + > +/** > + * Clock sources for timers in timer pool. > + */ > +typedef enum { > + /** Use CPU clock as clock source for timers */ > + ODP_CLOCK_CPU, > + /** Use external clock as clock source for timers */ > + ODP_CLOCK_EXT > + /* Platform dependent which other clock sources exist */ > +} odp_timer_clk_src_t; > + > +/** > +* ODP timer handle (platform dependent). > +*/ > typedef uint32_t odp_timer_t; > > -/** Invalid timer */ > -#define ODP_TIMER_INVALID 0 > +/** > +* ODP timeout handle (platform dependent). > +*/ > +typedef void *odp_timeout_t; > > +/** > + * Invalid timer handle (platform dependent). > + */ > +#define ODP_TIMER_INVALID ((uint32_t)~0U) > > /** > - * ODP timeout handle > + * Return values of timer set calls. > + */ > +typedef enum { > +/** > + * Timer set operation succeeded > */ > -typedef odp_buffer_t odp_timer_tmo_t; > + ODP_TIMER_SET_SUCCESS = 0, > +/** > + * Timer set operation failed, expiration too early. > + * Either retry with a later expiration time or process the timeout > + * immediately. */ > + ODP_TIMER_SET_TOOEARLY = -1, > > -/** Invalid timeout */ > -#define ODP_TIMER_TMO_INVALID 0 > +/** > + * Timer set operation failed, expiration too late. > + * Truncate the expiration time against the maximum timeout for the > + * timer pool. */ > + ODP_TIMER_SET_TOOLATE = -2, > +/** > + * Timer set operation failed because no timeout buffer specified of Typo? "or present" > present > + * in timer (timer inactive/expired). > + */ > + ODP_TIMER_SET_NOBUF = -3 > +} odp_timer_set_t; > > +/** Maximum timer pool name length in chars (including null char) */ > +#define ODP_TIMER_POOL_NAME_LEN 32 > > -/** > - * Timeout notification > +/** Timer pool parameters > + * Timer pool parameters are used when creating and querying timer pools. > */ > -typedef odp_buffer_t odp_timeout_t; > +typedef struct { > + uint64_t res_ns; /**< Timeout resolution in nanoseconds */ > + uint64_t min_tmo; /**< Minimum relative timeout in nanoseconds */ > + uint64_t max_tmo; /**< Maximum relative timeout in nanoseconds */ > + uint32_t num_timers; /**< (Minimum) number of supported timers */ > + int private; /**< Shared (false) or private (true) timer pool */ > + odp_timer_clk_src_t clk_src; /**< Clock source for timers */ > +} odp_timer_pool_param_t; > > +/** > + * Create a timer pool > + * > + * @param name Name of the timer pool. The string will be copied. > + * @param buf_pool Buffer pool for allocating timeouts > + * @param params Timer pool parameters. The content will be copied. > + * > + * @return Timer pool handle if successful, otherwise > ODP_TIMER_POOL_INVALID > + * and errno set This is the first API to use errno. Errno needs to be changed to odp_errno() (with odp_errno_clear(), odp_errno_print(), odp_errno_str()). That can be done after merge. > + */ > +odp_timer_pool_t > +odp_timer_pool_create(const char *name, > + odp_buffer_pool_t buf_pool, > + const odp_timer_pool_param_t *params); > > /** > - * Create a timer > + * Start a timer pool > * > - * Creates a new timer with requested properties. > + * Start all created timer pools, enabling the allocation of timers. > + * The purpose of this call is to coordinate the creation of multiple > timer > + * pools that may use the same underlying HW resources. > + * This function may be called multiple times. > + */ > +void odp_timer_pool_start(void); > + > +/** > + * Destroy a timer pool > * > - * @param name Name > - * @param pool Buffer pool for allocating timeout notifications > - * @param resolution Timeout resolution in nanoseconds > - * @param min_tmo Minimum timeout duration in nanoseconds > - * @param max_tmo Maximum timeout duration in nanoseconds > + * Destroy a timer pool, freeing all resources. > + * All timers must have been freed. > * > - * @return Timer handle if successful, otherwise ODP_TIMER_INVALID > + * @param tpid Timer pool identifier > */ > -odp_timer_t odp_timer_create(const char *name, odp_buffer_pool_t pool, > - uint64_t resolution, uint64_t min_tmo, > - uint64_t max_tmo); > +void odp_timer_pool_destroy(odp_timer_pool_t tpid); > > /** > * Convert timer ticks to nanoseconds > * > - * @param timer Timer > + * @param tpid Timer pool identifier > * @param ticks Timer ticks > * > * @return Nanoseconds > */ > -uint64_t odp_timer_tick_to_ns(odp_timer_t timer, uint64_t ticks); > +uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, uint64_t ticks); > > /** > * Convert nanoseconds to timer ticks > * > - * @param timer Timer > + * @param tpid Timer pool identifier > * @param ns Nanoseconds > * > * @return Timer ticks > */ > -uint64_t odp_timer_ns_to_tick(odp_timer_t timer, uint64_t ns); > +uint64_t odp_timer_ns_to_tick(odp_timer_pool_t tpid, uint64_t ns); > > /** > - * Timer resolution in nanoseconds > + * Current tick value > * > - * @param timer Timer > + * @param tpid Timer pool identifier > * > - * @return Resolution in nanoseconds > + * @return Current time in timer ticks > + */ > +uint64_t odp_timer_current_tick(odp_timer_pool_t tpid); > + > +/** > + * ODP timer pool information and configuration > */ > -uint64_t odp_timer_resolution(odp_timer_t timer); > + > +typedef struct { > + odp_timer_pool_param_t param; /**< Parameters specified at creation > */ > + uint32_t cur_timers; /**< Number of currently allocated timers */ > + uint32_t hwm_timers; /**< High watermark of allocated timers */ > + const char *name; /**< Name of timer pool */ > +} odp_timer_pool_info_t; > > /** > - * Maximum timeout in timer ticks > + * Query timer pool configuration and current state > * > - * @param timer Timer > + * @param tpid Timer pool identifier > + * @param[out] info Pointer to information buffer > * > - * @return Maximum timeout in timer ticks > + * @retval 0 Success > + * @retval -1 Failure. Info could not be retrieved. > */ > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer); > +int odp_timer_pool_info(odp_timer_pool_t tpid, > + odp_timer_pool_info_t *info); > > /** > - * Current timer tick > + * Allocate a timer > * > - * @param timer Timer > + * Create a timer (allocating all necessary resources e.g. timeout event) > from > + * the timer pool. The user_ptr is copied to timeouts and can be > retrieved > + * using the odp_timer_userptr() call. ... odp_timeout_user_ptr() call. > * > - * @return Current time in timer ticks > + * @param tpid Timer pool identifier > + * @param queue Destination queue for timeout notifications > + * @param user_ptr User defined pointer or NULL to be copied to timeouts > + * > + * @return Timer handle if successful, otherwise ODP_TIMER_INVALID and > + * errno set. > */ > -uint64_t odp_timer_current_tick(odp_timer_t timer); > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > + odp_queue_t queue, > + void *user_ptr); > > /** > - * Request timeout with an absolute timer tick > + * Free a timer > * > - * When tick reaches tmo_tick, the timer enqueues the timeout > notification into > - * the destination queue. > + * Free (destroy) a timer, reclaiming associated resources. > + * The timeout buffer for an active timer will be returned. > + * The timeout buffer for an expired timer will not be returned. It is > the > + * responsibility of the application to handle this timeout when it is > received. > * > - * @param timer Timer > - * @param tmo_tick Absolute timer tick value which triggers the timeout > - * @param queue Destination queue for the timeout notification > - * @param buf User defined timeout notification buffer. When > - * ODP_BUFFER_INVALID, default timeout notification is > used. > + * @param tim Timer handle > + * @return Buffer handle of timeout buffer or ODP_BUFFER_INVALID > + */ > +odp_buffer_t odp_timer_free(odp_timer_t tim); > + > +/** > + * Set a timer (absolute time) with a user-provided timeout buffer > + * > + * Set (arm) the timer to expire at specific time. The timeout > + * buffer will be enqueued when the timer expires. > + * > + * Note: any invalid parameters will be treated as programming errors and > will > + * cause the application to abort. > + * > + * @param tim Timer > + * @param abs_tck Expiration time in absolute timer ticks > + * @param tmo_buf Reference to a buffer variable that points to timeout > buffer > + * or NULL to reuse the existing timeout buffer Tmo_buf parameter usage needs clarification: - Can this be always NULL, meaning that timer should be set with a tmo buffer allocated from the pool provided in timer_pool_create call? - Is it also an output param? Timer_reset() writes it with old buffer handle value. > + * > + * @retval ODP_TIMER_SET_SUCCESS Operation succeeded > + * @retval ODP_TIMER_SET_TOOEARLY Operation failed because expiration > tick too > + * early > + * @retval ODP_TIMER_SET_TOOLATE Operation failed because expiration tick > too > + * late > + * @retval ODP_TIMER_SET_NOBUF Operation failed because timeout buffer > not > + * specified in call and not present in timer When this actually happens? When trying to reset an expired timer ? > + */ > +int odp_timer_set_abs(odp_timer_t tim, > + uint64_t abs_tck, > + odp_buffer_t *tmo_buf); > + > +/** > + * Set a timer with a relative expiration time and user-provided buffer. > + * > + * Set (arm) the timer to expire at a relative future time. > + * > + * Note: any invalid parameters will be treated as programming errors and > will > + * cause the application to abort. > + * > + * @param tim Timer > + * @param rel_tck Expiration time in timer ticks relative to current > time of > + * the timer pool the timer belongs to > + * @param tmo_buf Reference to a buffer variable that points to timeout > buffer > + * or NULL to reuse the existing timeout buffer > + * > + * @retval ODP_TIMER_SET_SUCCESS Operation succeeded > + * @retval ODP_TIMER_SET_TOOEARLY Operation failed because expiration > tick too > + * early > + * @retval ODP_TIMER_SET_TOOLATE Operation failed because expiration tick > too > + * late > + * @retval ODP_TIMER_SET_NOBUF Operation failed because timeout buffer > not > + * specified in call and not present in timer > + */ > +int odp_timer_set_rel(odp_timer_t tim, > + uint64_t rel_tck, > + odp_buffer_t *tmo_buf); > + > +/** > + * Cancel a timer > * > - * @return Timeout handle if successful, otherwise ODP_TIMER_TMO_INVALID > + * Cancel a timer, preventing future expiration and delivery. Return any > + * present timeout buffer. > + * > + * A timer that has already expired may be impossible to cancel and the > timeout > + * will instead be delivered to the destination queue. > + * > + * Note: any invalid parameters will be treated as programming errors and > will > + * cause the application to abort. > + * > + * @param tim Timer > + * @param[out] tmo_buf Pointer to a buffer variable > + * @retval 0 Success, active timer cancelled, timeout returned in > '*tmo_buf' > + * @retval -1 Failure, timer already expired (or inactive) > */ > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, uint64_t > tmo_tick, > - odp_queue_t queue, odp_buffer_t buf); > +int odp_timer_cancel(odp_timer_t tim, odp_buffer_t *tmo_buf); > > /** > - * Cancel a timeout > + * Return timeout handle that is associated with timeout buffer > + * > + * Note: any invalid parameters will cause undefined behavior and may > cause > + * the application to abort or crash. > * > - * @param timer Timer > - * @param tmo Timeout to cancel > + * @param buf A buffer of type ODP_BUFFER_TYPE_TIMEOUT > + * > + * @return timeout handle > + */ > +odp_timeout_t odp_timeout_from_buf(odp_buffer_t buf); > + > +/** > + * Check for fresh timeout > + * If the corresponding timer has been reset or cancelled since this > timeout > + * was enqueued, the timeout is stale (not fresh). > * > - * @return 0 if successful > + * @param tmo Timeout handle > + * @retval 1 Timeout is fresh > + * @retval 0 Timeout is stale > */ > -int odp_timer_cancel_tmo(odp_timer_t timer, odp_timer_tmo_t tmo); > +int /*odp_bool_t*/odp_timeout_fresh(odp_timeout_t tmo); Remove /*odp_bool_t*/ -Petri > > /** > - * Convert buffer handle to timeout handle > + * Return timer handle for the timeout > * > - * @param buf Buffer handle > + * Note: any invalid parameters will cause undefined behavior and may > cause > + * the application to abort or crash. > * > - * @return Timeout buffer handle > + * @param tmo Timeout handle > + * > + * @return Timer handle > */ > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf); > +odp_timer_t odp_timeout_timer(odp_timeout_t tmo); > > /** > - * Return absolute timeout tick > + * Return expiration tick for the timeout > + * > + * Note: any invalid parameters will cause undefined behavior and may > cause > + * the application to abort or crash. > * > - * @param tmo Timeout buffer handle > + * @param tmo Timeout handle > * > - * @return Absolute timeout tick > + * @return Expiration tick > */ > uint64_t odp_timeout_tick(odp_timeout_t tmo); > > /** > + * Return user pointer for the timeout > + * The user pointer was specified when the timer was allocated. > + * > + * Note: any invalid parameters will cause undefined behavior and may > cause > + * the application to abort or crash. > + * > + * @param tmo Timeout handle > + * > + * @return User pointer > + */ > +void *odp_timeout_user_ptr(odp_timeout_t tmo); > + > +/** > * @} > */ _______________________________________________ lng-odp mailing list lng-odp@lists.linaro.org http://lists.linaro.org/mailman/listinfo/lng-odp