Message ID | 1409671615-27117-1-git-send-email-ola.liljedahl@linaro.org |
---|---|
State | New |
Headers | show |
Any volunteers for reviewing this patch? -- Ola On 2 September 2014 17:26, Ola Liljedahl <ola.liljedahl@linaro.org> wrote: > Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org> > --- > (This document/code contribution attached is provided under the terms of > agreement LES-LTM-21309) > New timer API and corresponding SW implementation for linux-generic. > Read more about use cases and usage here: > > https://docs.google.com/a/linaro.org/document/d/1bfY_J8ecLJPsFTmYftb0NVmGnB9qkEc_NpcJ87yfaD8/edit# > > example/timer/odp_timer_test.c | 93 ++- > platform/linux-generic/Makefile.am | 1 + > platform/linux-generic/include/api/odp_timer.h | 478 ++++++++++-- > .../linux-generic/include/odp_timer_internal.h | 71 +- > platform/linux-generic/odp_timer.c | 823 > +++++++++++++-------- > platform/linux-generic/priority_queue.c | 289 ++++++++ > platform/linux-generic/priority_queue.h | 107 +++ > test/api_test/odp_timer_ping.c | 58 +- > 8 files changed, 1455 insertions(+), 465 deletions(-) > create mode 100644 platform/linux-generic/priority_queue.c > create mode 100644 platform/linux-generic/priority_queue.h > > diff --git a/example/timer/odp_timer_test.c > b/example/timer/odp_timer_test.c > index bf1d7df..0b4fedf 100644 > --- a/example/timer/odp_timer_test.c > +++ b/example/timer/odp_timer_test.c > @@ -35,7 +35,6 @@ > typedef struct { > int core_count; /**< Core count*/ > int resolution_us; /**< Timeout resolution in usec*/ > - int min_us; /**< Minimum timeout in usec*/ > int max_us; /**< Maximum timeout in usec*/ > int period_us; /**< Timeout period in usec*/ > int tmo_count; /**< Timeout count*/ > @@ -45,18 +44,16 @@ typedef struct { > /** @private Barrier for test synchronisation */ > static odp_barrier_t test_barrier; > > -/** @private Timer handle*/ > -static odp_timer_t test_timer; > +/** @private Timer pool handle*/ > +static odp_timer_pool_t tp; > > > /** @private test timeout */ > static void test_abs_timeouts(int thr, test_args_t *args) > { > - uint64_t tick; > - uint64_t period; > + odp_timer_tick_t period; > uint64_t period_ns; > odp_queue_t queue; > - odp_buffer_t buf; > int num; > > ODP_DBG(" [%i] test_timeouts\n", thr); > @@ -64,37 +61,51 @@ static void test_abs_timeouts(int thr, test_args_t > *args) > queue = odp_queue_lookup("timer_queue"); > > period_ns = args->period_us*USEC; > - period = odp_timer_ns_to_tick(test_timer, period_ns); > + period = odp_timer_ns_to_tick(tp, period_ns); > > ODP_DBG(" [%i] period %"PRIu64" ticks, %"PRIu64" ns\n", thr, > period, period_ns); > > - tick = odp_timer_current_tick(test_timer); > + ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, > + odp_timer_current_tick(tp)); > > - ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick); > - > - tick += period; > - > - if (odp_timer_absolute_tmo(test_timer, tick, queue, > ODP_BUFFER_INVALID) > - == ODP_TIMER_TMO_INVALID){ > - ODP_DBG("Timeout request failed\n"); > + odp_timer_t test_timer; > + test_timer = odp_timer_alloc(tp, queue, NULL); > + if (test_timer == ODP_TIMER_INVALID) { > + ODP_ERR("Failed to allocate timer\n"); > return; > } > + odp_timer_set_rel(test_timer, period); > > num = args->tmo_count; > > while (1) { > - odp_timeout_t tmo; > - > - buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); > + /* Local variables because received timeouts may not > + originate from timer we created above */ > + odp_timer_tmo_t tmo; > + odp_timer_tick_t tick; > + odp_timer_t hdl; > + > + /* Get the next ready buffer/timeout */ > + tmo = odp_schedule_one(&queue, ODP_SCHED_WAIT); > + switch (odp_timer_tmo_status(tmo)) { > + case ODP_TMO_FRESH: > + break; > + case ODP_TMO_STALE: > + ODP_DBG("[%i] Stale timeout received\n", thr); > + break; > + case ODP_TMO_ORPHAN: > + ODP_DBG("[%i] Orphaned timeout received\n", > + thr); > + odp_buffer_free(tmo); > + continue; > + } > > - tmo = odp_timeout_from_buffer(buf); > - tick = odp_timeout_tick(tmo); > + tick = odp_timer_get_expiry(tmo); > + hdl = odp_timer_get_handle(tmo); > > ODP_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, tick); > > - odp_buffer_free(buf); > - > num--; > > if (num == 0) > @@ -102,10 +113,13 @@ static void test_abs_timeouts(int thr, test_args_t > *args) > > tick += period; > > - odp_timer_absolute_tmo(test_timer, tick, > - queue, ODP_BUFFER_INVALID); > + if (hdl != ODP_TIMER_INVALID) > + odp_timer_set_abs(hdl, tick); > } > > + odp_timer_cancel(test_timer); > + odp_timer_free(test_timer); > + > if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) > odp_schedule_release_atomic(); > } > @@ -159,7 +173,6 @@ static void print_usage(void) > printf("Options:\n"); > printf(" -c, --count <number> core count, core IDs start from > 1\n"); > printf(" -r, --resolution <us> timeout resolution in usec\n"); > - printf(" -m, --min <us> minimum timeout in usec\n"); > printf(" -x, --max <us> maximum timeout in usec\n"); > printf(" -p, --period <us> timeout period in usec\n"); > printf(" -t, --timeouts <count> timeout repeat count\n"); > @@ -183,7 +196,6 @@ static void parse_args(int argc, char *argv[], > test_args_t *args) > static struct option longopts[] = { > {"count", required_argument, NULL, 'c'}, > {"resolution", required_argument, NULL, 'r'}, > - {"min", required_argument, NULL, 'm'}, > {"max", required_argument, NULL, 'x'}, > {"period", required_argument, NULL, 'p'}, > {"timeouts", required_argument, NULL, 't'}, > @@ -194,14 +206,13 @@ static void parse_args(int argc, char *argv[], > test_args_t *args) > /* defaults */ > args->core_count = 0; /* all cores */ > args->resolution_us = 10000; > - args->min_us = args->resolution_us; > args->max_us = 100000000; > args->period_us = 1000000; > args->tmo_count = 30; > > while (1) { > opt = getopt_long(argc, argv, "+c:r:m:x:p:t:h", > - longopts, &long_index); > + longopts, &long_index); > > if (opt == -1) > break; /* No more options */ > @@ -213,9 +224,6 @@ static void parse_args(int argc, char *argv[], > test_args_t *args) > case 'r': > args->resolution_us = atoi(optarg); > break; > - case 'm': > - args->min_us = atoi(optarg); > - break; > case 'x': > args->max_us = atoi(optarg); > break; > @@ -299,7 +307,6 @@ int main(int argc, char *argv[]) > > printf("first core: %i\n", first_core); > printf("resolution: %i usec\n", args.resolution_us); > - printf("min timeout: %i usec\n", args.min_us); > printf("max timeout: %i usec\n", args.max_us); > printf("period: %i usec\n", args.period_us); > printf("timeouts: %i\n", args.tmo_count); > @@ -323,10 +330,23 @@ int main(int argc, char *argv[]) > ODP_BUFFER_TYPE_TIMEOUT); > > if (pool == ODP_BUFFER_POOL_INVALID) { > - ODP_ERR("Pool create failed.\n"); > + ODP_ERR("Buffer pool create failed.\n"); > return -1; > } > > + tp = odp_timer_pool_create("timer_pool", pool, > + args.resolution_us*USEC, > + args.max_us*USEC, > + num_workers, /* One timer per worker */ > + true, > + ODP_CLOCK_DEFAULT); > + if (tp == ODP_TIMER_POOL_INVALID) { > + ODP_ERR("Timer pool create failed.\n"); > + return -1; > + } > + > + odp_shm_print_all(); > + > /* > * Create a queue for timer test > */ > @@ -342,13 +362,6 @@ int main(int argc, char *argv[]) > return -1; > } > > - test_timer = odp_timer_create("test_timer", pool, > - args.resolution_us*USEC, > - args.min_us*USEC, > - args.max_us*USEC); > - > - odp_shm_print_all(); > - > printf("CPU freq %"PRIu64" hz\n", odp_sys_cpu_hz()); > printf("Cycles vs nanoseconds:\n"); > ns = 0; > diff --git a/platform/linux-generic/Makefile.am > b/platform/linux-generic/Makefile.am > index f4dfdc1..e2bc1a7 100644 > --- a/platform/linux-generic/Makefile.am > +++ b/platform/linux-generic/Makefile.am > @@ -72,4 +72,5 @@ __LIB__libodp_la_SOURCES = \ > odp_thread.c \ > odp_ticketlock.c \ > odp_time.c \ > + priority_queue.c \ > odp_timer.c > diff --git a/platform/linux-generic/include/api/odp_timer.h > b/platform/linux-generic/include/api/odp_timer.h > index 01db839..e5f961c 100644 > --- a/platform/linux-generic/include/api/odp_timer.h > +++ b/platform/linux-generic/include/api/odp_timer.h > @@ -1,4 +1,4 @@ > -/* Copyright (c) 2013, Linaro Limited > +/* Copyright (c) 2014, Linaro Limited > * All rights reserved. > * > * SPDX-License-Identifier: BSD-3-Clause > @@ -8,7 +8,175 @@ > /** > * @file > * > - * ODP timer > + * ODP timer service > + * > + > +//Example #1 Retransmission timer (e.g. for reliable connections) > + > +//Create timer pool for reliable connections > +#define SEC 1000000000ULL //1s expressed in nanoseconds > +odp_timer_pool_t tcp_tpid = > + odp_timer_pool_create("TCP", > + buffer_pool, > + 1000000,//resolution 1ms > + 7200 * SEC,//max tmo length 2hours > + 40000,//num_timers > + true,//shared > + ODP_CLOCK_DEFAULT > + ); > +if (tcp_tpid == ODP_TIMER_POOL_INVALID) > +{ > + //Failed to create timer pool => fatal error > +} > + > + > +//Setting up a new connection > +//Allocate retransmission timeout (identical for supervision timeout) > +//The user pointer points back to the connection context > +conn->ret_tim = odp_timer_alloc(tcp_tpid, queue, conn); > +//Check if all resources were successfully allocated > +if (conn->ret_tim == ODP_TIMER_INVALID) > +{ > + //Failed to allocate all resources for connection => tear down > + //Destroy timeout > + odp_timer_free(conn->ret_tim); > + //Tear down connection > + ... > + return false; > +} > +//All necessary resources successfully allocated > +//Compute initial retransmission length in timer ticks > +conn->ret_len = odp_timer_ns_to_tick(tcp_tpid, 3 * SEC);//Per RFC1122 > +//Arm the timer > +odp_timer_set_rel(conn->ret_tim, conn->ret_len); > +return true; > + > + > +//A packet for the connection has just been transmitted > +//Reset the retransmission timer > +odp_timer_set_rel(conn->ret_tim, conn->ret_len); > + > + > +//A retransmission timeout for the connection has been received > +//Check if timeout is fresh or stale, for stale timeouts we need to reset > the > +//timer > +switch (odp_timer_tmo_status(tmo)) > +{ > + case ODP_TMO_FRESH : > + //Fresh timeout, last transmitted packet not acked in time => > + retransmit > + //Get connection from timeout event > + conn = odp_timer_get_userptr(tmo); > + //Retransmit last packet (e.g. TCP segment) > + ... > + //Re-arm timer using original delta value > + odp_timer_set_rel(conn->ret_tim, conn->ret_len); > + break; > + case ODP_TMO_STALE : > + break;//Do nothing > + case ODP_TMO_ORPHAN : > + odp_free_buffer(tmo); > + return;//Get out of here > +} > + > + > +//Example #2 Periodic tick > + > +//Create timer pool for periodic ticks > +odp_timer_pool_t per_tpid = > + odp_timer_pool_create("periodic-tick", > + buffer_pool, > + 1,//resolution 1ns > + 1000000000,//maximum timeout length 1s > + 10,//num_timers > + false,//not shared > + ODP_CLOCK_DEFAULT > + ); > +if (per_tpid == ODP_TIMER_POOL_INVALID) > +{ > + //Failed to create timer pool => fatal error > +} > + > + > +//Allocate periodic timer > +tim_1733 = odp_timer_alloc(per_tpid, queue, NULL); > +//Check if all resources were successfully allocated > +if (tim_1733 == ODP_TIMER_INVALID) > +{ > + //Failed to allocate all resources => tear down > + //Destroy timeout > + odp_timer_free(tim_1733); > + //Tear down other state > + ... > + return false; > +} > +//All necessary resources successfully allocated > +//Compute tick period in timer ticks > +period_1733 = odp_timer_ns_to_tick(per_tpid, 1000000000U / 1733U);//1733Hz > +//Compute when next tick should expire > +next_1733 = odp_timer_current_tick(per_tpid) + period_1733; > +//Arm the periodic timer > +odp_timer_set_abs(tim_1733, next_1733); > +return true; > + > + > + > +//A periodic timer timeout has been received > +//Must call odp_timer_tmo_status() on timeout! > +ret = odp_timer_tmo_status(tmo); > +//We expect the timeout is fresh since we are not calling set or cancel on > +//active or expired timers in this example > +assert(ret == ODP_TMO_FRESH); > +//Do processing driven by timeout *before* > +... > +do { > + //Compute when the timer should expire next > + next_1733 += period_1733; > + //Check that this is in the future > + if (likely(next_1733 > odp_timer_current_tick(per_tpid)) > + break;//Yes, done > + //Else we missed a timeout > + //Optionally attempt some recovery and/or logging of the problem > + ... > +} while (0); > +//Re-arm periodic timer > +odp_timer_set_abs(tim_1733, next_1733); > +//Or do processing driven by timeout *after* > +... > +return; > + > +//Example #3 Tear down of flow > +//ctx points to flow context data structure owned by application > +//Free the timer, cancelling any timeout > +odp_timer_free(ctx->timer);//Any enqueued timeout will be made invalid > +//Continue tearing down and eventually freeing context > +... > +return; > + > +//A timeout has been received, check status > +switch (odp_timer_tmo_status(tmo)) > +{ > + case ODP_TMO_FRESH : > + //A flow has timed out, tear it down > + //Find flow context from timeout > + ctx = (context *)odp_timer_get_userptr(tmo); > + //Free the supervision timer, any enqueued timeout will remain > + odp_timer_free(ctx->tim); > + //Free other flow related resources > + ... > + //Flow torn down > + break; > + case ODP_TMO_STALE : > + //A stale timeout was received, timer automatically reset > + break; > + case ODP_TMO_ORPHAN : > + //Orphaned timeout (from previously torn down flow) > + //No corresponding timer or flow context > + //Free the timeout > + odp_buffer_free(tmo); > + break; > +} > + > */ > > #ifndef ODP_TIMER_H_ > @@ -23,139 +191,325 @@ extern "C" { > #include <odp_buffer_pool.h> > #include <odp_queue.h> > > +/** > +* ODP timer pool handle (platform dependent) > +*/ > +struct odp_timer_pool; > +typedef struct odp_timer_pool *odp_timer_pool_t; > > /** > - * ODP timer handle > + * Invalid timer pool handle (platform dependent) > */ > -typedef uint32_t odp_timer_t; > +#define ODP_TIMER_POOL_INVALID NULL > > -/** Invalid timer */ > -#define ODP_TIMER_INVALID 0 > +typedef enum odp_timer_pool_clock_source_e { > + ODP_CLOCK_DEFAULT = 0, > + /* Platform dependent which clock sources exist beyond > + ODP_CLOCK_DEFAULT */ > + ODP_CLOCK_NONE = 1 > +} odp_timer_pool_clock_source_t; > > +/** > +* ODP timer handle (platform dependent) > +*/ > +struct odp_timer; > +typedef struct odp_timer *odp_timer_t; > > /** > - * ODP timeout handle > + * Invalid timer handle (platform dependent) > */ > -typedef odp_buffer_t odp_timer_tmo_t; > - > -/** Invalid timeout */ > -#define ODP_TIMER_TMO_INVALID 0 > +#define ODP_TIMER_INVALID NULL > > +/** > + * ODP timeout event handle > + */ > +typedef odp_buffer_t odp_timer_tmo_t; > > /** > - * Timeout notification > + * ODP timeout status > */ > -typedef odp_buffer_t odp_timeout_t; > +typedef enum odp_timer_tmo_status_e { > + ODP_TMO_FRESH, /* Timeout is fresh, process it */ > + ODP_TMO_STALE, /* Timer reset or cancelled, do nothing */ > + ODP_TMO_ORPHAN,/* Timer deleted, free timeout */ > +} odp_timer_tmo_status_t; > + > +/** > +* ODP tick value > +*/ > +typedef uint64_t odp_timer_tick_t; > > > /** > - * Create a timer > + * Create a timer pool > * > - * Creates a new timer with requested properties. > + * Create a new timer pool. > + * odp_timer_pool_create() is typically called once or a couple of times > during > + * application initialisation. > * > * @param name Name > - * @param pool Buffer pool for allocating timeout notifications > + * @param buf_pool Buffer pool for allocating timers > * @param resolution Timeout resolution in nanoseconds > - * @param min_tmo Minimum timeout duration in nanoseconds > - * @param max_tmo Maximum timeout duration in nanoseconds > + * @param max_tmo Maximum relative timeout in nanoseconds > + * @param num_timers Number of supported timers (minimum) > + * @param shared Shared or private timer pool. > + * Operations on shared timers will include the necessary > + * mutual exclusion, operations on private timers may not > + * (mutual exclusion is the responsibility of the caller). > + * @param clk_src Clock source to use > + * > + * @return Timer pool handle if successful, otherwise > ODP_TIMER_POOL_INVALID > + * and errno set > + */ > +odp_timer_pool_t > +odp_timer_pool_create(const char *name, > + odp_buffer_pool_t buf_pool, > + uint64_t resolution, > + uint64_t max_tmo, > + uint32_t num_timers, > + bool shared, > + odp_timer_pool_clock_source_t clk_src); > + > +/** > + * Start a timer pool > + * > + * 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 > * > - * @return Timer handle if successful, otherwise ODP_TIMER_INVALID > + * Destroy a timer pool, freeing all resources. > + * All timers must have been freed. > + * > + * @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, odp_timer_tick_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); > +odp_timer_tick_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 > + */ > +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid); > + > +/** > + * ODP timer configurations > */ > -uint64_t odp_timer_resolution(odp_timer_t timer); > + > +typedef enum odp_timer_pool_conf_e { > + ODP_TIMER_NAME, /* Return name of timer pool */ > + ODP_TIMER_RESOLUTION,/* Return the timer resolution (in ns) */ > + ODP_TIMER_MAX_TMO, /* Return the maximum supported timeout (in > ns) */ > + ODP_TIMER_NUM_TIMERS,/* Return number of supported timers */ > + ODP_TIMER_SHARED /* Return shared flag */ > +} odp_timer_pool_conf_t; > > /** > - * Maximum timeout in timer ticks > + * Query different timer pool configurations, e.g. > + * Timer resolution in nanoseconds > + * Maximum timeout in timer ticks > + * Number of supported timers > + * Shared or private timer pool > * > - * @param timer Timer > + * @param tpid Timer pool identifier > + * @param item Configuration item being queried > * > - * @return Maximum timeout in timer ticks > + * @return the requested piece of information or 0 for unknown item. > */ > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer); > +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, > + odp_timer_pool_conf_t item); > > /** > - * Current timer tick > + * Allocate a timer > * > - * @param timer Timer > + * Create a timer (allocating all necessary resources e.g. timeout event) > from > + * the timer pool. > * > - * @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 (copied to timeouts) > + * > + * @return Timer handle if successful, otherwise ODP_TIMER_INVALID and > + * errno set. > + */ > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > + odp_queue_t queue, > + void *user_ptr); > + > +/** > + * Free a timer > + * > + * Free (destroy) a timer, freeing all associated resources (e.g. default > + * timeout event). An expired and enqueued timeout event will not be > freed. > + * It is the responsibility of the application to free this timeout when > it > + * is received. > + * > + * @param tim Timer handle > */ > -uint64_t odp_timer_current_tick(odp_timer_t timer); > +void odp_timer_free(odp_timer_t tim); > > /** > - * Request timeout with an absolute timer tick > + * Set a timer (absolute time) with a user-defined timeout buffer > * > - * When tick reaches tmo_tick, the timer enqueues the timeout > notification into > - * the destination queue. > + * Set (arm) the timer to expire at specific time. The user-defined > + * buffer will be enqueued when the timer expires. > + * Arming may fail (if the timer is in state EXPIRED), an earlier timeout > + * will then be received. odp_timer_tmo_status() must be used to check if > + * the received timeout is valid. > * > - * @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. > + * Note: any invalid parameters will be treated as programming errors and > will > + * cause the application to abort. > + * Note: a timeout too near in time may be delivered immediately. > + * Note: a timeout too far away in time (beyond max_timeout) might be > delivered > + * early. > * > - * @return Timeout handle if successful, otherwise ODP_TIMER_TMO_INVALID > + * @param tim Timer > + * @param abs_tck Expiration time in absolute timer ticks > + * @param user_buf The buffer to use as timeout event > */ > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, uint64_t > tmo_tick, > - odp_queue_t queue, odp_buffer_t > buf); > +void odp_timer_set_abs_w_buf(odp_timer_t tim, > + odp_timer_tick_t abs_tck, > + odp_buffer_t user_buf); > > /** > - * Cancel a timeout > + * Set a timer with an absolute expiration time > + * > + * Set (arm) the timer to expire at a specific time. > + * Arming may fail (if the timer is in state EXPIRED), an earlier timeout > + * will then be received. odp_timer_tmo_status() must be used to check if > + * the received timeout is valid. > * > - * @param timer Timer > - * @param tmo Timeout to cancel > + * Note: any invalid parameters will be treated as programming errors and > will > + * cause the application to abort. > + * Note: a timeout too near in time may be delivered immediately. > + * Note: a timeout too far away in time (beyond max_timeout) might be > delivered > + * early, it will automatically be reset by odp_timer_tmo_status(). > * > - * @return 0 if successful > + * @param tim Timer > + * @param abs_tck Expiration time in absolute timer ticks > */ > -int odp_timer_cancel_tmo(odp_timer_t timer, odp_timer_tmo_t tmo); > +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck); > > /** > - * Convert buffer handle to timeout handle > + * Set a timer with a relative expiration time > * > - * @param buf Buffer handle > + * Set (arm) the timer to expire at a relative future time. > + * Arming may fail (if the timer is in state EXPIRED), > + * an earlier timeout will then be received. odp_timer_tmo_status() must > + * be used to check if the received timeout is valid. > * > - * @return Timeout buffer handle > + * Note: any invalid parameters will be treated as programming errors and > will > + * cause the application to abort. > + * Note: a timeout too near in time may be delivered immediately. > + * Note: a timeout too far away in time (beyond max_timeout) might be > delivered > + * early, it will automatically be reset by odp_timer_tmo_status(). > + * > + * @param tim Timer > + * @param rel_tck Expiration time in timer ticks relative to current time > of > + * the timer pool the timer belongs to > */ > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf); > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck); > > /** > - * Return absolute timeout tick > + * Cancel a timer > + * > + * Cancel a timer, preventing future expiration and delivery. > + * > + * A timer that has already expired and been enqueued for delivery may be > + * impossible to cancel and will instead be delivered to the destination > queue. > + * Use odp_timer_tmo_status() the check whether a received timeout is > fresh or > + * stale (cancelled). Stale timeouts will automatically be recycled. > * > - * @param tmo Timeout buffer handle > + * Note: any invalid parameters will be treated as programming errors and > will > + * cause the application to abort. > * > - * @return Absolute timeout tick > + * @param tim Timer handle > */ > -uint64_t odp_timeout_tick(odp_timeout_t tmo); > +void odp_timer_cancel(odp_timer_t tim); > + > +/** > + * Return fresh/stale/orphan status of timeout. > + * > + * Check a received timeout for orphaness (i.e. parent timer freed) and > + * staleness (i.e. parent timer has been reset or cancelled after timeout > + * was enqueued). > + * If the timeout is fresh, it should be processed. > + * If the timeout is stale, the timer will automatically be reset unless > it > + * was cancelled. > + * If the timeout is orphaned, it should be freed (by the caller). > + * > + * Note: odp_timer_tmo_status() must be called on all received (not > + * user-defined) timeouts! > + * > + * @param tmo Timeout > + * > + * @return ODP_TMO_FRESH, ODP_TMO_STALE, ODP_TMO_ORPHAN > + */ > +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo); > + > +/** > + * Get timer handle > + * > + * Return Handle of parent timer. > + * > + * @param tmo Timeout > + * > + * @return Timer handle or ODP_TIMER_INVALID for orphaned timeouts > + */ > +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo); > + > +/** > + * Get expiration time > + * > + * Return (actual) expiration time of timeout. > + * > + * @param tmo Timeout > + * > + * @return Expiration time > + */ > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo); > + > +/** > + * Get user pointer > + * > + * Return User pointer of timer associated with timeout. > + * The user pointer is often used to point to some associated context. > + * > + * @param tmo Timeout > + * > + * @return User pointer > + */ > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo); > + > +/* Helper functions */ > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, odp_timer_tick_t > tick); > > #ifdef __cplusplus > } > diff --git a/platform/linux-generic/include/odp_timer_internal.h > b/platform/linux-generic/include/odp_timer_internal.h > index ad28f53..ff8f209 100644 > --- a/platform/linux-generic/include/odp_timer_internal.h > +++ b/platform/linux-generic/include/odp_timer_internal.h > @@ -1,4 +1,4 @@ > -/* Copyright (c) 2013, Linaro Limited > +/* Copyright (c) 2014, Linaro Limited > * All rights reserved. > * > * SPDX-License-Identifier: BSD-3-Clause > @@ -8,72 +8,53 @@ > /** > * @file > * > - * ODP timer timeout descriptor - implementation internal > + * ODP timeout descriptor - implementation internal > */ > > #ifndef ODP_TIMER_INTERNAL_H_ > #define ODP_TIMER_INTERNAL_H_ > > -#ifdef __cplusplus > -extern "C" { > -#endif > - > -#include <odp_std_types.h> > -#include <odp_queue.h> > -#include <odp_buffer.h> > +#include <odp_align.h> > +#include <odp_debug.h> > #include <odp_buffer_internal.h> > #include <odp_buffer_pool_internal.h> > #include <odp_timer.h> > > -struct timeout_t; > - > -typedef struct timeout_t { > - struct timeout_t *next; > - int timer_id; > - int tick; > - uint64_t tmo_tick; > - odp_queue_t queue; > - odp_buffer_t buf; > - odp_buffer_t tmo_buf; > -} timeout_t; > - > - > -struct odp_timeout_hdr_t; > - > /** > - * Timeout notification header > + * Internal Timeout header > */ > -typedef struct odp_timeout_hdr_t { > +typedef struct { > + /* common buffer header */ > odp_buffer_hdr_t buf_hdr; > > - timeout_t meta; > - > + /* Requested expiration time */ > + odp_timer_tick_t expiration; > + /* User ptr inherited from parent timer */ > + void *user_ptr; > + /* Parent timer */ > + odp_timer_t timer; > + /* Tag inherited from parent timer at time of expiration */ > + uint32_t tag; > + /* Gen-cnt inherited from parent timer at time of creation */ > + uint32_t gc; > uint8_t buf_data[]; > } odp_timeout_hdr_t; > > - > - > +/* C++ doesn't allow offsetof() on "non-POD" datatypes. Don't know why > + odp_timeout_hdr_t is classified as non-POD, perhaps because of the > + inheritance? */ > ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) == > - ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), > - "ODP_TIMEOUT_HDR_T__SIZE_ERR"); > - > + ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), > + "sizeof(odp_timeout_hdr_t) == > ODP_OFFSETOF(odp_timeout_hdr_t, buf_data)"); > ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == 0, > - "ODP_TIMEOUT_HDR_T__SIZE_ERR2"); > - > + "sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == 0"); > > /** > - * Return timeout header > + * Return the timeout header > */ > -static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_timeout_t tmo) > +static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_buffer_t buf) > { > - odp_buffer_hdr_t *buf_hdr = odp_buf_to_hdr((odp_buffer_t)tmo); > - return (odp_timeout_hdr_t *)(uintptr_t)buf_hdr; > -} > - > - > - > -#ifdef __cplusplus > + return (odp_timeout_hdr_t *)odp_buf_to_hdr(buf); > } > -#endif > > #endif > diff --git a/platform/linux-generic/odp_timer.c > b/platform/linux-generic/odp_timer.c > index 1bf37f9..cea47a3 100644 > --- a/platform/linux-generic/odp_timer.c > +++ b/platform/linux-generic/odp_timer.c > @@ -1,402 +1,631 @@ > -/* Copyright (c) 2013, Linaro Limited > +/* Copyright (c) 2014, Linaro Limited > * All rights reserved. > * > * SPDX-License-Identifier: BSD-3-Clause > */ > > -#include <odp_timer.h> > -#include <odp_timer_internal.h> > -#include <odp_buffer_pool_internal.h> > -#include <odp_internal.h> > -#include <odp_atomic.h> > -#include <odp_spinlock.h> > -#include <odp_sync.h> > -#include <odp_debug.h> > > -#include <signal.h> > -#include <time.h> > +/** > + * @file > + * > + * ODP timer service > + * > + */ > > +#include <assert.h> > +#include <errno.h> > #include <string.h> > - > -#define NUM_TIMERS 1 > -#define MAX_TICKS 1024 > -#define RESOLUTION_NS 1000000 > - > - > -typedef struct { > - odp_spinlock_t lock; > - timeout_t *list; > -} tick_t; > - > -typedef struct { > - int allocated; > - volatile int active; > - volatile uint64_t cur_tick; > - timer_t timerid; > - odp_timer_t timer_hdl; > - odp_buffer_pool_t pool; > - uint64_t resolution_ns; > - uint64_t max_ticks; > - tick_t tick[MAX_TICKS]; > - > -} timer_ring_t; > - > -typedef struct { > - odp_spinlock_t lock; > - int num_timers; > - timer_ring_t timer[NUM_TIMERS]; > - > -} timer_global_t; > - > -/* Global */ > -static timer_global_t odp_timer; > - > -static void add_tmo(tick_t *tick, timeout_t *tmo) > +#include <stdlib.h> > +#include <time.h> > +#include <signal.h> > +#include "odp_std_types.h" > +#include "odp_buffer.h" > +#include "odp_buffer_pool.h" > +#include "odp_queue.h" > +#include "odp_hints.h" > +#include "odp_sync.h" > +#include "odp_spinlock.h" > +#include "odp_debug.h" > +#include "odp_align.h" > +#include "odp_shared_memory.h" > +#include "odp_hints.h" > +#include "odp_internal.h" > +#include "odp_timer.h" > +#include "odp_timer_internal.h" > +#include "priority_queue.h" > + > +typedef struct odp_timer { > + pq_element pqelem;/* Base class */ > + odp_timer_tick_t req_tmo;/* Requested timeout tick */ > + odp_buffer_t tmo_buf;/* ODP_BUFFER_INVALID if timeout enqueued */ > + odp_queue_t queue;/* ODP_QUEUE_INVALID if timer is free */ > + uint32_t tag;/* Reusing tag as next pointer/index when timer is > free */ > + uint32_t gc; > + bool user_buf; /* User-defined buffer? */ > +} odp_timer; > + > +/* Constructor for array of objects */ > +static inline void odp_timer_con(odp_timer *this) > { > - odp_spinlock_lock(&tick->lock); > - > - tmo->next = tick->list; > - tick->list = tmo; > - > - odp_spinlock_unlock(&tick->lock); > + pq_element_con(&this->pqelem); > + this->tmo_buf = ODP_BUFFER_INVALID; > + this->queue = ODP_QUEUE_INVALID; > + this->gc = 0; > } > > -static timeout_t *rem_tmo(tick_t *tick) > +/* Destructor */ > +static inline void odp_timer_des(odp_timer *this) > { > - timeout_t *tmo; > - > - odp_spinlock_lock(&tick->lock); > - > - tmo = tick->list; > - > - if (tmo) > - tick->list = tmo->next; > - > - odp_spinlock_unlock(&tick->lock); > + assert(this->tmo_buf == ODP_BUFFER_INVALID); > + assert(this->queue == ODP_QUEUE_INVALID); > + pq_element_des(&this->pqelem); > +} > > - if (tmo) > - tmo->next = NULL; > +/* Setup when timer is allocated */ > +static void setup(odp_timer *this, > + odp_queue_t _q, > + void *_up, > + odp_buffer_t _tmo) > +{ > + this->req_tmo = INVALID_PRIORITY; > + this->tmo_buf = _tmo; > + this->queue = _q; > + this->tag = 0; > + this->user_buf = false; > + /* Initialise constant fields of timeout event */ > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(this->tmo_buf); > + tmo_hdr->gc = this->gc; > + tmo_hdr->timer = this; > + tmo_hdr->user_ptr = _up; > + /* tmo_hdr->tag set at expiration time */ > + /* tmo_hdr->expiration set at expiration time */ > + assert(this->queue != ODP_QUEUE_INVALID); > +} > > - return tmo; > +/* Teardown when timer is freed */ > +static odp_buffer_t teardown(odp_timer *this) > +{ > + /* Increase generation count to make pending timeout orphaned */ > + ++this->gc; > + odp_buffer_t buf = this->tmo_buf; > + this->tmo_buf = ODP_BUFFER_INVALID; > + this->queue = ODP_QUEUE_INVALID; > + return buf; > } > > -/** > - * Search and delete tmo entry from timeout list > - * return -1 : on error.. handle not in list > - * 0 : success > - */ > -static int find_and_del_tmo(timeout_t **tmo, odp_timer_tmo_t handle) > +static inline uint32_t get_next_free(odp_timer *this) > { > - timeout_t *cur, *prev; > - prev = NULL; > + assert(this->queue == ODP_QUEUE_INVALID); > + return this->tag; > +} > > - for (cur = *tmo; cur != NULL; prev = cur, cur = cur->next) { > - if (cur->tmo_buf == handle) { > - if (prev == NULL) > - *tmo = cur->next; > - else > - prev->next = cur->next; > +static inline void set_next_free(odp_timer *this, uint32_t nf) > +{ > + assert(this->queue == ODP_QUEUE_INVALID); > + this->tag = nf; > +} > > - break; > +static inline void expire(odp_timer *this, odp_timer_tick_t tick) > +{ > + /* Timer expired, is there actually any timeout event */ > + /* we can enqueue? */ > + if (odp_likely(this->tmo_buf != ODP_BUFFER_INVALID)) { > + /* Swap out timeout buffer */ > + odp_buffer_t buf = this->tmo_buf; > + this->tmo_buf = ODP_BUFFER_INVALID; > + if (odp_likely(!this->user_buf)) { > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(buf); > + /* Copy tag from timer */ > + /* and actual expiration tick from timer pool */ > + tmo_hdr->tag = this->tag; > + tmo_hdr->expiration = tick; > } > + /* Else don't touch user-defined buffer */ > + int rc = odp_queue_enq(this->queue, buf); > + if (rc != 0) > + abort(); > } > - > - if (!cur) > - /* couldn't find tmo in list */ > - return -1; > - > - /* application to free tmo_buf provided by absolute_tmo call */ > - return 0; > + /* No, timeout event already enqueued */ > } > > -int odp_timer_cancel_tmo(odp_timer_t timer_hdl, odp_timer_tmo_t tmo) > +typedef struct odp_timer_pool { > + priority_queue pq; > + uint64_t tick; > + bool shared; > + odp_spinlock_t lock; > + const char *name; > + odp_buffer_pool_t buf_pool; > + uint64_t resolution_ns; > + uint64_t max_timeout; > + odp_timer *timers; > + uint32_t num_alloc;/* Current number of allocated timers */ > + uint32_t max_timers;/* Max number of timers */ > + uint32_t first_free;/* 0..max_timers-1 => free timer */ > + timer_t timerid; > + odp_timer_pool_clock_source_t clk_src; > +} odp_timer_pool; > + > +/* Forward declarations */ > +static void timer_init(odp_timer_pool *tp); > +static void timer_exit(odp_timer_pool *tp); > + > +static void odp_timer_pool_con(odp_timer_pool *this, > + const char *_n, > + odp_buffer_pool_t _bp, > + uint64_t _r, > + uint64_t _m, > + uint32_t _mt, > + bool _s, > + odp_timer_pool_clock_source_t _cs) > { > - int id; > - uint64_t tick_idx; > - timeout_t *cancel_tmo; > - odp_timeout_hdr_t *tmo_hdr; > - tick_t *tick; > - > - /* get id */ > - id = timer_hdl - 1; > - > - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo); > - /* get tmo_buf to cancel */ > - cancel_tmo = &tmo_hdr->meta; > - > - tick_idx = cancel_tmo->tick; > - tick = &odp_timer.timer[id].tick[tick_idx]; > - > - odp_spinlock_lock(&tick->lock); > - /* search and delete tmo from tick list */ > - if (find_and_del_tmo(&tick->list, tmo) != 0) { > - odp_spinlock_unlock(&tick->lock); > - ODP_DBG("Couldn't find the tmo (%d) in tick list\n", > (int)tmo); > - return -1; > + priority_queue_con(&this->pq, _mt); > + this->tick = 0; > + this->shared = _s; > + this->name = strdup(_n); > + this->buf_pool = _bp; > + this->resolution_ns = _r; > + this->max_timeout = _m; > + this->num_alloc = 0; > + this->max_timers = _mt; > + this->first_free = 0; > + this->clk_src = _cs; > + this->timers = malloc(sizeof(odp_timer) * this->max_timers); > + if (this->timers == NULL) { > + ODP_ERR("%s: malloc failed\n", _n); > + abort(); > } > - odp_spinlock_unlock(&tick->lock); > - > - return 0; > + uint32_t i; > + for (i = 0; i < this->max_timers; i++) > + odp_timer_con(&this->timers[i]); > + for (i = 0; i < this->max_timers; i++) > + set_next_free(&this->timers[i], i + 1); > + odp_spinlock_init(&this->lock); > + if (this->clk_src == ODP_CLOCK_DEFAULT) > + timer_init(this); > + /* Make sure timer pool initialisation is globally observable */ > + /* before we return a pointer to it */ > + odp_sync_stores(); > } > > -static void notify_function(union sigval sigval) > +static odp_timer_pool *odp_timer_pool_new( > + const char *_n, > + odp_buffer_pool_t _bp, > + uint64_t _r, > + uint64_t _m, > + uint32_t _mt, > + bool _s, > + odp_timer_pool_clock_source_t _cs) > { > - uint64_t cur_tick; > - timeout_t *tmo; > - tick_t *tick; > - timer_ring_t *timer; > - > - timer = sigval.sival_ptr; > - > - if (timer->active == 0) { > - ODP_DBG("Timer (%u) not active\n", timer->timer_hdl); > - return; > + odp_timer_pool *this = malloc(sizeof(odp_timer_pool)); > + if (odp_unlikely(this == NULL)) { > + ODP_ERR("%s: malloc failed\n", _n); > + abort(); > } > + odp_timer_pool_con(this, _n, _bp, _r, _m, _mt, _s, _cs); > + return this; > +} > > - /* ODP_DBG("Tick\n"); */ > +static void odp_timer_pool_des(odp_timer_pool *this) > +{ > + if (this->shared) > + odp_spinlock_lock(&this->lock); > + if (this->num_alloc != 0) { > + /* It's a programming error to attempt to destroy a */ > + /* timer pool which is still in use */ > + ODP_ERR("%s: timers in use\n", this->name); > + abort(); > + } > + if (this->clk_src == ODP_CLOCK_DEFAULT) > + timer_exit(this); > + uint32_t i; > + for (i = 0; i < this->max_timers; i++) > + odp_timer_des(&this->timers[i]); > + free(this->timers); > + priority_queue_des(&this->pq); > + odp_sync_stores(); > +} > > - cur_tick = timer->cur_tick++; > +static void odp_timer_pool_del(odp_timer_pool *this) > +{ > + odp_timer_pool_des(this); > + free(this); > +} > > - odp_sync_stores(); > +static inline odp_timer *timer_alloc(odp_timer_pool *this, > + odp_queue_t queue, > + void *user_ptr, > + odp_buffer_t tmo_buf) > +{ > + odp_timer *tim = ODP_TIMER_INVALID; > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + if (odp_likely(this->num_alloc < this->max_timers)) { > + this->num_alloc++; > + /* Remove first unused timer from free list */ > + assert(this->first_free != this->max_timers); > + tim = &this->timers[this->first_free]; > + this->first_free = get_next_free(tim); > + /* Insert timer into priority queue */ > + if (odp_unlikely(!pq_register_element(&this->pq, > + &tim->pqelem))) { > + /* Unexpected internal error */ > + abort(); > + } > + /* Create timer */ > + setup(tim, queue, user_ptr, tmo_buf); > + } else { > + errno = ENFILE; /* Reusing file table overvlow */ > + } > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + return tim; > +} > > - tick = &timer->tick[cur_tick % MAX_TICKS]; > +static inline void timer_free(odp_timer_pool *this, odp_timer *tim) > +{ > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + /* Destroy timer */ > + odp_buffer_t buf = teardown(tim); > + /* Remove timer from priority queue */ > + pq_unregister_element(&this->pq, &tim->pqelem); > + /* Insert timer into free list */ > + set_next_free(tim, this->first_free); > + this->first_free = (tim - &this->timers[0]) / > sizeof(this->timers[0]); > + assert(this->num_alloc != 0); > + this->num_alloc--; > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + if (buf != ODP_BUFFER_INVALID) > + odp_buffer_free(buf); > +} > > - while ((tmo = rem_tmo(tick)) != NULL) { > - odp_queue_t queue; > - odp_buffer_t buf; > +static inline void timer_reset(odp_timer_pool *this, > + odp_timer *tim, > + odp_timer_tick_t abs_tck) > +{ > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + /* Increase timer tag to make any pending timeout stale */ > + tim->tag++; > + /* Save requested timeout */ > + tim->req_tmo = abs_tck; > + /* Update timer position in priority queue */ > + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > +} > > - queue = tmo->queue; > - buf = tmo->buf; > +static inline void timer_reset_w_buf(odp_timer_pool *this, > + odp_timer *tim, > + odp_timer_tick_t abs_tck, > + odp_buffer_t user_buf) > +{ > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + /* Increase timer tag to make any pending timeout stale */ > + tim->tag++; > + /* Save requested timeout */ > + tim->req_tmo = abs_tck; > + /* Set flag indicating presence of user defined buffer */ > + tim->user_buf = true; > + /* Swap in new buffer, get any old buffer pointer */ > + odp_buffer_t old_buf = tim->tmo_buf; > + tim->tmo_buf = user_buf; > + /* Update timer position in priority queue */ > + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + /* Free old buffer if present */ > + if (odp_unlikely(old_buf != ODP_BUFFER_INVALID)) > + odp_buffer_free(old_buf); > +} > > - if (buf != tmo->tmo_buf) > - odp_buffer_free(tmo->tmo_buf); > +static inline void timer_cancel(odp_timer_pool *this, > + odp_timer *tim) > +{ > + odp_buffer_t tmo_buf = ODP_BUFFER_INVALID; > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + if (odp_unlikely(tim->user_buf)) { > + /* Swap out old user buffer */ > + tmo_buf = tim->tmo_buf; > + tim->tmo_buf = ODP_BUFFER_INVALID; > + tim->user_buf = false; > + } > + /* Else a normal timer (no user-defined buffer) */ > + /* Increase timer tag to make any pending timeout stale */ > + tim->tag++; > + /* Clear requested timeout */ > + tim->req_tmo = INVALID_PRIORITY; > + /* Remove timer from the priority queue */ > + pq_deactivate_element(&this->pq, &tim->pqelem); > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + /* Free user-defined buffer if present */ > + if (odp_unlikely(tmo_buf != ODP_BUFFER_INVALID)) > + odp_buffer_free(tmo_buf); > +} > > - odp_queue_enq(queue, buf); > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, odp_timer_tick_t > tick) > +{ > + odp_spinlock_lock(&tpid->lock); > + unsigned nexp = 0; > + odp_timer_t tim; > + tpid->tick = tick; > + while ((tim = (odp_timer_t)pq_release_element(&tpid->pq, tick)) != > + ODP_TIMER_INVALID) { > + assert(get_prio(&tim->pqelem) <= tick); > + expire(tim, tick); > + nexp++; > } > + odp_spinlock_unlock(&tpid->lock); > + return nexp; > } > > -static void timer_start(timer_ring_t *timer) > +/* Functions that use Linux/POSIX per-process timers and related > facilities */ > +static void timer_notify(union sigval sigval) > +{ > + odp_timer_pool *tp = (odp_timer_pool *)sigval.sival_ptr; > + uint64_t new_tick = tp->tick + 1; > + (void)odp_timer_pool_expire(tp, new_tick); > +} > + > +static void timer_init(odp_timer_pool *tp) > { > struct sigevent sigev; > struct itimerspec ispec; > > - ODP_DBG("\nTimer (%u) starts\n", timer->timer_hdl); > + ODP_DBG("Creating POSIX timer for timer pool %s, period %" > + PRIu64" ns\n", tp->name, tp->resolution_ns); > > memset(&sigev, 0, sizeof(sigev)); > memset(&ispec, 0, sizeof(ispec)); > > sigev.sigev_notify = SIGEV_THREAD; > - sigev.sigev_notify_function = notify_function; > - sigev.sigev_value.sival_ptr = timer; > + sigev.sigev_notify_function = timer_notify; > + sigev.sigev_value.sival_ptr = tp; > > - if (timer_create(CLOCK_MONOTONIC, &sigev, &timer->timerid)) { > - ODP_DBG("Timer create failed\n"); > - return; > + if (timer_create(CLOCK_MONOTONIC, &sigev, &tp->timerid)) { > + perror("timer_create"); > + abort(); > } > > ispec.it_interval.tv_sec = 0; > - ispec.it_interval.tv_nsec = RESOLUTION_NS; > + ispec.it_interval.tv_nsec = tp->resolution_ns; > ispec.it_value.tv_sec = 0; > - ispec.it_value.tv_nsec = RESOLUTION_NS; > + ispec.it_value.tv_nsec = tp->resolution_ns; > > - if (timer_settime(timer->timerid, 0, &ispec, NULL)) { > - ODP_DBG("Timer set failed\n"); > - return; > + if (timer_settime(&tp->timerid, 0, &ispec, NULL)) { > + perror("timer_settime"); > + abort(); > } > - > - return; > } > > -int odp_timer_init_global(void) > +static void timer_exit(odp_timer_pool *tp) > { > - ODP_DBG("Timer init ..."); > - > - memset(&odp_timer, 0, sizeof(timer_global_t)); > - > - odp_spinlock_init(&odp_timer.lock); > - > - ODP_DBG("done\n"); > - > - return 0; > + if (timer_delete(tp->timerid) != 0) { > + perror("timer_delete"); > + abort(); > + } > } > > -int odp_timer_disarm_all(void) > +odp_timer_pool_t > +odp_timer_pool_create(const char *name, > + odp_buffer_pool_t buf_pool, > + uint64_t resolution_ns, > + uint64_t max_timeout, > + uint32_t num_timers, > + bool shared, > + odp_timer_pool_clock_source_t clk_src) > { > - int timers; > - struct itimerspec ispec; > - > - odp_spinlock_lock(&odp_timer.lock); > - > - timers = odp_timer.num_timers; > - > - ispec.it_interval.tv_sec = 0; > - ispec.it_interval.tv_nsec = 0; > - ispec.it_value.tv_sec = 0; > - ispec.it_value.tv_nsec = 0; > - > - for (; timers >= 0; timers--) { > - if (timer_settime(odp_timer.timer[timers].timerid, > - 0, &ispec, NULL)) { > - ODP_DBG("Timer reset failed\n"); > - odp_spinlock_unlock(&odp_timer.lock); > - return -1; > - } > - odp_timer.num_timers--; > + /* Verify that buffer pool can be used for timeouts */ > + odp_buffer_t buf = odp_buffer_alloc(buf_pool); > + if (buf == ODP_BUFFER_INVALID) { > + ODP_ERR("%s: Failed to allocate buffer\n", name); > + abort(); > } > + if (odp_buffer_type(buf) != ODP_BUFFER_TYPE_TIMEOUT) { > + ODP_ERR("%s: Buffer pool wrong type\n", name); > + abort(); > + } > + odp_buffer_free(buf); > + odp_timer_pool_t tp = odp_timer_pool_new(name, buf_pool, > resolution_ns, > + max_timeout, num_timers, > + shared, clk_src); > + return tp; > +} > > - odp_spinlock_unlock(&odp_timer.lock); > +void odp_timer_pool_start(void) > +{ > + /* Nothing to do here */ > +} > > - return 0; > +void odp_timer_pool_destroy(odp_timer_pool_t tpid) > +{ > + odp_timer_pool_del(tpid); > } > > -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) > +uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, odp_timer_tick_t > ticks) > { > - uint32_t id; > - timer_ring_t *timer; > - odp_timer_t timer_hdl; > - int i; > - (void) name; (void) resolution; (void) min_tmo; (void) max_tmo; > + return ticks * tpid->resolution_ns; > +} > > - odp_spinlock_lock(&odp_timer.lock); > +odp_timer_tick_t odp_timer_ns_to_tick(odp_timer_pool_t tpid, uint64_t ns) > +{ > + return (odp_timer_tick_t)(ns / tpid->resolution_ns); > +} > > - if (odp_timer.num_timers >= NUM_TIMERS) { > - odp_spinlock_unlock(&odp_timer.lock); > - return ODP_TIMER_INVALID; > - } > +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid) > +{ > + return tpid->tick; > +} > > - for (id = 0; id < NUM_TIMERS; id++) { > - if (odp_timer.timer[id].allocated == 0) > - break; > +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, > + odp_timer_pool_conf_t item) > +{ > + switch (item) { > + case ODP_TIMER_NAME: > + return (uint64_t)(tpid->name); > + case ODP_TIMER_RESOLUTION: > + return tpid->resolution_ns; > + case ODP_TIMER_MAX_TMO: > + return tpid->max_timeout; > + case ODP_TIMER_NUM_TIMERS: > + return tpid->max_timers; > + case ODP_TIMER_SHARED: > + return tpid->shared; > + default: > + return 0; > } > +} > > - timer = &odp_timer.timer[id]; > - timer->allocated = 1; > - odp_timer.num_timers++; > - > - odp_spinlock_unlock(&odp_timer.lock); > - > - timer_hdl = id + 1; > - > - timer->timer_hdl = timer_hdl; > - timer->pool = pool; > - timer->resolution_ns = RESOLUTION_NS; > - timer->max_ticks = MAX_TICKS; > - > - for (i = 0; i < MAX_TICKS; i++) { > - odp_spinlock_init(&timer->tick[i].lock); > - timer->tick[i].list = NULL; > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > + odp_queue_t queue, > + void *user_ptr) > +{ > + /* We check this because ODP_QUEUE_INVALID is used */ > + /* to indicate a free timer */ > + if (odp_unlikely(queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("%s: Invalid queue identifier\n", tpid->name); > + abort(); > } > - > - timer->active = 1; > - odp_sync_stores(); > - > - timer_start(timer); > - > - return timer_hdl; > + odp_buffer_t tmo_buf = odp_buffer_alloc(tpid->buf_pool); > + if (odp_likely(tmo_buf != ODP_BUFFER_INVALID)) { > + odp_timer *tim = timer_alloc(tpid, queue, user_ptr, > tmo_buf); > + if (tim != ODP_TIMER_INVALID) { > + /* Success */ > + assert(tim->queue != ODP_QUEUE_INVALID); > + return tim; > + } > + odp_buffer_free(tmo_buf); > + } > + /* Else failed to allocate timeout event */ > + /* errno set by odp_buffer_alloc() or timer_alloc () */ > + return ODP_TIMER_INVALID; > } > > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer_hdl, uint64_t > tmo_tick, > - odp_queue_t queue, odp_buffer_t buf) > +void odp_timer_free(odp_timer_t tim) > { > - int id; > - uint64_t tick; > - uint64_t cur_tick; > - timeout_t *new_tmo; > - odp_buffer_t tmo_buf; > - odp_timeout_hdr_t *tmo_hdr; > - timer_ring_t *timer; > - > - id = timer_hdl - 1; > - timer = &odp_timer.timer[id]; > - > - cur_tick = timer->cur_tick; > - if (tmo_tick <= cur_tick) { > - ODP_DBG("timeout too close\n"); > - return ODP_TIMER_TMO_INVALID; > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_free(tp, tim); > +} > > - tick = tmo_tick - cur_tick; > - if (tick > MAX_TICKS) { > - ODP_DBG("timeout too far\n"); > - return ODP_TIMER_TMO_INVALID; > +void odp_timer_set_abs_w_buf(odp_timer_t tim, > + odp_timer_tick_t abs_tck, > + odp_buffer_t user_buf) > +{ > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_reset_w_buf(tp, tim, abs_tck, user_buf); > +} > > - tick = (cur_tick + tick) % MAX_TICKS; > - > - tmo_buf = odp_buffer_alloc(timer->pool); > - if (tmo_buf == ODP_BUFFER_INVALID) { > - ODP_DBG("alloc failed\n"); > - return ODP_TIMER_TMO_INVALID; > +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck) > +{ > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > } > - > - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo_buf); > - new_tmo = &tmo_hdr->meta; > - > - new_tmo->timer_id = id; > - new_tmo->tick = (int)tick; > - new_tmo->tmo_tick = tmo_tick; > - new_tmo->queue = queue; > - new_tmo->tmo_buf = tmo_buf; > - > - if (buf != ODP_BUFFER_INVALID) > - new_tmo->buf = buf; > - else > - new_tmo->buf = tmo_buf; > - > - add_tmo(&timer->tick[tick], new_tmo); > - > - return tmo_buf; > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_reset(tp, tim, abs_tck); > } > > -uint64_t odp_timer_tick_to_ns(odp_timer_t timer_hdl, uint64_t ticks) > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return ticks * odp_timer.timer[id].resolution_ns; > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > + } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_reset(tp, tim, tp->tick + rel_tck); > } > > -uint64_t odp_timer_ns_to_tick(odp_timer_t timer_hdl, uint64_t ns) > +void odp_timer_cancel(odp_timer_t tim) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return ns / odp_timer.timer[id].resolution_ns; > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > + } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_cancel(tp, tim); > } > > -uint64_t odp_timer_resolution(odp_timer_t timer_hdl) > +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo_buf) > { > - uint32_t id; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + odp_timer *tim = tmo_hdr->timer; > + > + /* Make sure stores to '*tim' are visible */ > + odp_sync_stores(); > > - id = timer_hdl - 1; > - return odp_timer.timer[id].resolution_ns; > + /* Compare generation count (gc) of timeout and parent timer (if > any)*/ > + if (odp_unlikely(tim == ODP_TIMER_INVALID || > + tmo_hdr->gc != tim->gc)) { > + /* Generation counters differ => timeout is orphaned */ > + return ODP_TMO_ORPHAN; > + } > + /* Else gen-cnts match => parent timer exists */ > + > + /* Return timeout to timer so that it can be delivered again */ > + tim->tmo_buf = tmo_buf; > + /* FIXME do we need some kind of synchronisation or locking here? > */ > + > + /* Compare tags of timeout and parent timer */ > + /* Compare requested and actual timeout time */ > + if (odp_likely(tim->tag == tmo_hdr->tag && > + tim->req_tmo <= tmo_hdr->expiration)) { > + /* Tags match, actual timeout is after requested => good! > */ > + return ODP_TMO_FRESH; > + } else { > + /* Tags don't match or actual timeout time is before */ > + /* requested */ > + /* Timer has been reset or cancelled and timeout is stale > */ > + /* or timeout expired too early */ > + if (tim->req_tmo != INVALID_PRIORITY) { > + /* Reset the timer for requested timeout */ > + odp_timer_set_abs(tim, tim->req_tmo); > + } > + /* Else timer was cancelled, do nothing */ > + return ODP_TMO_STALE; > + } > } > > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer_hdl) > +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo_buf) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return odp_timer.timer[id].max_ticks; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + odp_timer_t tim = tmo_hdr->timer; > + if (odp_likely(tim != ODP_TIMER_INVALID && tmo_hdr->gc == tim->gc)) > + return tim; > + else > + return ODP_TIMER_INVALID; > } > > -uint64_t odp_timer_current_tick(odp_timer_t timer_hdl) > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo_buf) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return odp_timer.timer[id].cur_tick; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + return tmo_hdr->expiration; > } > > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf) > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo_buf) > { > - return (odp_timeout_t) buf; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + return tmo_hdr->user_ptr; > } > > -uint64_t odp_timeout_tick(odp_timeout_t tmo) > +int odp_timer_init_global(void) > { > - odp_timeout_hdr_t *tmo_hdr = odp_timeout_hdr(tmo); > - return tmo_hdr->meta.tmo_tick; > + return 0; > } > diff --git a/platform/linux-generic/priority_queue.c > b/platform/linux-generic/priority_queue.c > new file mode 100644 > index 0000000..ba4ba0e > --- /dev/null > +++ b/platform/linux-generic/priority_queue.c > @@ -0,0 +1,289 @@ > +#define NDEBUG /* Enabled by default by ODP build system */ > +#include <assert.h> > +#include <unistd.h> > +#include <stdlib.h> > +#include <string.h> > +#include <strings.h> > +#include <odp_hints.h> > +#include <odp_align.h> > +#include <odp_debug.h> > + > +#include "priority_queue.h" > + > + > +#define NUM_CHILDREN 4 > +#define CHILD(n) (NUM_CHILDREN * (n) + 1) > +#define PARENT(n) (((n) - 1) / NUM_CHILDREN) > + > +/* Internal nodes in the array */ > +typedef struct heap_node { > + pq_element *elem; > + /* Copy of elem->prio so we avoid unnecessary dereferencing */ > + pq_priority_t prio; > +} heap_node; > + > +static void pq_assert_heap(priority_queue *this); > + > +#define ALIGNMENT(p) (1U << ((unsigned)ffs((int)p) - 1U)) > + > +void priority_queue_con(priority_queue *this, uint32_t _max_elems) > +{ > + this->max_elems = _max_elems; > + this->reg_elems = 0; > + this->num_elems = 0; > + this->org_ptr = malloc((_max_elems + 64 / sizeof(heap_node)) * > + sizeof(heap_node)); > + if (odp_unlikely(this->org_ptr == NULL)) { > + ODP_ERR("malloc failed\n"); > + abort(); > + } > + this->heap = this->org_ptr; > + assert((size_t)&this->heap[1] % 8 == 0); > + /* Increment base address until first child (index 1) is cache > line */ > + /* aligned and thus all children (e.g. index 1-4) stored in the */ > + /* same cache line. We are not interested in the alignment of */ > + /* heap[0] as this is a lone node */ > + while ((size_t)&this->heap[1] % ODP_CACHE_LINE_SIZE != 0) { > + /* Cast to ptr to struct member with the greatest > alignment */ > + /* requirement */ > + this->heap = (heap_node *)((pq_priority_t *)this->heap + > 1); > + } > +#if 0 > + printf("Alignment of heap[1]=%u\n", ALIGNMENT((size_t)&heap[1])); > + printf("Alignment of heap[CHILD(1)]=%u\n", > + ALIGNMENT((size_t)&heap[CHILD(1)])); > +#endif > + pq_assert_heap(this); > +} > + > +void priority_queue_des(priority_queue *this) > +{ > + pq_assert_heap(this); > + free(this->org_ptr); > +} > + > +#ifndef NDEBUG > +static uint32_t > +pq_assert_elem(priority_queue *this, uint32_t index, bool recurse) > +{ > + uint32_t num = 1; > + const pq_element *elem = this->heap[index].elem; > + assert(elem->index == index); > + assert(elem->prio == this->heap[index].prio); > + uint32_t child = CHILD(index); > + uint32_t i; > + for (i = 0; i < NUM_CHILDREN; i++, child++) { > + if (valid_index(this, child)) { > + assert(this->heap[child].elem != NULL); > + assert(this->heap[child].prio >= elem->prio); > + if (recurse) > + num += pq_assert_elem(this, child, > recurse); > + } > + } > + return num; > +} > +#endif > + > +static void > +pq_assert_heap(priority_queue *this) > +{ > + (void)this; > +#ifndef NDEBUG > + uint32_t num = 0; > + if (odp_likely(this->num_elems != 0)) { > + assert(this->heap[0].elem != NULL); > + num += pq_assert_elem(this, 0, true); > + } > + assert(num == this->num_elems); > + unsigned i; > + for (i = 0; i < this->num_elems; i++) { > + assert(this->heap[i].elem != NULL); > + assert(this->heap[i].prio != INVALID_PRIORITY); > + } > +#endif > +} > + > +/* Bubble up to proper position */ > +void > +pq_bubble_up(priority_queue *this, pq_element *elem) > +{ > + assert(this->heap[elem->index].elem == elem); > + assert(this->heap[elem->index].prio == elem->prio); > + uint32_t current = elem->index; > + pq_priority_t prio = elem->prio; > + assert(current == 0 || this->heap[PARENT(current)].elem != NULL); > + /* Move up into proper position */ > + while (current != 0 && this->heap[PARENT(current)].prio > prio) { > + uint32_t parent = PARENT(current); > + assert(this->heap[parent].elem != NULL); > + /* Swap current with parent */ > + /* 1) Move parent down */ > + this->heap[current].elem = this->heap[parent].elem; > + this->heap[current].prio = this->heap[parent].prio; > + this->heap[current].elem->index = current; > + /* 2) Move current up to parent */ > + this->heap[parent].elem = elem; > + this->heap[parent].prio = prio; > + this->heap[parent].elem->index = parent; > + /* Continue moving elem until it is in the right place */ > + current = parent; > + } > + pq_assert_heap(this); > +} > + > +/* Find the smallest child that is smaller than the specified priority */ > +/* TODO very hot function! */ > +uint32_t pq_smallest_child(priority_queue *this, > + uint32_t index, > + pq_priority_t val) > +{ > + uint32_t smallest = index; > + uint32_t child = CHILD(index); > +#if 1 > + /* Unroll loop when all children exist */ > + if (odp_likely(valid_index(this, child + 3))) { > + if (this->heap[child + 0].prio < val) > + val = this->heap[smallest = child + 0].prio; > + if (this->heap[child + 1].prio < val) > + val = this->heap[smallest = child + 1].prio; > + if (this->heap[child + 2].prio < val) > + val = this->heap[smallest = child + 2].prio; > + if (this->heap[child + 3].prio < val) > + val = this->heap[smallest = child + 3].prio; > + return smallest; > + } > +#endif > + uint32_t i; > + for (i = 0; i < NUM_CHILDREN; i++) { > + if (odp_unlikely(!valid_index(this, child + i))) > + break; > + if (this->heap[child + i].prio < val) { > + smallest = child + i; > + val = this->heap[smallest].prio; > + } > + } > + return smallest; > +} > + > +/* TODO very hot function, can it be optimised? */ > +void > +pq_bubble_down(priority_queue *this, pq_element *elem) > +{ > + assert(this->heap[elem->index].elem == elem); > + assert(this->heap[elem->index].prio == elem->prio); > + uint32_t current = elem->index; > + pq_priority_t prio = elem->prio; > + for (;;) { > + uint32_t child = pq_smallest_child(this, current, prio); > + if (current == child) { > + /* No smaller child, we are done */ > + pq_assert_heap(this); > + return; > + } > + /* Element larger than smaller child, must move down */ > + assert(this->heap[child].elem != NULL); > + /* 1) Move child up to current */ > + this->heap[current].elem = this->heap[child].elem; > + this->heap[current].prio = this->heap[child].prio; > + /* 2) Move current down to child */ > + this->heap[child].elem = elem; > + this->heap[child].prio = prio; > + this->heap[child].elem->index = child; > + > + this->heap[current].elem->index = current; /* cache > misses! */ > + /* Continue moving element until it is in the right place > */ > + current = child; > + } > +} > + > +bool > +pq_register_element(priority_queue *this, pq_element *elem) > +{ > + if (odp_likely(this->reg_elems < this->max_elems)) { > + elem->pq = this; > + this->reg_elems++; > + return true; > + } > + return false; > +} > + > +void > +pq_unregister_element(priority_queue *this, pq_element *elem) > +{ > + assert(elem->pq == this); > + if (is_active(elem)) > + pq_deactivate_element(this, elem); > + elem->pq = NULL; > + this->reg_elems--; > +} > + > +void > +pq_activate_element(priority_queue *this, pq_element *elem, pq_priority_t > prio) > +{ > + assert(elem->pq == this); > + /* Insert element at end */ > + uint32_t index = this->num_elems++; > + this->heap[index].elem = elem; > + this->heap[index].prio = prio; > + elem->index = index; > + elem->prio = prio; > + pq_bubble_up(this, elem); > +} > + > +void > +pq_deactivate_element(priority_queue *this, pq_element *elem) > +{ > + assert(elem->pq == this); > + if (odp_likely(is_active(elem))) { > + /* Swap element with last element */ > + uint32_t current = elem->index; > + uint32_t last = --this->num_elems; > + if (odp_likely(last != current)) { > + /* Move last element to current */ > + this->heap[current].elem = this->heap[last].elem; > + this->heap[current].prio = this->heap[last].prio; > + this->heap[current].elem->index = current; > + /* Bubble down old 'last' element to its proper > place*/ > + if (this->heap[current].prio < elem->prio) > + pq_bubble_up(this, > this->heap[current].elem); > + else > + pq_bubble_down(this, > this->heap[current].elem); > + } > + elem->index = INVALID_INDEX; > + pq_assert_heap(this); > + } > +} > + > +void > +pq_reset_element(priority_queue *this, pq_element *elem, pq_priority_t > prio) > +{ > + assert(prio != INVALID_PRIORITY); > + if (odp_likely(is_active(elem))) { > + assert(prio >= elem->prio); > + elem->prio = prio; > + this->heap[elem->index].prio = prio;/* cache misses here! > */ > + pq_bubble_down(this, elem); > + pq_assert_heap(this); > + } else { > + pq_activate_element(this, elem, prio); > + } > +} > + > +pq_priority_t pq_first_priority(const priority_queue *this) > +{ > + return this->num_elems != 0 ? this->heap[0].prio : > INVALID_PRIORITY; > +} > + > +pq_element * > +pq_release_element(priority_queue *this, pq_priority_t threshold) > +{ > + if (odp_likely(this->num_elems != 0 && > + this->heap[0].prio <= threshold)) { > + pq_element *elem = this->heap[0].elem; > + /* Remove element from heap */ > + pq_deactivate_element(this, elem); > + assert(elem->prio <= threshold); > + return elem; > + } > + return NULL; > +} > diff --git a/platform/linux-generic/priority_queue.h > b/platform/linux-generic/priority_queue.h > new file mode 100644 > index 0000000..c461590 > --- /dev/null > +++ b/platform/linux-generic/priority_queue.h > @@ -0,0 +1,107 @@ > +#ifndef _PRIORITY_QUEUE_H > +#define _PRIORITY_QUEUE_H > + > +#include <assert.h> > +#include <stddef.h> > +#include <stdint.h> > +#include <stdbool.h> > + > +#define INVALID_INDEX ~0U > +#define INVALID_PRIORITY ((pq_priority_t)~0ULL) > + > +typedef uint64_t pq_priority_t; > + > +struct heap_node; > + > +typedef struct priority_queue { > + uint32_t max_elems;/* Number of elements in heap */ > + /* Number of registered elements (active + inactive) */ > + uint32_t reg_elems; > + uint32_t num_elems;/* Number of active elements */ > + struct heap_node *heap; > + struct heap_node *org_ptr; > +} priority_queue; > + > +/* The user gets a pointer to this structure */ > +typedef struct { > + /* Set when pq_element registered with priority queue */ > + priority_queue *pq; > + uint32_t index;/* Index into heap array */ > + pq_priority_t prio; > +} pq_element; > + > +/*** Operations on pq_element ***/ > + > +static inline void pq_element_con(pq_element *this) > +{ > + this->pq = NULL; > + this->index = INVALID_INDEX; > + this->prio = 0U; > +} > + > +static inline void pq_element_des(pq_element *this) > +{ > + (void)this; > + assert(this->index == INVALID_INDEX); > +} > + > +static inline priority_queue *get_pq(const pq_element *this) > +{ > + return this->pq; > +} > + > +static inline pq_priority_t get_prio(const pq_element *this) > +{ > + return this->prio; > +} > + > +static inline uint32_t get_index(const pq_element *this) > +{ > + return this->index; > +} > + > +static inline bool is_active(const pq_element *this) > +{ > + return this->index != INVALID_INDEX; > +} > + > +/*** Operations on priority_queue ***/ > + > +extern uint32_t pq_smallest_child(priority_queue *, uint32_t, > pq_priority_t); > +extern void pq_bubble_down(priority_queue *, pq_element *); > +extern void pq_bubble_up(priority_queue *, pq_element *); > + > +static inline bool valid_index(priority_queue *this, uint32_t idx) > +{ > + return idx < this->num_elems; > +} > + > +extern void priority_queue_con(priority_queue *, uint32_t _max_elems); > +extern void priority_queue_des(priority_queue *); > + > +/* Register pq_element with priority queue */ > +/* Return false if priority queue full */ > +extern bool pq_register_element(priority_queue *, pq_element *); > + > +/* Activate and add pq_element to priority queue */ > +/* Element must be disarmed */ > +extern void pq_activate_element(priority_queue *, pq_element *, > pq_priority_t); > + > +/* Reset (increase) priority for pq_element */ > +/* Element may be active or inactive (released) */ > +extern void pq_reset_element(priority_queue *, pq_element *, > pq_priority_t); > + > +/* Deactivate and remove element from priority queue */ > +/* Element may be active or inactive (released) */ > +extern void pq_deactivate_element(priority_queue *, pq_element *); > + > +/* Unregister pq_element */ > +extern void pq_unregister_element(priority_queue *, pq_element *); > + > +/* Return priority of first element (lowest numerical value) */ > +extern pq_priority_t pq_first_priority(const priority_queue *); > + > +/* Deactivate and return first element if it's prio is <= threshold */ > +extern pq_element *pq_release_element(priority_queue *, pq_priority_t > thresh); > + > +#endif /* _PRIORITY_QUEUE_H */ > diff --git a/test/api_test/odp_timer_ping.c > b/test/api_test/odp_timer_ping.c > index c1cc255..c4332e3 100644 > --- a/test/api_test/odp_timer_ping.c > +++ b/test/api_test/odp_timer_ping.c > @@ -20,6 +20,7 @@ > * Otherwise timeout may happen bcz of slow nw speed > */ > > +#include <stdlib.h> > #include <unistd.h> > #include <fcntl.h> > #include <errno.h> > @@ -43,7 +44,8 @@ > #define PING_CNT 10 > #define PING_THRD 2 /* Send and Rx Ping thread */ > > -static odp_timer_t test_timer_ping; > +static odp_timer_pool_t tp; > +static odp_timer_t test_timer_ping = ODP_TIMER_INVALID; > static odp_timer_tmo_t test_ping_tmo; > > #define PKTSIZE 64 > @@ -123,15 +125,7 @@ static int listen_to_pingack(void) > (socklen_t *)&len); > if (bytes > 0) { > /* pkt rxvd therefore cancel the timeout */ > - if (odp_timer_cancel_tmo(test_timer_ping, > - test_ping_tmo) != > 0) { > - ODP_ERR("cancel_tmo failed > ..exiting listner thread\n"); > - /* avoid exiting from here even if > tmo > - * failed for current ping, > - * allow subsequent ping_rx > request */ > - err = -1; > - > - } > + odp_timer_cancel(test_timer_ping); > /* cruel bad hack used for sender, listner > ipc.. > * euwww.. FIXME .. > */ > @@ -153,7 +147,7 @@ static int send_ping_request(struct sockaddr_in *addr) > int sd, cnt = 1; > struct packet pckt; > > - uint64_t tick; > + odp_timer_tick_t tick; > odp_queue_t queue; > odp_buffer_t buf; > > @@ -179,6 +173,12 @@ static int send_ping_request(struct sockaddr_in *addr) > > /* get the ping queue */ > queue = odp_queue_lookup("ping_timer_queue"); > + test_timer_ping = odp_timer_alloc(tp, queue, NULL); > + if (test_timer_ping == ODP_TIMER_INVALID) { > + ODP_ERR("Failed to allocate timer.\n"); > + err = -1; > + goto err; > + } > > for (i = 0; i < PING_CNT; i++) { > /* prepare icmp pkt */ > @@ -204,12 +204,10 @@ static int send_ping_request(struct sockaddr_in > *addr) > printf(" icmp_sent msg_cnt %d\n", i); > > /* arm the timer */ > - tick = odp_timer_current_tick(test_timer_ping); > + tick = odp_timer_current_tick(tp); > > tick += 1000; > - test_ping_tmo = odp_timer_absolute_tmo(test_timer_ping, > tick, > - queue, > - ODP_BUFFER_INVALID); > + odp_timer_set_abs(test_timer_ping, tick); > /* wait for timeout event */ > while ((buf = odp_queue_deq(queue)) == ODP_BUFFER_INVALID) > { > /* flag true means ack rxvd.. a cruel hack as I > @@ -225,16 +223,24 @@ static int send_ping_request(struct sockaddr_in > *addr) > } > } > > - /* free tmo_buf for timeout case */ > - if (buf != ODP_BUFFER_INVALID) { > + switch (odp_timer_tmo_status(buf)) { > + case ODP_TMO_FRESH: > ODP_DBG(" timeout msg_cnt [%i] \n", i); > /* so to avoid seg fault commented */ > - odp_buffer_free(buf); > err = -1; > + break; > + case ODP_TMO_STALE: > + /* Ignore stale timeouts */ > + break; > + case ODP_TMO_ORPHAN: > + ODP_ERR("Received orphaned timeout!\n"); > + abort(); > } > } > > err: > + if (test_timer_ping != ODP_TIMER_INVALID) > + odp_timer_free(test_timer_ping); > return err; > } > > @@ -335,7 +341,7 @@ int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED) > ODP_CACHE_LINE_SIZE, > ODP_BUFFER_TYPE_RAW); > if (pool == ODP_BUFFER_POOL_INVALID) { > - ODP_ERR("Pool create failed.\n"); > + ODP_ERR("Buffer pool create failed.\n"); > return -1; > } > > @@ -350,8 +356,18 @@ int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED) > return -1; > } > > - test_timer_ping = odp_timer_create("ping_timer", pool, > - 1000000, 1000000, > 1000000000000UL); > + /* > + * Create timer pool > + */ > + tp = odp_timer_pool_create("timer_pool", pool, > + 1000000U, /* 1 millisecond */ > + 10000000000U, /* 10 seconds */ > + 1, false, ODP_CLOCK_DEFAULT); > + if (tp == ODP_TIMER_POOL_INVALID) { > + ODP_ERR("Timer pool create failed.\n"); > + return -1; > + } > + > odp_shm_print_all(); > > pingarg.thrdarg.testcase = ODP_TIMER_PING_TEST; > -- > 1.9.1 > >
On 09/03/2014 10:48 PM, Ola Liljedahl wrote: > Any volunteers for reviewing this patch? > > -- Ola > > Hello Ola, it will be really useful if it's possible to split this big patch. At least for: 1. timer api + test example 2. priority queue priority queue also need some test case. Also we talked about your patch with Mike and Anders yesterday. You provided example in doxygen. But it will be great to move that example to source code. In that case we can check that it always valid and works. A little bit confusing with C++ style comments "//" instead of C style "/* .... */". But patchwork does not complain about that. I think we should be consistent with comments too. Best regards, Maxim. > > On 2 September 2014 17:26, Ola Liljedahl <ola.liljedahl@linaro.org > <mailto:ola.liljedahl@linaro.org>> wrote: > > Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org > <mailto:ola.liljedahl@linaro.org>> > --- > (This document/code contribution attached is provided under the > terms of agreement LES-LTM-21309) > New timer API and corresponding SW implementation for linux-generic. > Read more about use cases and usage here: > https://docs.google.com/a/linaro.org/document/d/1bfY_J8ecLJPsFTmYftb0NVmGnB9qkEc_NpcJ87yfaD8/edit# > > example/timer/odp_timer_test.c | 93 ++- > platform/linux-generic/Makefile.am | 1 + > platform/linux-generic/include/api/odp_timer.h | 478 ++++++++++-- > .../linux-generic/include/odp_timer_internal.h | 71 +- > platform/linux-generic/odp_timer.c | 823 > +++++++++++++-------- > platform/linux-generic/priority_queue.c | 289 ++++++++ > platform/linux-generic/priority_queue.h | 107 +++ > test/api_test/odp_timer_ping.c | 58 +- > 8 files changed, 1455 insertions(+), 465 deletions(-) > create mode 100644 platform/linux-generic/priority_queue.c > create mode 100644 platform/linux-generic/priority_queue.h > > diff --git a/example/timer/odp_timer_test.c > b/example/timer/odp_timer_test.c > index bf1d7df..0b4fedf 100644 > --- a/example/timer/odp_timer_test.c > +++ b/example/timer/odp_timer_test.c > @@ -35,7 +35,6 @@ > typedef struct { > int core_count; /**< Core count*/ > int resolution_us; /**< Timeout resolution in usec*/ > - int min_us; /**< Minimum timeout in usec*/ > int max_us; /**< Maximum timeout in usec*/ > int period_us; /**< Timeout period in usec*/ > int tmo_count; /**< Timeout count*/ > @@ -45,18 +44,16 @@ typedef struct { > /** @private Barrier for test synchronisation */ > static odp_barrier_t test_barrier; > > -/** @private Timer handle*/ > -static odp_timer_t test_timer; > +/** @private Timer pool handle*/ > +static odp_timer_pool_t tp; > > > /** @private test timeout */ > static void test_abs_timeouts(int thr, test_args_t *args) > { > - uint64_t tick; > - uint64_t period; > + odp_timer_tick_t period; > uint64_t period_ns; > odp_queue_t queue; > - odp_buffer_t buf; > int num; > > ODP_DBG(" [%i] test_timeouts\n", thr); > @@ -64,37 +61,51 @@ static void test_abs_timeouts(int thr, > test_args_t *args) > queue = odp_queue_lookup("timer_queue"); > > period_ns = args->period_us*USEC; > - period = odp_timer_ns_to_tick(test_timer, period_ns); > + period = odp_timer_ns_to_tick(tp, period_ns); > > ODP_DBG(" [%i] period %"PRIu64" ticks, %"PRIu64" ns\n", thr, > period, period_ns); > > - tick = odp_timer_current_tick(test_timer); > + ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, > + odp_timer_current_tick(tp)); > > - ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick); > - > - tick += period; > - > - if (odp_timer_absolute_tmo(test_timer, tick, queue, > ODP_BUFFER_INVALID) > - == ODP_TIMER_TMO_INVALID){ > - ODP_DBG("Timeout request failed\n"); > + odp_timer_t test_timer; > + test_timer = odp_timer_alloc(tp, queue, NULL); > + if (test_timer == ODP_TIMER_INVALID) { > + ODP_ERR("Failed to allocate timer\n"); > return; > } > + odp_timer_set_rel(test_timer, period); > > num = args->tmo_count; > > while (1) { > - odp_timeout_t tmo; > - > - buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); > + /* Local variables because received timeouts may not > + originate from timer we created above */ > + odp_timer_tmo_t tmo; > + odp_timer_tick_t tick; > + odp_timer_t hdl; > + > + /* Get the next ready buffer/timeout */ > + tmo = odp_schedule_one(&queue, ODP_SCHED_WAIT); > + switch (odp_timer_tmo_status(tmo)) { > + case ODP_TMO_FRESH: > + break; > + case ODP_TMO_STALE: > + ODP_DBG("[%i] Stale timeout received\n", thr); > + break; > + case ODP_TMO_ORPHAN: > + ODP_DBG("[%i] Orphaned timeout received\n", > + thr); > + odp_buffer_free(tmo); > + continue; > + } > > - tmo = odp_timeout_from_buffer(buf); > - tick = odp_timeout_tick(tmo); > + tick = odp_timer_get_expiry(tmo); > + hdl = odp_timer_get_handle(tmo); > > ODP_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, > tick); > > - odp_buffer_free(buf); > - > num--; > > if (num == 0) > @@ -102,10 +113,13 @@ static void test_abs_timeouts(int thr, > test_args_t *args) > > tick += period; > > - odp_timer_absolute_tmo(test_timer, tick, > - queue, ODP_BUFFER_INVALID); > + if (hdl != ODP_TIMER_INVALID) > + odp_timer_set_abs(hdl, tick); > } > > + odp_timer_cancel(test_timer); > + odp_timer_free(test_timer); > + > if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) > odp_schedule_release_atomic(); > } > @@ -159,7 +173,6 @@ static void print_usage(void) > printf("Options:\n"); > printf(" -c, --count <number> core count, core IDs > start from 1\n"); > printf(" -r, --resolution <us> timeout resolution in > usec\n"); > - printf(" -m, --min <us> minimum timeout in usec\n"); > printf(" -x, --max <us> maximum timeout in usec\n"); > printf(" -p, --period <us> timeout period in usec\n"); > printf(" -t, --timeouts <count> timeout repeat count\n"); > @@ -183,7 +196,6 @@ static void parse_args(int argc, char *argv[], > test_args_t *args) > static struct option longopts[] = { > {"count", required_argument, NULL, 'c'}, > {"resolution", required_argument, NULL, 'r'}, > - {"min", required_argument, NULL, 'm'}, > {"max", required_argument, NULL, 'x'}, > {"period", required_argument, NULL, 'p'}, > {"timeouts", required_argument, NULL, 't'}, > @@ -194,14 +206,13 @@ static void parse_args(int argc, char > *argv[], test_args_t *args) > /* defaults */ > args->core_count = 0; /* all cores */ > args->resolution_us = 10000; > - args->min_us = args->resolution_us; > args->max_us = 100000000; > args->period_us = 1000000; > args->tmo_count = 30; > > while (1) { > opt = getopt_long(argc, argv, "+c:r:m:x:p:t:h", > - longopts, &long_index); > + longopts, &long_index); > > if (opt == -1) > break; /* No more options */ > @@ -213,9 +224,6 @@ static void parse_args(int argc, char *argv[], > test_args_t *args) > case 'r': > args->resolution_us = atoi(optarg); > break; > - case 'm': > - args->min_us = atoi(optarg); > - break; > case 'x': > args->max_us = atoi(optarg); > break; > @@ -299,7 +307,6 @@ int main(int argc, char *argv[]) > > printf("first core: %i\n", first_core); > printf("resolution: %i usec\n", args.resolution_us); > - printf("min timeout: %i usec\n", args.min_us); > printf("max timeout: %i usec\n", args.max_us); > printf("period: %i usec\n", args.period_us); > printf("timeouts: %i\n", args.tmo_count); > @@ -323,10 +330,23 @@ int main(int argc, char *argv[]) > ODP_BUFFER_TYPE_TIMEOUT); > > if (pool == ODP_BUFFER_POOL_INVALID) { > - ODP_ERR("Pool create failed.\n"); > + ODP_ERR("Buffer pool create failed.\n"); > return -1; > } > > + tp = odp_timer_pool_create("timer_pool", pool, > + args.resolution_us*USEC, > + args.max_us*USEC, > + num_workers, /* One timer per > worker */ > + true, > + ODP_CLOCK_DEFAULT); > + if (tp == ODP_TIMER_POOL_INVALID) { > + ODP_ERR("Timer pool create failed.\n"); > + return -1; > + } > + > + odp_shm_print_all(); > + > /* > * Create a queue for timer test > */ > @@ -342,13 +362,6 @@ int main(int argc, char *argv[]) > return -1; > } > > - test_timer = odp_timer_create("test_timer", pool, > - args.resolution_us*USEC, > - args.min_us*USEC, > - args.max_us*USEC); > - > - odp_shm_print_all(); > - > printf("CPU freq %"PRIu64" hz\n", odp_sys_cpu_hz()); > printf("Cycles vs nanoseconds:\n"); > ns = 0; > diff --git a/platform/linux-generic/Makefile.am > b/platform/linux-generic/Makefile.am > index f4dfdc1..e2bc1a7 100644 > --- a/platform/linux-generic/Makefile.am > +++ b/platform/linux-generic/Makefile.am > @@ -72,4 +72,5 @@ __LIB__libodp_la_SOURCES = \ > odp_thread.c \ > odp_ticketlock.c \ > odp_time.c \ > + priority_queue.c \ > odp_timer.c > diff --git a/platform/linux-generic/include/api/odp_timer.h > b/platform/linux-generic/include/api/odp_timer.h > index 01db839..e5f961c 100644 > --- a/platform/linux-generic/include/api/odp_timer.h > +++ b/platform/linux-generic/include/api/odp_timer.h > @@ -1,4 +1,4 @@ > -/* Copyright (c) 2013, Linaro Limited > +/* Copyright (c) 2014, Linaro Limited > * All rights reserved. > * > * SPDX-License-Identifier: BSD-3-Clause > @@ -8,7 +8,175 @@ > /** > * @file > * > - * ODP timer > + * ODP timer service > + * > + > +//Example #1 Retransmission timer (e.g. for reliable connections) > + > +//Create timer pool for reliable connections > +#define SEC 1000000000ULL //1s expressed in nanoseconds > +odp_timer_pool_t tcp_tpid = > + odp_timer_pool_create("TCP", > + buffer_pool, > + 1000000,//resolution 1ms > + 7200 * SEC,//max tmo length 2hours > + 40000,//num_timers > + true,//shared > + ODP_CLOCK_DEFAULT > + ); > +if (tcp_tpid == ODP_TIMER_POOL_INVALID) > +{ > + //Failed to create timer pool => fatal error > +} > + > + > +//Setting up a new connection > +//Allocate retransmission timeout (identical for supervision timeout) > +//The user pointer points back to the connection context > +conn->ret_tim = odp_timer_alloc(tcp_tpid, queue, conn); > +//Check if all resources were successfully allocated > +if (conn->ret_tim == ODP_TIMER_INVALID) > +{ > + //Failed to allocate all resources for connection => tear down > + //Destroy timeout > + odp_timer_free(conn->ret_tim); > + //Tear down connection > + ... > + return false; > +} > +//All necessary resources successfully allocated > +//Compute initial retransmission length in timer ticks > +conn->ret_len = odp_timer_ns_to_tick(tcp_tpid, 3 * SEC);//Per RFC1122 > +//Arm the timer > +odp_timer_set_rel(conn->ret_tim, conn->ret_len); > +return true; > + > + > +//A packet for the connection has just been transmitted > +//Reset the retransmission timer > +odp_timer_set_rel(conn->ret_tim, conn->ret_len); > + > + > +//A retransmission timeout for the connection has been received > +//Check if timeout is fresh or stale, for stale timeouts we need > to reset the > +//timer > +switch (odp_timer_tmo_status(tmo)) > +{ > + case ODP_TMO_FRESH : > + //Fresh timeout, last transmitted packet not acked in time => > + retransmit > + //Get connection from timeout event > + conn = odp_timer_get_userptr(tmo); > + //Retransmit last packet (e.g. TCP segment) > + ... > + //Re-arm timer using original delta value > + odp_timer_set_rel(conn->ret_tim, conn->ret_len); > + break; > + case ODP_TMO_STALE : > + break;//Do nothing > + case ODP_TMO_ORPHAN : > + odp_free_buffer(tmo); > + return;//Get out of here > +} > + > + > +//Example #2 Periodic tick > + > +//Create timer pool for periodic ticks > +odp_timer_pool_t per_tpid = > + odp_timer_pool_create("periodic-tick", > + buffer_pool, > + 1,//resolution 1ns > + 1000000000,//maximum timeout length 1s > + 10,//num_timers > + false,//not shared > + ODP_CLOCK_DEFAULT > + ); > +if (per_tpid == ODP_TIMER_POOL_INVALID) > +{ > + //Failed to create timer pool => fatal error > +} > + > + > +//Allocate periodic timer > +tim_1733 = odp_timer_alloc(per_tpid, queue, NULL); > +//Check if all resources were successfully allocated > +if (tim_1733 == ODP_TIMER_INVALID) > +{ > + //Failed to allocate all resources => tear down > + //Destroy timeout > + odp_timer_free(tim_1733); > + //Tear down other state > + ... > + return false; > +} > +//All necessary resources successfully allocated > +//Compute tick period in timer ticks > +period_1733 = odp_timer_ns_to_tick(per_tpid, 1000000000U / > 1733U);//1733Hz > +//Compute when next tick should expire > +next_1733 = odp_timer_current_tick(per_tpid) + period_1733; > +//Arm the periodic timer > +odp_timer_set_abs(tim_1733, next_1733); > +return true; > + > + > + > +//A periodic timer timeout has been received > +//Must call odp_timer_tmo_status() on timeout! > +ret = odp_timer_tmo_status(tmo); > +//We expect the timeout is fresh since we are not calling set or > cancel on > +//active or expired timers in this example > +assert(ret == ODP_TMO_FRESH); > +//Do processing driven by timeout *before* > +... > +do { > + //Compute when the timer should expire next > + next_1733 += period_1733; > + //Check that this is in the future > + if (likely(next_1733 > odp_timer_current_tick(per_tpid)) > + break;//Yes, done > + //Else we missed a timeout > + //Optionally attempt some recovery and/or logging of the > problem > + ... > +} while (0); > +//Re-arm periodic timer > +odp_timer_set_abs(tim_1733, next_1733); > +//Or do processing driven by timeout *after* > +... > +return; > + > +//Example #3 Tear down of flow > +//ctx points to flow context data structure owned by application > +//Free the timer, cancelling any timeout > +odp_timer_free(ctx->timer);//Any enqueued timeout will be made > invalid > +//Continue tearing down and eventually freeing context > +... > +return; > + > +//A timeout has been received, check status > +switch (odp_timer_tmo_status(tmo)) > +{ > + case ODP_TMO_FRESH : > + //A flow has timed out, tear it down > + //Find flow context from timeout > + ctx = (context *)odp_timer_get_userptr(tmo); > + //Free the supervision timer, any enqueued timeout will remain > + odp_timer_free(ctx->tim); > + //Free other flow related resources > + ... > + //Flow torn down > + break; > + case ODP_TMO_STALE : > + //A stale timeout was received, timer automatically reset > + break; > + case ODP_TMO_ORPHAN : > + //Orphaned timeout (from previously torn down flow) > + //No corresponding timer or flow context > + //Free the timeout > + odp_buffer_free(tmo); > + break; > +} > + > */ > > #ifndef ODP_TIMER_H_ > @@ -23,139 +191,325 @@ extern "C" { > #include <odp_buffer_pool.h> > #include <odp_queue.h> > > +/** > +* ODP timer pool handle (platform dependent) > +*/ > +struct odp_timer_pool; > +typedef struct odp_timer_pool *odp_timer_pool_t; > > /** > - * ODP timer handle > + * Invalid timer pool handle (platform dependent) > */ > -typedef uint32_t odp_timer_t; > +#define ODP_TIMER_POOL_INVALID NULL > > -/** Invalid timer */ > -#define ODP_TIMER_INVALID 0 > +typedef enum odp_timer_pool_clock_source_e { > + ODP_CLOCK_DEFAULT = 0, > + /* Platform dependent which clock sources exist beyond > + ODP_CLOCK_DEFAULT */ > + ODP_CLOCK_NONE = 1 > +} odp_timer_pool_clock_source_t; > > +/** > +* ODP timer handle (platform dependent) > +*/ > +struct odp_timer; > +typedef struct odp_timer *odp_timer_t; > > /** > - * ODP timeout handle > + * Invalid timer handle (platform dependent) > */ > -typedef odp_buffer_t odp_timer_tmo_t; > - > -/** Invalid timeout */ > -#define ODP_TIMER_TMO_INVALID 0 > +#define ODP_TIMER_INVALID NULL > > +/** > + * ODP timeout event handle > + */ > +typedef odp_buffer_t odp_timer_tmo_t; > > /** > - * Timeout notification > + * ODP timeout status > */ > -typedef odp_buffer_t odp_timeout_t; > +typedef enum odp_timer_tmo_status_e { > + ODP_TMO_FRESH, /* Timeout is fresh, process it */ > + ODP_TMO_STALE, /* Timer reset or cancelled, do nothing */ > + ODP_TMO_ORPHAN,/* Timer deleted, free timeout */ > +} odp_timer_tmo_status_t; > + > +/** > +* ODP tick value > +*/ > +typedef uint64_t odp_timer_tick_t; > > > /** > - * Create a timer > + * Create a timer pool > * > - * Creates a new timer with requested properties. > + * Create a new timer pool. > + * odp_timer_pool_create() is typically called once or a couple > of times during > + * application initialisation. > * > * @param name Name > - * @param pool Buffer pool for allocating timeout notifications > + * @param buf_pool Buffer pool for allocating timers > * @param resolution Timeout resolution in nanoseconds > - * @param min_tmo Minimum timeout duration in nanoseconds > - * @param max_tmo Maximum timeout duration in nanoseconds > + * @param max_tmo Maximum relative timeout in nanoseconds > + * @param num_timers Number of supported timers (minimum) > + * @param shared Shared or private timer pool. > + * Operations on shared timers will include the > necessary > + * mutual exclusion, operations on private timers > may not > + * (mutual exclusion is the responsibility of the > caller). > + * @param clk_src Clock source to use > + * > + * @return Timer pool handle if successful, otherwise > ODP_TIMER_POOL_INVALID > + * and errno set > + */ > +odp_timer_pool_t > +odp_timer_pool_create(const char *name, > + odp_buffer_pool_t buf_pool, > + uint64_t resolution, > + uint64_t max_tmo, > + uint32_t num_timers, > + bool shared, > + odp_timer_pool_clock_source_t clk_src); > + > +/** > + * Start a timer pool > + * > + * 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 > * > - * @return Timer handle if successful, otherwise ODP_TIMER_INVALID > + * Destroy a timer pool, freeing all resources. > + * All timers must have been freed. > + * > + * @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, > odp_timer_tick_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); > +odp_timer_tick_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 > + */ > +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid); > + > +/** > + * ODP timer configurations > */ > -uint64_t odp_timer_resolution(odp_timer_t timer); > + > +typedef enum odp_timer_pool_conf_e { > + ODP_TIMER_NAME, /* Return name of timer pool */ > + ODP_TIMER_RESOLUTION,/* Return the timer resolution (in ns) */ > + ODP_TIMER_MAX_TMO, /* Return the maximum supported > timeout (in ns) */ > + ODP_TIMER_NUM_TIMERS,/* Return number of supported timers */ > + ODP_TIMER_SHARED /* Return shared flag */ > +} odp_timer_pool_conf_t; > > /** > - * Maximum timeout in timer ticks > + * Query different timer pool configurations, e.g. > + * Timer resolution in nanoseconds > + * Maximum timeout in timer ticks > + * Number of supported timers > + * Shared or private timer pool > * > - * @param timer Timer > + * @param tpid Timer pool identifier > + * @param item Configuration item being queried > * > - * @return Maximum timeout in timer ticks > + * @return the requested piece of information or 0 for unknown item. > */ > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer); > +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, > + odp_timer_pool_conf_t item); > > /** > - * Current timer tick > + * Allocate a timer > * > - * @param timer Timer > + * Create a timer (allocating all necessary resources e.g. > timeout event) from > + * the timer pool. > * > - * @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 (copied to timeouts) > + * > + * @return Timer handle if successful, otherwise > ODP_TIMER_INVALID and > + * errno set. > + */ > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > + odp_queue_t queue, > + void *user_ptr); > + > +/** > + * Free a timer > + * > + * Free (destroy) a timer, freeing all associated resources (e.g. > default > + * timeout event). An expired and enqueued timeout event will not > be freed. > + * It is the responsibility of the application to free this > timeout when it > + * is received. > + * > + * @param tim Timer handle > */ > -uint64_t odp_timer_current_tick(odp_timer_t timer); > +void odp_timer_free(odp_timer_t tim); > > /** > - * Request timeout with an absolute timer tick > + * Set a timer (absolute time) with a user-defined timeout buffer > * > - * When tick reaches tmo_tick, the timer enqueues the timeout > notification into > - * the destination queue. > + * Set (arm) the timer to expire at specific time. The user-defined > + * buffer will be enqueued when the timer expires. > + * Arming may fail (if the timer is in state EXPIRED), an earlier > timeout > + * will then be received. odp_timer_tmo_status() must be used to > check if > + * the received timeout is valid. > * > - * @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. > + * Note: any invalid parameters will be treated as programming > errors and will > + * cause the application to abort. > + * Note: a timeout too near in time may be delivered immediately. > + * Note: a timeout too far away in time (beyond max_timeout) > might be delivered > + * early. > * > - * @return Timeout handle if successful, otherwise > ODP_TIMER_TMO_INVALID > + * @param tim Timer > + * @param abs_tck Expiration time in absolute timer ticks > + * @param user_buf The buffer to use as timeout event > */ > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, > uint64_t tmo_tick, > - odp_queue_t queue, > odp_buffer_t buf); > +void odp_timer_set_abs_w_buf(odp_timer_t tim, > + odp_timer_tick_t abs_tck, > + odp_buffer_t user_buf); > > /** > - * Cancel a timeout > + * Set a timer with an absolute expiration time > + * > + * Set (arm) the timer to expire at a specific time. > + * Arming may fail (if the timer is in state EXPIRED), an earlier > timeout > + * will then be received. odp_timer_tmo_status() must be used to > check if > + * the received timeout is valid. > * > - * @param timer Timer > - * @param tmo Timeout to cancel > + * Note: any invalid parameters will be treated as programming > errors and will > + * cause the application to abort. > + * Note: a timeout too near in time may be delivered immediately. > + * Note: a timeout too far away in time (beyond max_timeout) > might be delivered > + * early, it will automatically be reset by odp_timer_tmo_status(). > * > - * @return 0 if successful > + * @param tim Timer > + * @param abs_tck Expiration time in absolute timer ticks > */ > -int odp_timer_cancel_tmo(odp_timer_t timer, odp_timer_tmo_t tmo); > +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck); > > /** > - * Convert buffer handle to timeout handle > + * Set a timer with a relative expiration time > * > - * @param buf Buffer handle > + * Set (arm) the timer to expire at a relative future time. > + * Arming may fail (if the timer is in state EXPIRED), > + * an earlier timeout will then be received. > odp_timer_tmo_status() must > + * be used to check if the received timeout is valid. > * > - * @return Timeout buffer handle > + * Note: any invalid parameters will be treated as programming > errors and will > + * cause the application to abort. > + * Note: a timeout too near in time may be delivered immediately. > + * Note: a timeout too far away in time (beyond max_timeout) > might be delivered > + * early, it will automatically be reset by odp_timer_tmo_status(). > + * > + * @param tim Timer > + * @param rel_tck Expiration time in timer ticks relative to > current time of > + * the timer pool the timer belongs to > */ > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf); > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck); > > /** > - * Return absolute timeout tick > + * Cancel a timer > + * > + * Cancel a timer, preventing future expiration and delivery. > + * > + * A timer that has already expired and been enqueued for > delivery may be > + * impossible to cancel and will instead be delivered to the > destination queue. > + * Use odp_timer_tmo_status() the check whether a received > timeout is fresh or > + * stale (cancelled). Stale timeouts will automatically be recycled. > * > - * @param tmo Timeout buffer handle > + * Note: any invalid parameters will be treated as programming > errors and will > + * cause the application to abort. > * > - * @return Absolute timeout tick > + * @param tim Timer handle > */ > -uint64_t odp_timeout_tick(odp_timeout_t tmo); > +void odp_timer_cancel(odp_timer_t tim); > + > +/** > + * Return fresh/stale/orphan status of timeout. > + * > + * Check a received timeout for orphaness (i.e. parent timer > freed) and > + * staleness (i.e. parent timer has been reset or cancelled after > timeout > + * was enqueued). > + * If the timeout is fresh, it should be processed. > + * If the timeout is stale, the timer will automatically be reset > unless it > + * was cancelled. > + * If the timeout is orphaned, it should be freed (by the caller). > + * > + * Note: odp_timer_tmo_status() must be called on all received (not > + * user-defined) timeouts! > + * > + * @param tmo Timeout > + * > + * @return ODP_TMO_FRESH, ODP_TMO_STALE, ODP_TMO_ORPHAN > + */ > +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo); > + > +/** > + * Get timer handle > + * > + * Return Handle of parent timer. > + * > + * @param tmo Timeout > + * > + * @return Timer handle or ODP_TIMER_INVALID for orphaned timeouts > + */ > +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo); > + > +/** > + * Get expiration time > + * > + * Return (actual) expiration time of timeout. > + * > + * @param tmo Timeout > + * > + * @return Expiration time > + */ > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo); > + > +/** > + * Get user pointer > + * > + * Return User pointer of timer associated with timeout. > + * The user pointer is often used to point to some associated > context. > + * > + * @param tmo Timeout > + * > + * @return User pointer > + */ > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo); > + > +/* Helper functions */ > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, > odp_timer_tick_t tick); > > #ifdef __cplusplus > } > diff --git a/platform/linux-generic/include/odp_timer_internal.h > b/platform/linux-generic/include/odp_timer_internal.h > index ad28f53..ff8f209 100644 > --- a/platform/linux-generic/include/odp_timer_internal.h > +++ b/platform/linux-generic/include/odp_timer_internal.h > @@ -1,4 +1,4 @@ > -/* Copyright (c) 2013, Linaro Limited > +/* Copyright (c) 2014, Linaro Limited > * All rights reserved. > * > * SPDX-License-Identifier: BSD-3-Clause > @@ -8,72 +8,53 @@ > /** > * @file > * > - * ODP timer timeout descriptor - implementation internal > + * ODP timeout descriptor - implementation internal > */ > > #ifndef ODP_TIMER_INTERNAL_H_ > #define ODP_TIMER_INTERNAL_H_ > > -#ifdef __cplusplus > -extern "C" { > -#endif > - > -#include <odp_std_types.h> > -#include <odp_queue.h> > -#include <odp_buffer.h> > +#include <odp_align.h> > +#include <odp_debug.h> > #include <odp_buffer_internal.h> > #include <odp_buffer_pool_internal.h> > #include <odp_timer.h> > > -struct timeout_t; > - > -typedef struct timeout_t { > - struct timeout_t *next; > - int timer_id; > - int tick; > - uint64_t tmo_tick; > - odp_queue_t queue; > - odp_buffer_t buf; > - odp_buffer_t tmo_buf; > -} timeout_t; > - > - > -struct odp_timeout_hdr_t; > - > /** > - * Timeout notification header > + * Internal Timeout header > */ > -typedef struct odp_timeout_hdr_t { > +typedef struct { > + /* common buffer header */ > odp_buffer_hdr_t buf_hdr; > > - timeout_t meta; > - > + /* Requested expiration time */ > + odp_timer_tick_t expiration; > + /* User ptr inherited from parent timer */ > + void *user_ptr; > + /* Parent timer */ > + odp_timer_t timer; > + /* Tag inherited from parent timer at time of expiration */ > + uint32_t tag; > + /* Gen-cnt inherited from parent timer at time of creation */ > + uint32_t gc; > uint8_t buf_data[]; > } odp_timeout_hdr_t; > > - > - > +/* C++ doesn't allow offsetof() on "non-POD" datatypes. Don't > know why > + odp_timeout_hdr_t is classified as non-POD, perhaps because of the > + inheritance? */ > ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) == > - ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), > - "ODP_TIMEOUT_HDR_T__SIZE_ERR"); > - > + ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), > + "sizeof(odp_timeout_hdr_t) == > ODP_OFFSETOF(odp_timeout_hdr_t, buf_data)"); > ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == 0, > - "ODP_TIMEOUT_HDR_T__SIZE_ERR2"); > - > + "sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == > 0"); > > /** > - * Return timeout header > + * Return the timeout header > */ > -static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_timeout_t tmo) > +static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_buffer_t buf) > { > - odp_buffer_hdr_t *buf_hdr = odp_buf_to_hdr((odp_buffer_t)tmo); > - return (odp_timeout_hdr_t *)(uintptr_t)buf_hdr; > -} > - > - > - > -#ifdef __cplusplus > + return (odp_timeout_hdr_t *)odp_buf_to_hdr(buf); > } > -#endif > > #endif > diff --git a/platform/linux-generic/odp_timer.c > b/platform/linux-generic/odp_timer.c > index 1bf37f9..cea47a3 100644 > --- a/platform/linux-generic/odp_timer.c > +++ b/platform/linux-generic/odp_timer.c > @@ -1,402 +1,631 @@ > -/* Copyright (c) 2013, Linaro Limited > +/* Copyright (c) 2014, Linaro Limited > * All rights reserved. > * > * SPDX-License-Identifier: BSD-3-Clause > */ > > -#include <odp_timer.h> > -#include <odp_timer_internal.h> > -#include <odp_buffer_pool_internal.h> > -#include <odp_internal.h> > -#include <odp_atomic.h> > -#include <odp_spinlock.h> > -#include <odp_sync.h> > -#include <odp_debug.h> > > -#include <signal.h> > -#include <time.h> > +/** > + * @file > + * > + * ODP timer service > + * > + */ > > +#include <assert.h> > +#include <errno.h> > #include <string.h> > - > -#define NUM_TIMERS 1 > -#define MAX_TICKS 1024 > -#define RESOLUTION_NS 1000000 > - > - > -typedef struct { > - odp_spinlock_t lock; > - timeout_t *list; > -} tick_t; > - > -typedef struct { > - int allocated; > - volatile int active; > - volatile uint64_t cur_tick; > - timer_t timerid; > - odp_timer_t timer_hdl; > - odp_buffer_pool_t pool; > - uint64_t resolution_ns; > - uint64_t max_ticks; > - tick_t tick[MAX_TICKS]; > - > -} timer_ring_t; > - > -typedef struct { > - odp_spinlock_t lock; > - int num_timers; > - timer_ring_t timer[NUM_TIMERS]; > - > -} timer_global_t; > - > -/* Global */ > -static timer_global_t odp_timer; > - > -static void add_tmo(tick_t *tick, timeout_t *tmo) > +#include <stdlib.h> > +#include <time.h> > +#include <signal.h> > +#include "odp_std_types.h" > +#include "odp_buffer.h" > +#include "odp_buffer_pool.h" > +#include "odp_queue.h" > +#include "odp_hints.h" > +#include "odp_sync.h" > +#include "odp_spinlock.h" > +#include "odp_debug.h" > +#include "odp_align.h" > +#include "odp_shared_memory.h" > +#include "odp_hints.h" > +#include "odp_internal.h" > +#include "odp_timer.h" > +#include "odp_timer_internal.h" > +#include "priority_queue.h" > + > +typedef struct odp_timer { > + pq_element pqelem;/* Base class */ > + odp_timer_tick_t req_tmo;/* Requested timeout tick */ > + odp_buffer_t tmo_buf;/* ODP_BUFFER_INVALID if timeout > enqueued */ > + odp_queue_t queue;/* ODP_QUEUE_INVALID if timer is free */ > + uint32_t tag;/* Reusing tag as next pointer/index when > timer is free */ > + uint32_t gc; > + bool user_buf; /* User-defined buffer? */ > +} odp_timer; > + > +/* Constructor for array of objects */ > +static inline void odp_timer_con(odp_timer *this) > { > - odp_spinlock_lock(&tick->lock); > - > - tmo->next = tick->list; > - tick->list = tmo; > - > - odp_spinlock_unlock(&tick->lock); > + pq_element_con(&this->pqelem); > + this->tmo_buf = ODP_BUFFER_INVALID; > + this->queue = ODP_QUEUE_INVALID; > + this->gc = 0; > } > > -static timeout_t *rem_tmo(tick_t *tick) > +/* Destructor */ > +static inline void odp_timer_des(odp_timer *this) > { > - timeout_t *tmo; > - > - odp_spinlock_lock(&tick->lock); > - > - tmo = tick->list; > - > - if (tmo) > - tick->list = tmo->next; > - > - odp_spinlock_unlock(&tick->lock); > + assert(this->tmo_buf == ODP_BUFFER_INVALID); > + assert(this->queue == ODP_QUEUE_INVALID); > + pq_element_des(&this->pqelem); > +} > > - if (tmo) > - tmo->next = NULL; > +/* Setup when timer is allocated */ > +static void setup(odp_timer *this, > + odp_queue_t _q, > + void *_up, > + odp_buffer_t _tmo) > +{ > + this->req_tmo = INVALID_PRIORITY; > + this->tmo_buf = _tmo; > + this->queue = _q; > + this->tag = 0; > + this->user_buf = false; > + /* Initialise constant fields of timeout event */ > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(this->tmo_buf); > + tmo_hdr->gc = this->gc; > + tmo_hdr->timer = this; > + tmo_hdr->user_ptr = _up; > + /* tmo_hdr->tag set at expiration time */ > + /* tmo_hdr->expiration set at expiration time */ > + assert(this->queue != ODP_QUEUE_INVALID); > +} > > - return tmo; > +/* Teardown when timer is freed */ > +static odp_buffer_t teardown(odp_timer *this) > +{ > + /* Increase generation count to make pending timeout > orphaned */ > + ++this->gc; > + odp_buffer_t buf = this->tmo_buf; > + this->tmo_buf = ODP_BUFFER_INVALID; > + this->queue = ODP_QUEUE_INVALID; > + return buf; > } > > -/** > - * Search and delete tmo entry from timeout list > - * return -1 : on error.. handle not in list > - * 0 : success > - */ > -static int find_and_del_tmo(timeout_t **tmo, odp_timer_tmo_t handle) > +static inline uint32_t get_next_free(odp_timer *this) > { > - timeout_t *cur, *prev; > - prev = NULL; > + assert(this->queue == ODP_QUEUE_INVALID); > + return this->tag; > +} > > - for (cur = *tmo; cur != NULL; prev = cur, cur = cur->next) { > - if (cur->tmo_buf == handle) { > - if (prev == NULL) > - *tmo = cur->next; > - else > - prev->next = cur->next; > +static inline void set_next_free(odp_timer *this, uint32_t nf) > +{ > + assert(this->queue == ODP_QUEUE_INVALID); > + this->tag = nf; > +} > > - break; > +static inline void expire(odp_timer *this, odp_timer_tick_t tick) > +{ > + /* Timer expired, is there actually any timeout event */ > + /* we can enqueue? */ > + if (odp_likely(this->tmo_buf != ODP_BUFFER_INVALID)) { > + /* Swap out timeout buffer */ > + odp_buffer_t buf = this->tmo_buf; > + this->tmo_buf = ODP_BUFFER_INVALID; > + if (odp_likely(!this->user_buf)) { > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t > *)odp_buf_to_hdr(buf); > + /* Copy tag from timer */ > + /* and actual expiration tick from timer > pool */ > + tmo_hdr->tag = this->tag; > + tmo_hdr->expiration = tick; > } > + /* Else don't touch user-defined buffer */ > + int rc = odp_queue_enq(this->queue, buf); > + if (rc != 0) > + abort(); > } > - > - if (!cur) > - /* couldn't find tmo in list */ > - return -1; > - > - /* application to free tmo_buf provided by absolute_tmo > call */ > - return 0; > + /* No, timeout event already enqueued */ > } > > -int odp_timer_cancel_tmo(odp_timer_t timer_hdl, odp_timer_tmo_t tmo) > +typedef struct odp_timer_pool { > + priority_queue pq; > + uint64_t tick; > + bool shared; > + odp_spinlock_t lock; > + const char *name; > + odp_buffer_pool_t buf_pool; > + uint64_t resolution_ns; > + uint64_t max_timeout; > + odp_timer *timers; > + uint32_t num_alloc;/* Current number of allocated timers */ > + uint32_t max_timers;/* Max number of timers */ > + uint32_t first_free;/* 0..max_timers-1 => free timer */ > + timer_t timerid; > + odp_timer_pool_clock_source_t clk_src; > +} odp_timer_pool; > + > +/* Forward declarations */ > +static void timer_init(odp_timer_pool *tp); > +static void timer_exit(odp_timer_pool *tp); > + > +static void odp_timer_pool_con(odp_timer_pool *this, > + const char *_n, > + odp_buffer_pool_t _bp, > + uint64_t _r, > + uint64_t _m, > + uint32_t _mt, > + bool _s, > + odp_timer_pool_clock_source_t _cs) > { > - int id; > - uint64_t tick_idx; > - timeout_t *cancel_tmo; > - odp_timeout_hdr_t *tmo_hdr; > - tick_t *tick; > - > - /* get id */ > - id = timer_hdl - 1; > - > - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo); > - /* get tmo_buf to cancel */ > - cancel_tmo = &tmo_hdr->meta; > - > - tick_idx = cancel_tmo->tick; > - tick = &odp_timer.timer[id].tick[tick_idx]; > - > - odp_spinlock_lock(&tick->lock); > - /* search and delete tmo from tick list */ > - if (find_and_del_tmo(&tick->list, tmo) != 0) { > - odp_spinlock_unlock(&tick->lock); > - ODP_DBG("Couldn't find the tmo (%d) in tick > list\n", (int)tmo); > - return -1; > + priority_queue_con(&this->pq, _mt); > + this->tick = 0; > + this->shared = _s; > + this->name = strdup(_n); > + this->buf_pool = _bp; > + this->resolution_ns = _r; > + this->max_timeout = _m; > + this->num_alloc = 0; > + this->max_timers = _mt; > + this->first_free = 0; > + this->clk_src = _cs; > + this->timers = malloc(sizeof(odp_timer) * this->max_timers); > + if (this->timers == NULL) { > + ODP_ERR("%s: malloc failed\n", _n); > + abort(); > } > - odp_spinlock_unlock(&tick->lock); > - > - return 0; > + uint32_t i; > + for (i = 0; i < this->max_timers; i++) > + odp_timer_con(&this->timers[i]); > + for (i = 0; i < this->max_timers; i++) > + set_next_free(&this->timers[i], i + 1); > + odp_spinlock_init(&this->lock); > + if (this->clk_src == ODP_CLOCK_DEFAULT) > + timer_init(this); > + /* Make sure timer pool initialisation is globally > observable */ > + /* before we return a pointer to it */ > + odp_sync_stores(); > } > > -static void notify_function(union sigval sigval) > +static odp_timer_pool *odp_timer_pool_new( > + const char *_n, > + odp_buffer_pool_t _bp, > + uint64_t _r, > + uint64_t _m, > + uint32_t _mt, > + bool _s, > + odp_timer_pool_clock_source_t _cs) > { > - uint64_t cur_tick; > - timeout_t *tmo; > - tick_t *tick; > - timer_ring_t *timer; > - > - timer = sigval.sival_ptr; > - > - if (timer->active == 0) { > - ODP_DBG("Timer (%u) not active\n", timer->timer_hdl); > - return; > + odp_timer_pool *this = malloc(sizeof(odp_timer_pool)); > + if (odp_unlikely(this == NULL)) { > + ODP_ERR("%s: malloc failed\n", _n); > + abort(); > } > + odp_timer_pool_con(this, _n, _bp, _r, _m, _mt, _s, _cs); > + return this; > +} > > - /* ODP_DBG("Tick\n"); */ > +static void odp_timer_pool_des(odp_timer_pool *this) > +{ > + if (this->shared) > + odp_spinlock_lock(&this->lock); > + if (this->num_alloc != 0) { > + /* It's a programming error to attempt to destroy a */ > + /* timer pool which is still in use */ > + ODP_ERR("%s: timers in use\n", this->name); > + abort(); > + } > + if (this->clk_src == ODP_CLOCK_DEFAULT) > + timer_exit(this); > + uint32_t i; > + for (i = 0; i < this->max_timers; i++) > + odp_timer_des(&this->timers[i]); > + free(this->timers); > + priority_queue_des(&this->pq); > + odp_sync_stores(); > +} > > - cur_tick = timer->cur_tick++; > +static void odp_timer_pool_del(odp_timer_pool *this) > +{ > + odp_timer_pool_des(this); > + free(this); > +} > > - odp_sync_stores(); > +static inline odp_timer *timer_alloc(odp_timer_pool *this, > + odp_queue_t queue, > + void *user_ptr, > + odp_buffer_t tmo_buf) > +{ > + odp_timer *tim = ODP_TIMER_INVALID; > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + if (odp_likely(this->num_alloc < this->max_timers)) { > + this->num_alloc++; > + /* Remove first unused timer from free list */ > + assert(this->first_free != this->max_timers); > + tim = &this->timers[this->first_free]; > + this->first_free = get_next_free(tim); > + /* Insert timer into priority queue */ > + if (odp_unlikely(!pq_register_element(&this->pq, > + &tim->pqelem))) { > + /* Unexpected internal error */ > + abort(); > + } > + /* Create timer */ > + setup(tim, queue, user_ptr, tmo_buf); > + } else { > + errno = ENFILE; /* Reusing file table overvlow */ > + } > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + return tim; > +} > > - tick = &timer->tick[cur_tick % MAX_TICKS]; > +static inline void timer_free(odp_timer_pool *this, odp_timer *tim) > +{ > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + /* Destroy timer */ > + odp_buffer_t buf = teardown(tim); > + /* Remove timer from priority queue */ > + pq_unregister_element(&this->pq, &tim->pqelem); > + /* Insert timer into free list */ > + set_next_free(tim, this->first_free); > + this->first_free = (tim - &this->timers[0]) / > sizeof(this->timers[0]); > + assert(this->num_alloc != 0); > + this->num_alloc--; > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + if (buf != ODP_BUFFER_INVALID) > + odp_buffer_free(buf); > +} > > - while ((tmo = rem_tmo(tick)) != NULL) { > - odp_queue_t queue; > - odp_buffer_t buf; > +static inline void timer_reset(odp_timer_pool *this, > + odp_timer *tim, > + odp_timer_tick_t abs_tck) > +{ > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + /* Increase timer tag to make any pending timeout stale */ > + tim->tag++; > + /* Save requested timeout */ > + tim->req_tmo = abs_tck; > + /* Update timer position in priority queue */ > + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > +} > > - queue = tmo->queue; > - buf = tmo->buf; > +static inline void timer_reset_w_buf(odp_timer_pool *this, > + odp_timer *tim, > + odp_timer_tick_t abs_tck, > + odp_buffer_t user_buf) > +{ > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + /* Increase timer tag to make any pending timeout stale */ > + tim->tag++; > + /* Save requested timeout */ > + tim->req_tmo = abs_tck; > + /* Set flag indicating presence of user defined buffer */ > + tim->user_buf = true; > + /* Swap in new buffer, get any old buffer pointer */ > + odp_buffer_t old_buf = tim->tmo_buf; > + tim->tmo_buf = user_buf; > + /* Update timer position in priority queue */ > + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + /* Free old buffer if present */ > + if (odp_unlikely(old_buf != ODP_BUFFER_INVALID)) > + odp_buffer_free(old_buf); > +} > > - if (buf != tmo->tmo_buf) > - odp_buffer_free(tmo->tmo_buf); > +static inline void timer_cancel(odp_timer_pool *this, > + odp_timer *tim) > +{ > + odp_buffer_t tmo_buf = ODP_BUFFER_INVALID; > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + if (odp_unlikely(tim->user_buf)) { > + /* Swap out old user buffer */ > + tmo_buf = tim->tmo_buf; > + tim->tmo_buf = ODP_BUFFER_INVALID; > + tim->user_buf = false; > + } > + /* Else a normal timer (no user-defined buffer) */ > + /* Increase timer tag to make any pending timeout stale */ > + tim->tag++; > + /* Clear requested timeout */ > + tim->req_tmo = INVALID_PRIORITY; > + /* Remove timer from the priority queue */ > + pq_deactivate_element(&this->pq, &tim->pqelem); > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + /* Free user-defined buffer if present */ > + if (odp_unlikely(tmo_buf != ODP_BUFFER_INVALID)) > + odp_buffer_free(tmo_buf); > +} > > - odp_queue_enq(queue, buf); > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, > odp_timer_tick_t tick) > +{ > + odp_spinlock_lock(&tpid->lock); > + unsigned nexp = 0; > + odp_timer_t tim; > + tpid->tick = tick; > + while ((tim = (odp_timer_t)pq_release_element(&tpid->pq, > tick)) != > + ODP_TIMER_INVALID) { > + assert(get_prio(&tim->pqelem) <= tick); > + expire(tim, tick); > + nexp++; > } > + odp_spinlock_unlock(&tpid->lock); > + return nexp; > } > > -static void timer_start(timer_ring_t *timer) > +/* Functions that use Linux/POSIX per-process timers and related > facilities */ > +static void timer_notify(union sigval sigval) > +{ > + odp_timer_pool *tp = (odp_timer_pool *)sigval.sival_ptr; > + uint64_t new_tick = tp->tick + 1; > + (void)odp_timer_pool_expire(tp, new_tick); > +} > + > +static void timer_init(odp_timer_pool *tp) > { > struct sigevent sigev; > struct itimerspec ispec; > > - ODP_DBG("\nTimer (%u) starts\n", timer->timer_hdl); > + ODP_DBG("Creating POSIX timer for timer pool %s, period %" > + PRIu64" ns\n", tp->name, tp->resolution_ns); > > memset(&sigev, 0, sizeof(sigev)); > memset(&ispec, 0, sizeof(ispec)); > > sigev.sigev_notify = SIGEV_THREAD; > - sigev.sigev_notify_function = notify_function; > - sigev.sigev_value.sival_ptr = timer; > + sigev.sigev_notify_function = timer_notify; > + sigev.sigev_value.sival_ptr = tp; > > - if (timer_create(CLOCK_MONOTONIC, &sigev, &timer->timerid)) { > - ODP_DBG("Timer create failed\n"); > - return; > + if (timer_create(CLOCK_MONOTONIC, &sigev, &tp->timerid)) { > + perror("timer_create"); > + abort(); > } > > ispec.it_interval.tv_sec = 0; > - ispec.it_interval.tv_nsec = RESOLUTION_NS; > + ispec.it_interval.tv_nsec = tp->resolution_ns; > ispec.it_value.tv_sec = 0; > - ispec.it_value.tv_nsec = RESOLUTION_NS; > + ispec.it_value.tv_nsec = tp->resolution_ns; > > - if (timer_settime(timer->timerid, 0, &ispec, NULL)) { > - ODP_DBG("Timer set failed\n"); > - return; > + if (timer_settime(&tp->timerid, 0, &ispec, NULL)) { > + perror("timer_settime"); > + abort(); > } > - > - return; > } > > -int odp_timer_init_global(void) > +static void timer_exit(odp_timer_pool *tp) > { > - ODP_DBG("Timer init ..."); > - > - memset(&odp_timer, 0, sizeof(timer_global_t)); > - > - odp_spinlock_init(&odp_timer.lock); > - > - ODP_DBG("done\n"); > - > - return 0; > + if (timer_delete(tp->timerid) != 0) { > + perror("timer_delete"); > + abort(); > + } > } > > -int odp_timer_disarm_all(void) > +odp_timer_pool_t > +odp_timer_pool_create(const char *name, > + odp_buffer_pool_t buf_pool, > + uint64_t resolution_ns, > + uint64_t max_timeout, > + uint32_t num_timers, > + bool shared, > + odp_timer_pool_clock_source_t clk_src) > { > - int timers; > - struct itimerspec ispec; > - > - odp_spinlock_lock(&odp_timer.lock); > - > - timers = odp_timer.num_timers; > - > - ispec.it_interval.tv_sec = 0; > - ispec.it_interval.tv_nsec = 0; > - ispec.it_value.tv_sec = 0; > - ispec.it_value.tv_nsec = 0; > - > - for (; timers >= 0; timers--) { > - if (timer_settime(odp_timer.timer[timers].timerid, > - 0, &ispec, NULL)) { > - ODP_DBG("Timer reset failed\n"); > - odp_spinlock_unlock(&odp_timer.lock); > - return -1; > - } > - odp_timer.num_timers--; > + /* Verify that buffer pool can be used for timeouts */ > + odp_buffer_t buf = odp_buffer_alloc(buf_pool); > + if (buf == ODP_BUFFER_INVALID) { > + ODP_ERR("%s: Failed to allocate buffer\n", name); > + abort(); > } > + if (odp_buffer_type(buf) != ODP_BUFFER_TYPE_TIMEOUT) { > + ODP_ERR("%s: Buffer pool wrong type\n", name); > + abort(); > + } > + odp_buffer_free(buf); > + odp_timer_pool_t tp = odp_timer_pool_new(name, buf_pool, > resolution_ns, > + max_timeout, num_timers, > + shared, clk_src); > + return tp; > +} > > - odp_spinlock_unlock(&odp_timer.lock); > +void odp_timer_pool_start(void) > +{ > + /* Nothing to do here */ > +} > > - return 0; > +void odp_timer_pool_destroy(odp_timer_pool_t tpid) > +{ > + odp_timer_pool_del(tpid); > } > > -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) > +uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, > odp_timer_tick_t ticks) > { > - uint32_t id; > - timer_ring_t *timer; > - odp_timer_t timer_hdl; > - int i; > - (void) name; (void) resolution; (void) min_tmo; (void) > max_tmo; > + return ticks * tpid->resolution_ns; > +} > > - odp_spinlock_lock(&odp_timer.lock); > +odp_timer_tick_t odp_timer_ns_to_tick(odp_timer_pool_t tpid, > uint64_t ns) > +{ > + return (odp_timer_tick_t)(ns / tpid->resolution_ns); > +} > > - if (odp_timer.num_timers >= NUM_TIMERS) { > - odp_spinlock_unlock(&odp_timer.lock); > - return ODP_TIMER_INVALID; > - } > +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid) > +{ > + return tpid->tick; > +} > > - for (id = 0; id < NUM_TIMERS; id++) { > - if (odp_timer.timer[id].allocated == 0) > - break; > +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, > + odp_timer_pool_conf_t item) > +{ > + switch (item) { > + case ODP_TIMER_NAME: > + return (uint64_t)(tpid->name); > + case ODP_TIMER_RESOLUTION: > + return tpid->resolution_ns; > + case ODP_TIMER_MAX_TMO: > + return tpid->max_timeout; > + case ODP_TIMER_NUM_TIMERS: > + return tpid->max_timers; > + case ODP_TIMER_SHARED: > + return tpid->shared; > + default: > + return 0; > } > +} > > - timer = &odp_timer.timer[id]; > - timer->allocated = 1; > - odp_timer.num_timers++; > - > - odp_spinlock_unlock(&odp_timer.lock); > - > - timer_hdl = id + 1; > - > - timer->timer_hdl = timer_hdl; > - timer->pool = pool; > - timer->resolution_ns = RESOLUTION_NS; > - timer->max_ticks = MAX_TICKS; > - > - for (i = 0; i < MAX_TICKS; i++) { > - odp_spinlock_init(&timer->tick[i].lock); > - timer->tick[i].list = NULL; > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > + odp_queue_t queue, > + void *user_ptr) > +{ > + /* We check this because ODP_QUEUE_INVALID is used */ > + /* to indicate a free timer */ > + if (odp_unlikely(queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("%s: Invalid queue identifier\n", tpid->name); > + abort(); > } > - > - timer->active = 1; > - odp_sync_stores(); > - > - timer_start(timer); > - > - return timer_hdl; > + odp_buffer_t tmo_buf = odp_buffer_alloc(tpid->buf_pool); > + if (odp_likely(tmo_buf != ODP_BUFFER_INVALID)) { > + odp_timer *tim = timer_alloc(tpid, queue, > user_ptr, tmo_buf); > + if (tim != ODP_TIMER_INVALID) { > + /* Success */ > + assert(tim->queue != ODP_QUEUE_INVALID); > + return tim; > + } > + odp_buffer_free(tmo_buf); > + } > + /* Else failed to allocate timeout event */ > + /* errno set by odp_buffer_alloc() or timer_alloc () */ > + return ODP_TIMER_INVALID; > } > > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer_hdl, > uint64_t tmo_tick, > - odp_queue_t queue, > odp_buffer_t buf) > +void odp_timer_free(odp_timer_t tim) > { > - int id; > - uint64_t tick; > - uint64_t cur_tick; > - timeout_t *new_tmo; > - odp_buffer_t tmo_buf; > - odp_timeout_hdr_t *tmo_hdr; > - timer_ring_t *timer; > - > - id = timer_hdl - 1; > - timer = &odp_timer.timer[id]; > - > - cur_tick = timer->cur_tick; > - if (tmo_tick <= cur_tick) { > - ODP_DBG("timeout too close\n"); > - return ODP_TIMER_TMO_INVALID; > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_free(tp, tim); > +} > > - tick = tmo_tick - cur_tick; > - if (tick > MAX_TICKS) { > - ODP_DBG("timeout too far\n"); > - return ODP_TIMER_TMO_INVALID; > +void odp_timer_set_abs_w_buf(odp_timer_t tim, > + odp_timer_tick_t abs_tck, > + odp_buffer_t user_buf) > +{ > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_reset_w_buf(tp, tim, abs_tck, user_buf); > +} > > - tick = (cur_tick + tick) % MAX_TICKS; > - > - tmo_buf = odp_buffer_alloc(timer->pool); > - if (tmo_buf == ODP_BUFFER_INVALID) { > - ODP_DBG("alloc failed\n"); > - return ODP_TIMER_TMO_INVALID; > +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck) > +{ > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > } > - > - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo_buf); > - new_tmo = &tmo_hdr->meta; > - > - new_tmo->timer_id = id; > - new_tmo->tick = (int)tick; > - new_tmo->tmo_tick = tmo_tick; > - new_tmo->queue = queue; > - new_tmo->tmo_buf = tmo_buf; > - > - if (buf != ODP_BUFFER_INVALID) > - new_tmo->buf = buf; > - else > - new_tmo->buf = tmo_buf; > - > - add_tmo(&timer->tick[tick], new_tmo); > - > - return tmo_buf; > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_reset(tp, tim, abs_tck); > } > > -uint64_t odp_timer_tick_to_ns(odp_timer_t timer_hdl, uint64_t ticks) > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return ticks * odp_timer.timer[id].resolution_ns; > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > + } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_reset(tp, tim, tp->tick + rel_tck); > } > > -uint64_t odp_timer_ns_to_tick(odp_timer_t timer_hdl, uint64_t ns) > +void odp_timer_cancel(odp_timer_t tim) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return ns / odp_timer.timer[id].resolution_ns; > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > + } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_cancel(tp, tim); > } > > -uint64_t odp_timer_resolution(odp_timer_t timer_hdl) > +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo_buf) > { > - uint32_t id; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + odp_timer *tim = tmo_hdr->timer; > + > + /* Make sure stores to '*tim' are visible */ > + odp_sync_stores(); > > - id = timer_hdl - 1; > - return odp_timer.timer[id].resolution_ns; > + /* Compare generation count (gc) of timeout and parent > timer (if any)*/ > + if (odp_unlikely(tim == ODP_TIMER_INVALID || > + tmo_hdr->gc != tim->gc)) { > + /* Generation counters differ => timeout is > orphaned */ > + return ODP_TMO_ORPHAN; > + } > + /* Else gen-cnts match => parent timer exists */ > + > + /* Return timeout to timer so that it can be delivered > again */ > + tim->tmo_buf = tmo_buf; > + /* FIXME do we need some kind of synchronisation or > locking here? */ > + > + /* Compare tags of timeout and parent timer */ > + /* Compare requested and actual timeout time */ > + if (odp_likely(tim->tag == tmo_hdr->tag && > + tim->req_tmo <= tmo_hdr->expiration)) { > + /* Tags match, actual timeout is after requested > => good! */ > + return ODP_TMO_FRESH; > + } else { > + /* Tags don't match or actual timeout time is > before */ > + /* requested */ > + /* Timer has been reset or cancelled and timeout > is stale */ > + /* or timeout expired too early */ > + if (tim->req_tmo != INVALID_PRIORITY) { > + /* Reset the timer for requested timeout */ > + odp_timer_set_abs(tim, tim->req_tmo); > + } > + /* Else timer was cancelled, do nothing */ > + return ODP_TMO_STALE; > + } > } > > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer_hdl) > +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo_buf) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return odp_timer.timer[id].max_ticks; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + odp_timer_t tim = tmo_hdr->timer; > + if (odp_likely(tim != ODP_TIMER_INVALID && tmo_hdr->gc == > tim->gc)) > + return tim; > + else > + return ODP_TIMER_INVALID; > } > > -uint64_t odp_timer_current_tick(odp_timer_t timer_hdl) > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo_buf) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return odp_timer.timer[id].cur_tick; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + return tmo_hdr->expiration; > } > > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf) > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo_buf) > { > - return (odp_timeout_t) buf; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + return tmo_hdr->user_ptr; > } > > -uint64_t odp_timeout_tick(odp_timeout_t tmo) > +int odp_timer_init_global(void) > { > - odp_timeout_hdr_t *tmo_hdr = odp_timeout_hdr(tmo); > - return tmo_hdr->meta.tmo_tick; > + return 0; > } > diff --git a/platform/linux-generic/priority_queue.c > b/platform/linux-generic/priority_queue.c > new file mode 100644 > index 0000000..ba4ba0e > --- /dev/null > +++ b/platform/linux-generic/priority_queue.c > @@ -0,0 +1,289 @@ > +#define NDEBUG /* Enabled by default by ODP build system */ > +#include <assert.h> > +#include <unistd.h> > +#include <stdlib.h> > +#include <string.h> > +#include <strings.h> > +#include <odp_hints.h> > +#include <odp_align.h> > +#include <odp_debug.h> > + > +#include "priority_queue.h" > + > + > +#define NUM_CHILDREN 4 > +#define CHILD(n) (NUM_CHILDREN * (n) + 1) > +#define PARENT(n) (((n) - 1) / NUM_CHILDREN) > + > +/* Internal nodes in the array */ > +typedef struct heap_node { > + pq_element *elem; > + /* Copy of elem->prio so we avoid unnecessary dereferencing */ > + pq_priority_t prio; > +} heap_node; > + > +static void pq_assert_heap(priority_queue *this); > + > +#define ALIGNMENT(p) (1U << ((unsigned)ffs((int)p) - 1U)) > + > +void priority_queue_con(priority_queue *this, uint32_t _max_elems) > +{ > + this->max_elems = _max_elems; > + this->reg_elems = 0; > + this->num_elems = 0; > + this->org_ptr = malloc((_max_elems + 64 / sizeof(heap_node)) * > + sizeof(heap_node)); > + if (odp_unlikely(this->org_ptr == NULL)) { > + ODP_ERR("malloc failed\n"); > + abort(); > + } > + this->heap = this->org_ptr; > + assert((size_t)&this->heap[1] % 8 == 0); > + /* Increment base address until first child (index 1) is > cache line */ > + /* aligned and thus all children (e.g. index 1-4) stored > in the */ > + /* same cache line. We are not interested in the alignment > of */ > + /* heap[0] as this is a lone node */ > + while ((size_t)&this->heap[1] % ODP_CACHE_LINE_SIZE != 0) { > + /* Cast to ptr to struct member with the greatest > alignment */ > + /* requirement */ > + this->heap = (heap_node *)((pq_priority_t > *)this->heap + 1); > + } > +#if 0 > + printf("Alignment of heap[1]=%u\n", > ALIGNMENT((size_t)&heap[1])); > + printf("Alignment of heap[CHILD(1)]=%u\n", > + ALIGNMENT((size_t)&heap[CHILD(1)])); > +#endif > + pq_assert_heap(this); > +} > + > +void priority_queue_des(priority_queue *this) > +{ > + pq_assert_heap(this); > + free(this->org_ptr); > +} > + > +#ifndef NDEBUG > +static uint32_t > +pq_assert_elem(priority_queue *this, uint32_t index, bool recurse) > +{ > + uint32_t num = 1; > + const pq_element *elem = this->heap[index].elem; > + assert(elem->index == index); > + assert(elem->prio == this->heap[index].prio); > + uint32_t child = CHILD(index); > + uint32_t i; > + for (i = 0; i < NUM_CHILDREN; i++, child++) { > + if (valid_index(this, child)) { > + assert(this->heap[child].elem != NULL); > + assert(this->heap[child].prio >= elem->prio); > + if (recurse) > + num += pq_assert_elem(this, child, > recurse); > + } > + } > + return num; > +} > +#endif > + > +static void > +pq_assert_heap(priority_queue *this) > +{ > + (void)this; > +#ifndef NDEBUG > + uint32_t num = 0; > + if (odp_likely(this->num_elems != 0)) { > + assert(this->heap[0].elem != NULL); > + num += pq_assert_elem(this, 0, true); > + } > + assert(num == this->num_elems); > + unsigned i; > + for (i = 0; i < this->num_elems; i++) { > + assert(this->heap[i].elem != NULL); > + assert(this->heap[i].prio != INVALID_PRIORITY); > + } > +#endif > +} > + > +/* Bubble up to proper position */ > +void > +pq_bubble_up(priority_queue *this, pq_element *elem) > +{ > + assert(this->heap[elem->index].elem == elem); > + assert(this->heap[elem->index].prio == elem->prio); > + uint32_t current = elem->index; > + pq_priority_t prio = elem->prio; > + assert(current == 0 || this->heap[PARENT(current)].elem != > NULL); > + /* Move up into proper position */ > + while (current != 0 && this->heap[PARENT(current)].prio > > prio) { > + uint32_t parent = PARENT(current); > + assert(this->heap[parent].elem != NULL); > + /* Swap current with parent */ > + /* 1) Move parent down */ > + this->heap[current].elem = this->heap[parent].elem; > + this->heap[current].prio = this->heap[parent].prio; > + this->heap[current].elem->index = current; > + /* 2) Move current up to parent */ > + this->heap[parent].elem = elem; > + this->heap[parent].prio = prio; > + this->heap[parent].elem->index = parent; > + /* Continue moving elem until it is in the right > place */ > + current = parent; > + } > + pq_assert_heap(this); > +} > + > +/* Find the smallest child that is smaller than the specified > priority */ > +/* TODO very hot function! */ > +uint32_t pq_smallest_child(priority_queue *this, > + uint32_t index, > + pq_priority_t val) > +{ > + uint32_t smallest = index; > + uint32_t child = CHILD(index); > +#if 1 > + /* Unroll loop when all children exist */ > + if (odp_likely(valid_index(this, child + 3))) { > + if (this->heap[child + 0].prio < val) > + val = this->heap[smallest = child + 0].prio; > + if (this->heap[child + 1].prio < val) > + val = this->heap[smallest = child + 1].prio; > + if (this->heap[child + 2].prio < val) > + val = this->heap[smallest = child + 2].prio; > + if (this->heap[child + 3].prio < val) > + val = this->heap[smallest = child + 3].prio; > + return smallest; > + } > +#endif > + uint32_t i; > + for (i = 0; i < NUM_CHILDREN; i++) { > + if (odp_unlikely(!valid_index(this, child + i))) > + break; > + if (this->heap[child + i].prio < val) { > + smallest = child + i; > + val = this->heap[smallest].prio; > + } > + } > + return smallest; > +} > + > +/* TODO very hot function, can it be optimised? */ > +void > +pq_bubble_down(priority_queue *this, pq_element *elem) > +{ > + assert(this->heap[elem->index].elem == elem); > + assert(this->heap[elem->index].prio == elem->prio); > + uint32_t current = elem->index; > + pq_priority_t prio = elem->prio; > + for (;;) { > + uint32_t child = pq_smallest_child(this, current, > prio); > + if (current == child) { > + /* No smaller child, we are done */ > + pq_assert_heap(this); > + return; > + } > + /* Element larger than smaller child, must move > down */ > + assert(this->heap[child].elem != NULL); > + /* 1) Move child up to current */ > + this->heap[current].elem = this->heap[child].elem; > + this->heap[current].prio = this->heap[child].prio; > + /* 2) Move current down to child */ > + this->heap[child].elem = elem; > + this->heap[child].prio = prio; > + this->heap[child].elem->index = child; > + > + this->heap[current].elem->index = current; /* > cache misses! */ > + /* Continue moving element until it is in the > right place */ > + current = child; > + } > +} > + > +bool > +pq_register_element(priority_queue *this, pq_element *elem) > +{ > + if (odp_likely(this->reg_elems < this->max_elems)) { > + elem->pq = this; > + this->reg_elems++; > + return true; > + } > + return false; > +} > + > +void > +pq_unregister_element(priority_queue *this, pq_element *elem) > +{ > + assert(elem->pq == this); > + if (is_active(elem)) > + pq_deactivate_element(this, elem); > + elem->pq = NULL; > + this->reg_elems--; > +} > + > +void > +pq_activate_element(priority_queue *this, pq_element *elem, > pq_priority_t prio) > +{ > + assert(elem->pq == this); > + /* Insert element at end */ > + uint32_t index = this->num_elems++; > + this->heap[index].elem = elem; > + this->heap[index].prio = prio; > + elem->index = index; > + elem->prio = prio; > + pq_bubble_up(this, elem); > +} > + > +void > +pq_deactivate_element(priority_queue *this, pq_element *elem) > +{ > + assert(elem->pq == this); > + if (odp_likely(is_active(elem))) { > + /* Swap element with last element */ > + uint32_t current = elem->index; > + uint32_t last = --this->num_elems; > + if (odp_likely(last != current)) { > + /* Move last element to current */ > + this->heap[current].elem = > this->heap[last].elem; > + this->heap[current].prio = > this->heap[last].prio; > + this->heap[current].elem->index = current; > + /* Bubble down old 'last' element to its > proper place*/ > + if (this->heap[current].prio < elem->prio) > + pq_bubble_up(this, > this->heap[current].elem); > + else > + pq_bubble_down(this, > this->heap[current].elem); > + } > + elem->index = INVALID_INDEX; > + pq_assert_heap(this); > + } > +} > + > +void > +pq_reset_element(priority_queue *this, pq_element *elem, > pq_priority_t prio) > +{ > + assert(prio != INVALID_PRIORITY); > + if (odp_likely(is_active(elem))) { > + assert(prio >= elem->prio); > + elem->prio = prio; > + this->heap[elem->index].prio = prio;/* cache > misses here! */ > + pq_bubble_down(this, elem); > + pq_assert_heap(this); > + } else { > + pq_activate_element(this, elem, prio); > + } > +} > + > +pq_priority_t pq_first_priority(const priority_queue *this) > +{ > + return this->num_elems != 0 ? this->heap[0].prio : > INVALID_PRIORITY; > +} > + > +pq_element * > +pq_release_element(priority_queue *this, pq_priority_t threshold) > +{ > + if (odp_likely(this->num_elems != 0 && > + this->heap[0].prio <= threshold)) { > + pq_element *elem = this->heap[0].elem; > + /* Remove element from heap */ > + pq_deactivate_element(this, elem); > + assert(elem->prio <= threshold); > + return elem; > + } > + return NULL; > +} > diff --git a/platform/linux-generic/priority_queue.h > b/platform/linux-generic/priority_queue.h > new file mode 100644 > index 0000000..c461590 > --- /dev/null > +++ b/platform/linux-generic/priority_queue.h > @@ -0,0 +1,107 @@ > +#ifndef _PRIORITY_QUEUE_H > +#define _PRIORITY_QUEUE_H > + > +#include <assert.h> > +#include <stddef.h> > +#include <stdint.h> > +#include <stdbool.h> > + > +#define INVALID_INDEX ~0U > +#define INVALID_PRIORITY ((pq_priority_t)~0ULL) > + > +typedef uint64_t pq_priority_t; > + > +struct heap_node; > + > +typedef struct priority_queue { > + uint32_t max_elems;/* Number of elements in heap */ > + /* Number of registered elements (active + inactive) */ > + uint32_t reg_elems; > + uint32_t num_elems;/* Number of active elements */ > + struct heap_node *heap; > + struct heap_node *org_ptr; > +} priority_queue; > + > +/* The user gets a pointer to this structure */ > +typedef struct { > + /* Set when pq_element registered with priority queue */ > + priority_queue *pq; > + uint32_t index;/* Index into heap array */ > + pq_priority_t prio; > +} pq_element; > + > +/*** Operations on pq_element ***/ > + > +static inline void pq_element_con(pq_element *this) > +{ > + this->pq = NULL; > + this->index = INVALID_INDEX; > + this->prio = 0U; > +} > + > +static inline void pq_element_des(pq_element *this) > +{ > + (void)this; > + assert(this->index == INVALID_INDEX); > +} > + > +static inline priority_queue *get_pq(const pq_element *this) > +{ > + return this->pq; > +} > + > +static inline pq_priority_t get_prio(const pq_element *this) > +{ > + return this->prio; > +} > + > +static inline uint32_t get_index(const pq_element *this) > +{ > + return this->index; > +} > + > +static inline bool is_active(const pq_element *this) > +{ > + return this->index != INVALID_INDEX; > +} > + > +/*** Operations on priority_queue ***/ > + > +extern uint32_t pq_smallest_child(priority_queue *, uint32_t, > pq_priority_t); > +extern void pq_bubble_down(priority_queue *, pq_element *); > +extern void pq_bubble_up(priority_queue *, pq_element *); > + > +static inline bool valid_index(priority_queue *this, uint32_t idx) > +{ > + return idx < this->num_elems; > +} > + > +extern void priority_queue_con(priority_queue *, uint32_t > _max_elems); > +extern void priority_queue_des(priority_queue *); > + > +/* Register pq_element with priority queue */ > +/* Return false if priority queue full */ > +extern bool pq_register_element(priority_queue *, pq_element *); > + > +/* Activate and add pq_element to priority queue */ > +/* Element must be disarmed */ > +extern void pq_activate_element(priority_queue *, pq_element *, > pq_priority_t); > + > +/* Reset (increase) priority for pq_element */ > +/* Element may be active or inactive (released) */ > +extern void pq_reset_element(priority_queue *, pq_element *, > pq_priority_t); > + > +/* Deactivate and remove element from priority queue */ > +/* Element may be active or inactive (released) */ > +extern void pq_deactivate_element(priority_queue *, pq_element *); > + > +/* Unregister pq_element */ > +extern void pq_unregister_element(priority_queue *, pq_element *); > + > +/* Return priority of first element (lowest numerical value) */ > +extern pq_priority_t pq_first_priority(const priority_queue *); > + > +/* Deactivate and return first element if it's prio is <= > threshold */ > +extern pq_element *pq_release_element(priority_queue *, > pq_priority_t thresh); > + > +#endif /* _PRIORITY_QUEUE_H */ > diff --git a/test/api_test/odp_timer_ping.c > b/test/api_test/odp_timer_ping.c > index c1cc255..c4332e3 100644 > --- a/test/api_test/odp_timer_ping.c > +++ b/test/api_test/odp_timer_ping.c > @@ -20,6 +20,7 @@ > * Otherwise timeout may happen bcz of slow nw speed > */ > > +#include <stdlib.h> > #include <unistd.h> > #include <fcntl.h> > #include <errno.h> > @@ -43,7 +44,8 @@ > #define PING_CNT 10 > #define PING_THRD 2 /* Send and Rx Ping thread */ > > -static odp_timer_t test_timer_ping; > +static odp_timer_pool_t tp; > +static odp_timer_t test_timer_ping = ODP_TIMER_INVALID; > static odp_timer_tmo_t test_ping_tmo; > > #define PKTSIZE 64 > @@ -123,15 +125,7 @@ static int listen_to_pingack(void) > (socklen_t *)&len); > if (bytes > 0) { > /* pkt rxvd therefore cancel the > timeout */ > - if > (odp_timer_cancel_tmo(test_timer_ping, > - test_ping_tmo) != 0) { > - ODP_ERR("cancel_tmo failed > ..exiting listner thread\n"); > - /* avoid exiting from here > even if tmo > - * failed for current ping, > - * allow subsequent > ping_rx request */ > - err = -1; > - > - } > + odp_timer_cancel(test_timer_ping); > /* cruel bad hack used for sender, > listner ipc.. > * euwww.. FIXME .. > */ > @@ -153,7 +147,7 @@ static int send_ping_request(struct > sockaddr_in *addr) > int sd, cnt = 1; > struct packet pckt; > > - uint64_t tick; > + odp_timer_tick_t tick; > odp_queue_t queue; > odp_buffer_t buf; > > @@ -179,6 +173,12 @@ static int send_ping_request(struct > sockaddr_in *addr) > > /* get the ping queue */ > queue = odp_queue_lookup("ping_timer_queue"); > + test_timer_ping = odp_timer_alloc(tp, queue, NULL); > + if (test_timer_ping == ODP_TIMER_INVALID) { > + ODP_ERR("Failed to allocate timer.\n"); > + err = -1; > + goto err; > + } > > for (i = 0; i < PING_CNT; i++) { > /* prepare icmp pkt */ > @@ -204,12 +204,10 @@ static int send_ping_request(struct > sockaddr_in *addr) > printf(" icmp_sent msg_cnt %d\n", i); > > /* arm the timer */ > - tick = odp_timer_current_tick(test_timer_ping); > + tick = odp_timer_current_tick(tp); > > tick += 1000; > - test_ping_tmo = > odp_timer_absolute_tmo(test_timer_ping, tick, > - queue, > - ODP_BUFFER_INVALID); > + odp_timer_set_abs(test_timer_ping, tick); > /* wait for timeout event */ > while ((buf = odp_queue_deq(queue)) == > ODP_BUFFER_INVALID) { > /* flag true means ack rxvd.. a cruel hack > as I > @@ -225,16 +223,24 @@ static int send_ping_request(struct > sockaddr_in *addr) > } > } > > - /* free tmo_buf for timeout case */ > - if (buf != ODP_BUFFER_INVALID) { > + switch (odp_timer_tmo_status(buf)) { > + case ODP_TMO_FRESH: > ODP_DBG(" timeout msg_cnt [%i] \n", i); > /* so to avoid seg fault commented */ > - odp_buffer_free(buf); > err = -1; > + break; > + case ODP_TMO_STALE: > + /* Ignore stale timeouts */ > + break; > + case ODP_TMO_ORPHAN: > + ODP_ERR("Received orphaned timeout!\n"); > + abort(); > } > } > > err: > + if (test_timer_ping != ODP_TIMER_INVALID) > + odp_timer_free(test_timer_ping); > return err; > } > > @@ -335,7 +341,7 @@ int main(int argc ODP_UNUSED, char *argv[] > ODP_UNUSED) > ODP_CACHE_LINE_SIZE, > ODP_BUFFER_TYPE_RAW); > if (pool == ODP_BUFFER_POOL_INVALID) { > - ODP_ERR("Pool create failed.\n"); > + ODP_ERR("Buffer pool create failed.\n"); > return -1; > } > > @@ -350,8 +356,18 @@ int main(int argc ODP_UNUSED, char *argv[] > ODP_UNUSED) > return -1; > } > > - test_timer_ping = odp_timer_create("ping_timer", pool, > - 1000000, 1000000, > 1000000000000UL); > + /* > + * Create timer pool > + */ > + tp = odp_timer_pool_create("timer_pool", pool, > + 1000000U, /* 1 millisecond */ > + 10000000000U, /* 10 seconds */ > + 1, false, ODP_CLOCK_DEFAULT); > + if (tp == ODP_TIMER_POOL_INVALID) { > + ODP_ERR("Timer pool create failed.\n"); > + return -1; > + } > + > odp_shm_print_all(); > > pingarg.thrdarg.testcase = ODP_TIMER_PING_TEST; > -- > 1.9.1 > > > > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp
On 2014-09-04 01:01, Maxim Uvarov wrote: > On 09/03/2014 10:48 PM, Ola Liljedahl wrote: > >Any volunteers for reviewing this patch? > > > >-- Ola > > > > > Hello Ola, > > it will be really useful if it's possible to split this big patch. > At least for: > 1. timer api + test example > 2. priority queue > > priority queue also need some test case. We should probably focus on writing tests for the public API before we start write tests for the internal implementation fuctions. Cheers, Anders > > Also we talked about your patch with Mike and Anders yesterday. You > provided example in doxygen. But it will > be great to move that example to source code. In that case we can > check that it always valid and works. > > A little bit confusing with C++ style comments "//" instead of C > style "/* .... */". But patchwork does not complain about > that. I think we should be consistent with comments too. > > Best regards, > Maxim. > > > > >On 2 September 2014 17:26, Ola Liljedahl <ola.liljedahl@linaro.org > ><mailto:ola.liljedahl@linaro.org>> wrote: > > > > Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org > > <mailto:ola.liljedahl@linaro.org>> > > --- > > (This document/code contribution attached is provided under the > > terms of agreement LES-LTM-21309) > > New timer API and corresponding SW implementation for linux-generic. > > Read more about use cases and usage here: > > https://docs.google.com/a/linaro.org/document/d/1bfY_J8ecLJPsFTmYftb0NVmGnB9qkEc_NpcJ87yfaD8/edit# > > > > example/timer/odp_timer_test.c | 93 ++- > > platform/linux-generic/Makefile.am | 1 + > > platform/linux-generic/include/api/odp_timer.h | 478 ++++++++++-- > > .../linux-generic/include/odp_timer_internal.h | 71 +- > > platform/linux-generic/odp_timer.c | 823 > > +++++++++++++-------- > > platform/linux-generic/priority_queue.c | 289 ++++++++ > > platform/linux-generic/priority_queue.h | 107 +++ > > test/api_test/odp_timer_ping.c | 58 +- > > 8 files changed, 1455 insertions(+), 465 deletions(-) > > create mode 100644 platform/linux-generic/priority_queue.c > > create mode 100644 platform/linux-generic/priority_queue.h > > > > diff --git a/example/timer/odp_timer_test.c > > b/example/timer/odp_timer_test.c > > index bf1d7df..0b4fedf 100644 > > --- a/example/timer/odp_timer_test.c > > +++ b/example/timer/odp_timer_test.c > > @@ -35,7 +35,6 @@ > > typedef struct { > > int core_count; /**< Core count*/ > > int resolution_us; /**< Timeout resolution in usec*/ > > - int min_us; /**< Minimum timeout in usec*/ > > int max_us; /**< Maximum timeout in usec*/ > > int period_us; /**< Timeout period in usec*/ > > int tmo_count; /**< Timeout count*/ > > @@ -45,18 +44,16 @@ typedef struct { > > /** @private Barrier for test synchronisation */ > > static odp_barrier_t test_barrier; > > > > -/** @private Timer handle*/ > > -static odp_timer_t test_timer; > > +/** @private Timer pool handle*/ > > +static odp_timer_pool_t tp; > > > > > > /** @private test timeout */ > > static void test_abs_timeouts(int thr, test_args_t *args) > > { > > - uint64_t tick; > > - uint64_t period; > > + odp_timer_tick_t period; > > uint64_t period_ns; > > odp_queue_t queue; > > - odp_buffer_t buf; > > int num; > > > > ODP_DBG(" [%i] test_timeouts\n", thr); > > @@ -64,37 +61,51 @@ static void test_abs_timeouts(int thr, > > test_args_t *args) > > queue = odp_queue_lookup("timer_queue"); > > > > period_ns = args->period_us*USEC; > > - period = odp_timer_ns_to_tick(test_timer, period_ns); > > + period = odp_timer_ns_to_tick(tp, period_ns); > > > > ODP_DBG(" [%i] period %"PRIu64" ticks, %"PRIu64" ns\n", thr, > > period, period_ns); > > > > - tick = odp_timer_current_tick(test_timer); > > + ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, > > + odp_timer_current_tick(tp)); > > > > - ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick); > > - > > - tick += period; > > - > > - if (odp_timer_absolute_tmo(test_timer, tick, queue, > > ODP_BUFFER_INVALID) > > - == ODP_TIMER_TMO_INVALID){ > > - ODP_DBG("Timeout request failed\n"); > > + odp_timer_t test_timer; > > + test_timer = odp_timer_alloc(tp, queue, NULL); > > + if (test_timer == ODP_TIMER_INVALID) { > > + ODP_ERR("Failed to allocate timer\n"); > > return; > > } > > + odp_timer_set_rel(test_timer, period); > > > > num = args->tmo_count; > > > > while (1) { > > - odp_timeout_t tmo; > > - > > - buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); > > + /* Local variables because received timeouts may not > > + originate from timer we created above */ > > + odp_timer_tmo_t tmo; > > + odp_timer_tick_t tick; > > + odp_timer_t hdl; > > + > > + /* Get the next ready buffer/timeout */ > > + tmo = odp_schedule_one(&queue, ODP_SCHED_WAIT); > > + switch (odp_timer_tmo_status(tmo)) { > > + case ODP_TMO_FRESH: > > + break; > > + case ODP_TMO_STALE: > > + ODP_DBG("[%i] Stale timeout received\n", thr); > > + break; > > + case ODP_TMO_ORPHAN: > > + ODP_DBG("[%i] Orphaned timeout received\n", > > + thr); > > + odp_buffer_free(tmo); > > + continue; > > + } > > > > - tmo = odp_timeout_from_buffer(buf); > > - tick = odp_timeout_tick(tmo); > > + tick = odp_timer_get_expiry(tmo); > > + hdl = odp_timer_get_handle(tmo); > > > > ODP_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, > > tick); > > > > - odp_buffer_free(buf); > > - > > num--; > > > > if (num == 0) > > @@ -102,10 +113,13 @@ static void test_abs_timeouts(int thr, > > test_args_t *args) > > > > tick += period; > > > > - odp_timer_absolute_tmo(test_timer, tick, > > - queue, ODP_BUFFER_INVALID); > > + if (hdl != ODP_TIMER_INVALID) > > + odp_timer_set_abs(hdl, tick); > > } > > > > + odp_timer_cancel(test_timer); > > + odp_timer_free(test_timer); > > + > > if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) > > odp_schedule_release_atomic(); > > } > > @@ -159,7 +173,6 @@ static void print_usage(void) > > printf("Options:\n"); > > printf(" -c, --count <number> core count, core IDs > > start from 1\n"); > > printf(" -r, --resolution <us> timeout resolution in > > usec\n"); > > - printf(" -m, --min <us> minimum timeout in usec\n"); > > printf(" -x, --max <us> maximum timeout in usec\n"); > > printf(" -p, --period <us> timeout period in usec\n"); > > printf(" -t, --timeouts <count> timeout repeat count\n"); > > @@ -183,7 +196,6 @@ static void parse_args(int argc, char *argv[], > > test_args_t *args) > > static struct option longopts[] = { > > {"count", required_argument, NULL, 'c'}, > > {"resolution", required_argument, NULL, 'r'}, > > - {"min", required_argument, NULL, 'm'}, > > {"max", required_argument, NULL, 'x'}, > > {"period", required_argument, NULL, 'p'}, > > {"timeouts", required_argument, NULL, 't'}, > > @@ -194,14 +206,13 @@ static void parse_args(int argc, char > > *argv[], test_args_t *args) > > /* defaults */ > > args->core_count = 0; /* all cores */ > > args->resolution_us = 10000; > > - args->min_us = args->resolution_us; > > args->max_us = 100000000; > > args->period_us = 1000000; > > args->tmo_count = 30; > > > > while (1) { > > opt = getopt_long(argc, argv, "+c:r:m:x:p:t:h", > > - longopts, &long_index); > > + longopts, &long_index); > > > > if (opt == -1) > > break; /* No more options */ > > @@ -213,9 +224,6 @@ static void parse_args(int argc, char *argv[], > > test_args_t *args) > > case 'r': > > args->resolution_us = atoi(optarg); > > break; > > - case 'm': > > - args->min_us = atoi(optarg); > > - break; > > case 'x': > > args->max_us = atoi(optarg); > > break; > > @@ -299,7 +307,6 @@ int main(int argc, char *argv[]) > > > > printf("first core: %i\n", first_core); > > printf("resolution: %i usec\n", args.resolution_us); > > - printf("min timeout: %i usec\n", args.min_us); > > printf("max timeout: %i usec\n", args.max_us); > > printf("period: %i usec\n", args.period_us); > > printf("timeouts: %i\n", args.tmo_count); > > @@ -323,10 +330,23 @@ int main(int argc, char *argv[]) > > ODP_BUFFER_TYPE_TIMEOUT); > > > > if (pool == ODP_BUFFER_POOL_INVALID) { > > - ODP_ERR("Pool create failed.\n"); > > + ODP_ERR("Buffer pool create failed.\n"); > > return -1; > > } > > > > + tp = odp_timer_pool_create("timer_pool", pool, > > + args.resolution_us*USEC, > > + args.max_us*USEC, > > + num_workers, /* One timer per > > worker */ > > + true, > > + ODP_CLOCK_DEFAULT); > > + if (tp == ODP_TIMER_POOL_INVALID) { > > + ODP_ERR("Timer pool create failed.\n"); > > + return -1; > > + } > > + > > + odp_shm_print_all(); > > + > > /* > > * Create a queue for timer test > > */ > > @@ -342,13 +362,6 @@ int main(int argc, char *argv[]) > > return -1; > > } > > > > - test_timer = odp_timer_create("test_timer", pool, > > - args.resolution_us*USEC, > > - args.min_us*USEC, > > - args.max_us*USEC); > > - > > - odp_shm_print_all(); > > - > > printf("CPU freq %"PRIu64" hz\n", odp_sys_cpu_hz()); > > printf("Cycles vs nanoseconds:\n"); > > ns = 0; > > diff --git a/platform/linux-generic/Makefile.am > > b/platform/linux-generic/Makefile.am > > index f4dfdc1..e2bc1a7 100644 > > --- a/platform/linux-generic/Makefile.am > > +++ b/platform/linux-generic/Makefile.am > > @@ -72,4 +72,5 @@ __LIB__libodp_la_SOURCES = \ > > odp_thread.c \ > > odp_ticketlock.c \ > > odp_time.c \ > > + priority_queue.c \ > > odp_timer.c > > diff --git a/platform/linux-generic/include/api/odp_timer.h > > b/platform/linux-generic/include/api/odp_timer.h > > index 01db839..e5f961c 100644 > > --- a/platform/linux-generic/include/api/odp_timer.h > > +++ b/platform/linux-generic/include/api/odp_timer.h > > @@ -1,4 +1,4 @@ > > -/* Copyright (c) 2013, Linaro Limited > > +/* Copyright (c) 2014, Linaro Limited > > * All rights reserved. > > * > > * SPDX-License-Identifier: BSD-3-Clause > > @@ -8,7 +8,175 @@ > > /** > > * @file > > * > > - * ODP timer > > + * ODP timer service > > + * > > + > > +//Example #1 Retransmission timer (e.g. for reliable connections) > > + > > +//Create timer pool for reliable connections > > +#define SEC 1000000000ULL //1s expressed in nanoseconds > > +odp_timer_pool_t tcp_tpid = > > + odp_timer_pool_create("TCP", > > + buffer_pool, > > + 1000000,//resolution 1ms > > + 7200 * SEC,//max tmo length 2hours > > + 40000,//num_timers > > + true,//shared > > + ODP_CLOCK_DEFAULT > > + ); > > +if (tcp_tpid == ODP_TIMER_POOL_INVALID) > > +{ > > + //Failed to create timer pool => fatal error > > +} > > + > > + > > +//Setting up a new connection > > +//Allocate retransmission timeout (identical for supervision timeout) > > +//The user pointer points back to the connection context > > +conn->ret_tim = odp_timer_alloc(tcp_tpid, queue, conn); > > +//Check if all resources were successfully allocated > > +if (conn->ret_tim == ODP_TIMER_INVALID) > > +{ > > + //Failed to allocate all resources for connection => tear down > > + //Destroy timeout > > + odp_timer_free(conn->ret_tim); > > + //Tear down connection > > + ... > > + return false; > > +} > > +//All necessary resources successfully allocated > > +//Compute initial retransmission length in timer ticks > > +conn->ret_len = odp_timer_ns_to_tick(tcp_tpid, 3 * SEC);//Per RFC1122 > > +//Arm the timer > > +odp_timer_set_rel(conn->ret_tim, conn->ret_len); > > +return true; > > + > > + > > +//A packet for the connection has just been transmitted > > +//Reset the retransmission timer > > +odp_timer_set_rel(conn->ret_tim, conn->ret_len); > > + > > + > > +//A retransmission timeout for the connection has been received > > +//Check if timeout is fresh or stale, for stale timeouts we need > > to reset the > > +//timer > > +switch (odp_timer_tmo_status(tmo)) > > +{ > > + case ODP_TMO_FRESH : > > + //Fresh timeout, last transmitted packet not acked in time => > > + retransmit > > + //Get connection from timeout event > > + conn = odp_timer_get_userptr(tmo); > > + //Retransmit last packet (e.g. TCP segment) > > + ... > > + //Re-arm timer using original delta value > > + odp_timer_set_rel(conn->ret_tim, conn->ret_len); > > + break; > > + case ODP_TMO_STALE : > > + break;//Do nothing > > + case ODP_TMO_ORPHAN : > > + odp_free_buffer(tmo); > > + return;//Get out of here > > +} > > + > > + > > +//Example #2 Periodic tick > > + > > +//Create timer pool for periodic ticks > > +odp_timer_pool_t per_tpid = > > + odp_timer_pool_create("periodic-tick", > > + buffer_pool, > > + 1,//resolution 1ns > > + 1000000000,//maximum timeout length 1s > > + 10,//num_timers > > + false,//not shared > > + ODP_CLOCK_DEFAULT > > + ); > > +if (per_tpid == ODP_TIMER_POOL_INVALID) > > +{ > > + //Failed to create timer pool => fatal error > > +} > > + > > + > > +//Allocate periodic timer > > +tim_1733 = odp_timer_alloc(per_tpid, queue, NULL); > > +//Check if all resources were successfully allocated > > +if (tim_1733 == ODP_TIMER_INVALID) > > +{ > > + //Failed to allocate all resources => tear down > > + //Destroy timeout > > + odp_timer_free(tim_1733); > > + //Tear down other state > > + ... > > + return false; > > +} > > +//All necessary resources successfully allocated > > +//Compute tick period in timer ticks > > +period_1733 = odp_timer_ns_to_tick(per_tpid, 1000000000U / > > 1733U);//1733Hz > > +//Compute when next tick should expire > > +next_1733 = odp_timer_current_tick(per_tpid) + period_1733; > > +//Arm the periodic timer > > +odp_timer_set_abs(tim_1733, next_1733); > > +return true; > > + > > + > > + > > +//A periodic timer timeout has been received > > +//Must call odp_timer_tmo_status() on timeout! > > +ret = odp_timer_tmo_status(tmo); > > +//We expect the timeout is fresh since we are not calling set or > > cancel on > > +//active or expired timers in this example > > +assert(ret == ODP_TMO_FRESH); > > +//Do processing driven by timeout *before* > > +... > > +do { > > + //Compute when the timer should expire next > > + next_1733 += period_1733; > > + //Check that this is in the future > > + if (likely(next_1733 > odp_timer_current_tick(per_tpid)) > > + break;//Yes, done > > + //Else we missed a timeout > > + //Optionally attempt some recovery and/or logging of the > > problem > > + ... > > +} while (0); > > +//Re-arm periodic timer > > +odp_timer_set_abs(tim_1733, next_1733); > > +//Or do processing driven by timeout *after* > > +... > > +return; > > + > > +//Example #3 Tear down of flow > > +//ctx points to flow context data structure owned by application > > +//Free the timer, cancelling any timeout > > +odp_timer_free(ctx->timer);//Any enqueued timeout will be made > > invalid > > +//Continue tearing down and eventually freeing context > > +... > > +return; > > + > > +//A timeout has been received, check status > > +switch (odp_timer_tmo_status(tmo)) > > +{ > > + case ODP_TMO_FRESH : > > + //A flow has timed out, tear it down > > + //Find flow context from timeout > > + ctx = (context *)odp_timer_get_userptr(tmo); > > + //Free the supervision timer, any enqueued timeout will remain > > + odp_timer_free(ctx->tim); > > + //Free other flow related resources > > + ... > > + //Flow torn down > > + break; > > + case ODP_TMO_STALE : > > + //A stale timeout was received, timer automatically reset > > + break; > > + case ODP_TMO_ORPHAN : > > + //Orphaned timeout (from previously torn down flow) > > + //No corresponding timer or flow context > > + //Free the timeout > > + odp_buffer_free(tmo); > > + break; > > +} > > + > > */ > > > > #ifndef ODP_TIMER_H_ > > @@ -23,139 +191,325 @@ extern "C" { > > #include <odp_buffer_pool.h> > > #include <odp_queue.h> > > > > +/** > > +* ODP timer pool handle (platform dependent) > > +*/ > > +struct odp_timer_pool; > > +typedef struct odp_timer_pool *odp_timer_pool_t; > > > > /** > > - * ODP timer handle > > + * Invalid timer pool handle (platform dependent) > > */ > > -typedef uint32_t odp_timer_t; > > +#define ODP_TIMER_POOL_INVALID NULL > > > > -/** Invalid timer */ > > -#define ODP_TIMER_INVALID 0 > > +typedef enum odp_timer_pool_clock_source_e { > > + ODP_CLOCK_DEFAULT = 0, > > + /* Platform dependent which clock sources exist beyond > > + ODP_CLOCK_DEFAULT */ > > + ODP_CLOCK_NONE = 1 > > +} odp_timer_pool_clock_source_t; > > > > +/** > > +* ODP timer handle (platform dependent) > > +*/ > > +struct odp_timer; > > +typedef struct odp_timer *odp_timer_t; > > > > /** > > - * ODP timeout handle > > + * Invalid timer handle (platform dependent) > > */ > > -typedef odp_buffer_t odp_timer_tmo_t; > > - > > -/** Invalid timeout */ > > -#define ODP_TIMER_TMO_INVALID 0 > > +#define ODP_TIMER_INVALID NULL > > > > +/** > > + * ODP timeout event handle > > + */ > > +typedef odp_buffer_t odp_timer_tmo_t; > > > > /** > > - * Timeout notification > > + * ODP timeout status > > */ > > -typedef odp_buffer_t odp_timeout_t; > > +typedef enum odp_timer_tmo_status_e { > > + ODP_TMO_FRESH, /* Timeout is fresh, process it */ > > + ODP_TMO_STALE, /* Timer reset or cancelled, do nothing */ > > + ODP_TMO_ORPHAN,/* Timer deleted, free timeout */ > > +} odp_timer_tmo_status_t; > > + > > +/** > > +* ODP tick value > > +*/ > > +typedef uint64_t odp_timer_tick_t; > > > > > > /** > > - * Create a timer > > + * Create a timer pool > > * > > - * Creates a new timer with requested properties. > > + * Create a new timer pool. > > + * odp_timer_pool_create() is typically called once or a couple > > of times during > > + * application initialisation. > > * > > * @param name Name > > - * @param pool Buffer pool for allocating timeout notifications > > + * @param buf_pool Buffer pool for allocating timers > > * @param resolution Timeout resolution in nanoseconds > > - * @param min_tmo Minimum timeout duration in nanoseconds > > - * @param max_tmo Maximum timeout duration in nanoseconds > > + * @param max_tmo Maximum relative timeout in nanoseconds > > + * @param num_timers Number of supported timers (minimum) > > + * @param shared Shared or private timer pool. > > + * Operations on shared timers will include the > > necessary > > + * mutual exclusion, operations on private timers > > may not > > + * (mutual exclusion is the responsibility of the > > caller). > > + * @param clk_src Clock source to use > > + * > > + * @return Timer pool handle if successful, otherwise > > ODP_TIMER_POOL_INVALID > > + * and errno set > > + */ > > +odp_timer_pool_t > > +odp_timer_pool_create(const char *name, > > + odp_buffer_pool_t buf_pool, > > + uint64_t resolution, > > + uint64_t max_tmo, > > + uint32_t num_timers, > > + bool shared, > > + odp_timer_pool_clock_source_t clk_src); > > + > > +/** > > + * Start a timer pool > > + * > > + * 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 > > * > > - * @return Timer handle if successful, otherwise ODP_TIMER_INVALID > > + * Destroy a timer pool, freeing all resources. > > + * All timers must have been freed. > > + * > > + * @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, > > odp_timer_tick_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); > > +odp_timer_tick_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 > > + */ > > +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid); > > + > > +/** > > + * ODP timer configurations > > */ > > -uint64_t odp_timer_resolution(odp_timer_t timer); > > + > > +typedef enum odp_timer_pool_conf_e { > > + ODP_TIMER_NAME, /* Return name of timer pool */ > > + ODP_TIMER_RESOLUTION,/* Return the timer resolution (in ns) */ > > + ODP_TIMER_MAX_TMO, /* Return the maximum supported > > timeout (in ns) */ > > + ODP_TIMER_NUM_TIMERS,/* Return number of supported timers */ > > + ODP_TIMER_SHARED /* Return shared flag */ > > +} odp_timer_pool_conf_t; > > > > /** > > - * Maximum timeout in timer ticks > > + * Query different timer pool configurations, e.g. > > + * Timer resolution in nanoseconds > > + * Maximum timeout in timer ticks > > + * Number of supported timers > > + * Shared or private timer pool > > * > > - * @param timer Timer > > + * @param tpid Timer pool identifier > > + * @param item Configuration item being queried > > * > > - * @return Maximum timeout in timer ticks > > + * @return the requested piece of information or 0 for unknown item. > > */ > > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer); > > +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, > > + odp_timer_pool_conf_t item); > > > > /** > > - * Current timer tick > > + * Allocate a timer > > * > > - * @param timer Timer > > + * Create a timer (allocating all necessary resources e.g. > > timeout event) from > > + * the timer pool. > > * > > - * @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 (copied to timeouts) > > + * > > + * @return Timer handle if successful, otherwise > > ODP_TIMER_INVALID and > > + * errno set. > > + */ > > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > > + odp_queue_t queue, > > + void *user_ptr); > > + > > +/** > > + * Free a timer > > + * > > + * Free (destroy) a timer, freeing all associated resources (e.g. > > default > > + * timeout event). An expired and enqueued timeout event will not > > be freed. > > + * It is the responsibility of the application to free this > > timeout when it > > + * is received. > > + * > > + * @param tim Timer handle > > */ > > -uint64_t odp_timer_current_tick(odp_timer_t timer); > > +void odp_timer_free(odp_timer_t tim); > > > > /** > > - * Request timeout with an absolute timer tick > > + * Set a timer (absolute time) with a user-defined timeout buffer > > * > > - * When tick reaches tmo_tick, the timer enqueues the timeout > > notification into > > - * the destination queue. > > + * Set (arm) the timer to expire at specific time. The user-defined > > + * buffer will be enqueued when the timer expires. > > + * Arming may fail (if the timer is in state EXPIRED), an earlier > > timeout > > + * will then be received. odp_timer_tmo_status() must be used to > > check if > > + * the received timeout is valid. > > * > > - * @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. > > + * Note: any invalid parameters will be treated as programming > > errors and will > > + * cause the application to abort. > > + * Note: a timeout too near in time may be delivered immediately. > > + * Note: a timeout too far away in time (beyond max_timeout) > > might be delivered > > + * early. > > * > > - * @return Timeout handle if successful, otherwise > > ODP_TIMER_TMO_INVALID > > + * @param tim Timer > > + * @param abs_tck Expiration time in absolute timer ticks > > + * @param user_buf The buffer to use as timeout event > > */ > > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, > > uint64_t tmo_tick, > > - odp_queue_t queue, > > odp_buffer_t buf); > > +void odp_timer_set_abs_w_buf(odp_timer_t tim, > > + odp_timer_tick_t abs_tck, > > + odp_buffer_t user_buf); > > > > /** > > - * Cancel a timeout > > + * Set a timer with an absolute expiration time > > + * > > + * Set (arm) the timer to expire at a specific time. > > + * Arming may fail (if the timer is in state EXPIRED), an earlier > > timeout > > + * will then be received. odp_timer_tmo_status() must be used to > > check if > > + * the received timeout is valid. > > * > > - * @param timer Timer > > - * @param tmo Timeout to cancel > > + * Note: any invalid parameters will be treated as programming > > errors and will > > + * cause the application to abort. > > + * Note: a timeout too near in time may be delivered immediately. > > + * Note: a timeout too far away in time (beyond max_timeout) > > might be delivered > > + * early, it will automatically be reset by odp_timer_tmo_status(). > > * > > - * @return 0 if successful > > + * @param tim Timer > > + * @param abs_tck Expiration time in absolute timer ticks > > */ > > -int odp_timer_cancel_tmo(odp_timer_t timer, odp_timer_tmo_t tmo); > > +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck); > > > > /** > > - * Convert buffer handle to timeout handle > > + * Set a timer with a relative expiration time > > * > > - * @param buf Buffer handle > > + * Set (arm) the timer to expire at a relative future time. > > + * Arming may fail (if the timer is in state EXPIRED), > > + * an earlier timeout will then be received. > > odp_timer_tmo_status() must > > + * be used to check if the received timeout is valid. > > * > > - * @return Timeout buffer handle > > + * Note: any invalid parameters will be treated as programming > > errors and will > > + * cause the application to abort. > > + * Note: a timeout too near in time may be delivered immediately. > > + * Note: a timeout too far away in time (beyond max_timeout) > > might be delivered > > + * early, it will automatically be reset by odp_timer_tmo_status(). > > + * > > + * @param tim Timer > > + * @param rel_tck Expiration time in timer ticks relative to > > current time of > > + * the timer pool the timer belongs to > > */ > > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf); > > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck); > > > > /** > > - * Return absolute timeout tick > > + * Cancel a timer > > + * > > + * Cancel a timer, preventing future expiration and delivery. > > + * > > + * A timer that has already expired and been enqueued for > > delivery may be > > + * impossible to cancel and will instead be delivered to the > > destination queue. > > + * Use odp_timer_tmo_status() the check whether a received > > timeout is fresh or > > + * stale (cancelled). Stale timeouts will automatically be recycled. > > * > > - * @param tmo Timeout buffer handle > > + * Note: any invalid parameters will be treated as programming > > errors and will > > + * cause the application to abort. > > * > > - * @return Absolute timeout tick > > + * @param tim Timer handle > > */ > > -uint64_t odp_timeout_tick(odp_timeout_t tmo); > > +void odp_timer_cancel(odp_timer_t tim); > > + > > +/** > > + * Return fresh/stale/orphan status of timeout. > > + * > > + * Check a received timeout for orphaness (i.e. parent timer > > freed) and > > + * staleness (i.e. parent timer has been reset or cancelled after > > timeout > > + * was enqueued). > > + * If the timeout is fresh, it should be processed. > > + * If the timeout is stale, the timer will automatically be reset > > unless it > > + * was cancelled. > > + * If the timeout is orphaned, it should be freed (by the caller). > > + * > > + * Note: odp_timer_tmo_status() must be called on all received (not > > + * user-defined) timeouts! > > + * > > + * @param tmo Timeout > > + * > > + * @return ODP_TMO_FRESH, ODP_TMO_STALE, ODP_TMO_ORPHAN > > + */ > > +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo); > > + > > +/** > > + * Get timer handle > > + * > > + * Return Handle of parent timer. > > + * > > + * @param tmo Timeout > > + * > > + * @return Timer handle or ODP_TIMER_INVALID for orphaned timeouts > > + */ > > +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo); > > + > > +/** > > + * Get expiration time > > + * > > + * Return (actual) expiration time of timeout. > > + * > > + * @param tmo Timeout > > + * > > + * @return Expiration time > > + */ > > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo); > > + > > +/** > > + * Get user pointer > > + * > > + * Return User pointer of timer associated with timeout. > > + * The user pointer is often used to point to some associated > > context. > > + * > > + * @param tmo Timeout > > + * > > + * @return User pointer > > + */ > > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo); > > + > > +/* Helper functions */ > > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, > > odp_timer_tick_t tick); > > > > #ifdef __cplusplus > > } > > diff --git a/platform/linux-generic/include/odp_timer_internal.h > > b/platform/linux-generic/include/odp_timer_internal.h > > index ad28f53..ff8f209 100644 > > --- a/platform/linux-generic/include/odp_timer_internal.h > > +++ b/platform/linux-generic/include/odp_timer_internal.h > > @@ -1,4 +1,4 @@ > > -/* Copyright (c) 2013, Linaro Limited > > +/* Copyright (c) 2014, Linaro Limited > > * All rights reserved. > > * > > * SPDX-License-Identifier: BSD-3-Clause > > @@ -8,72 +8,53 @@ > > /** > > * @file > > * > > - * ODP timer timeout descriptor - implementation internal > > + * ODP timeout descriptor - implementation internal > > */ > > > > #ifndef ODP_TIMER_INTERNAL_H_ > > #define ODP_TIMER_INTERNAL_H_ > > > > -#ifdef __cplusplus > > -extern "C" { > > -#endif > > - > > -#include <odp_std_types.h> > > -#include <odp_queue.h> > > -#include <odp_buffer.h> > > +#include <odp_align.h> > > +#include <odp_debug.h> > > #include <odp_buffer_internal.h> > > #include <odp_buffer_pool_internal.h> > > #include <odp_timer.h> > > > > -struct timeout_t; > > - > > -typedef struct timeout_t { > > - struct timeout_t *next; > > - int timer_id; > > - int tick; > > - uint64_t tmo_tick; > > - odp_queue_t queue; > > - odp_buffer_t buf; > > - odp_buffer_t tmo_buf; > > -} timeout_t; > > - > > - > > -struct odp_timeout_hdr_t; > > - > > /** > > - * Timeout notification header > > + * Internal Timeout header > > */ > > -typedef struct odp_timeout_hdr_t { > > +typedef struct { > > + /* common buffer header */ > > odp_buffer_hdr_t buf_hdr; > > > > - timeout_t meta; > > - > > + /* Requested expiration time */ > > + odp_timer_tick_t expiration; > > + /* User ptr inherited from parent timer */ > > + void *user_ptr; > > + /* Parent timer */ > > + odp_timer_t timer; > > + /* Tag inherited from parent timer at time of expiration */ > > + uint32_t tag; > > + /* Gen-cnt inherited from parent timer at time of creation */ > > + uint32_t gc; > > uint8_t buf_data[]; > > } odp_timeout_hdr_t; > > > > - > > - > > +/* C++ doesn't allow offsetof() on "non-POD" datatypes. Don't > > know why > > + odp_timeout_hdr_t is classified as non-POD, perhaps because of the > > + inheritance? */ > > ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) == > > - ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), > > - "ODP_TIMEOUT_HDR_T__SIZE_ERR"); > > - > > + ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), > > + "sizeof(odp_timeout_hdr_t) == > > ODP_OFFSETOF(odp_timeout_hdr_t, buf_data)"); > > ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == 0, > > - "ODP_TIMEOUT_HDR_T__SIZE_ERR2"); > > - > > + "sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == > > 0"); > > > > /** > > - * Return timeout header > > + * Return the timeout header > > */ > > -static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_timeout_t tmo) > > +static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_buffer_t buf) > > { > > - odp_buffer_hdr_t *buf_hdr = odp_buf_to_hdr((odp_buffer_t)tmo); > > - return (odp_timeout_hdr_t *)(uintptr_t)buf_hdr; > > -} > > - > > - > > - > > -#ifdef __cplusplus > > + return (odp_timeout_hdr_t *)odp_buf_to_hdr(buf); > > } > > -#endif > > > > #endif > > diff --git a/platform/linux-generic/odp_timer.c > > b/platform/linux-generic/odp_timer.c > > index 1bf37f9..cea47a3 100644 > > --- a/platform/linux-generic/odp_timer.c > > +++ b/platform/linux-generic/odp_timer.c > > @@ -1,402 +1,631 @@ > > -/* Copyright (c) 2013, Linaro Limited > > +/* Copyright (c) 2014, Linaro Limited > > * All rights reserved. > > * > > * SPDX-License-Identifier: BSD-3-Clause > > */ > > > > -#include <odp_timer.h> > > -#include <odp_timer_internal.h> > > -#include <odp_buffer_pool_internal.h> > > -#include <odp_internal.h> > > -#include <odp_atomic.h> > > -#include <odp_spinlock.h> > > -#include <odp_sync.h> > > -#include <odp_debug.h> > > > > -#include <signal.h> > > -#include <time.h> > > +/** > > + * @file > > + * > > + * ODP timer service > > + * > > + */ > > > > +#include <assert.h> > > +#include <errno.h> > > #include <string.h> > > - > > -#define NUM_TIMERS 1 > > -#define MAX_TICKS 1024 > > -#define RESOLUTION_NS 1000000 > > - > > - > > -typedef struct { > > - odp_spinlock_t lock; > > - timeout_t *list; > > -} tick_t; > > - > > -typedef struct { > > - int allocated; > > - volatile int active; > > - volatile uint64_t cur_tick; > > - timer_t timerid; > > - odp_timer_t timer_hdl; > > - odp_buffer_pool_t pool; > > - uint64_t resolution_ns; > > - uint64_t max_ticks; > > - tick_t tick[MAX_TICKS]; > > - > > -} timer_ring_t; > > - > > -typedef struct { > > - odp_spinlock_t lock; > > - int num_timers; > > - timer_ring_t timer[NUM_TIMERS]; > > - > > -} timer_global_t; > > - > > -/* Global */ > > -static timer_global_t odp_timer; > > - > > -static void add_tmo(tick_t *tick, timeout_t *tmo) > > +#include <stdlib.h> > > +#include <time.h> > > +#include <signal.h> > > +#include "odp_std_types.h" > > +#include "odp_buffer.h" > > +#include "odp_buffer_pool.h" > > +#include "odp_queue.h" > > +#include "odp_hints.h" > > +#include "odp_sync.h" > > +#include "odp_spinlock.h" > > +#include "odp_debug.h" > > +#include "odp_align.h" > > +#include "odp_shared_memory.h" > > +#include "odp_hints.h" > > +#include "odp_internal.h" > > +#include "odp_timer.h" > > +#include "odp_timer_internal.h" > > +#include "priority_queue.h" > > + > > +typedef struct odp_timer { > > + pq_element pqelem;/* Base class */ > > + odp_timer_tick_t req_tmo;/* Requested timeout tick */ > > + odp_buffer_t tmo_buf;/* ODP_BUFFER_INVALID if timeout > > enqueued */ > > + odp_queue_t queue;/* ODP_QUEUE_INVALID if timer is free */ > > + uint32_t tag;/* Reusing tag as next pointer/index when > > timer is free */ > > + uint32_t gc; > > + bool user_buf; /* User-defined buffer? */ > > +} odp_timer; > > + > > +/* Constructor for array of objects */ > > +static inline void odp_timer_con(odp_timer *this) > > { > > - odp_spinlock_lock(&tick->lock); > > - > > - tmo->next = tick->list; > > - tick->list = tmo; > > - > > - odp_spinlock_unlock(&tick->lock); > > + pq_element_con(&this->pqelem); > > + this->tmo_buf = ODP_BUFFER_INVALID; > > + this->queue = ODP_QUEUE_INVALID; > > + this->gc = 0; > > } > > > > -static timeout_t *rem_tmo(tick_t *tick) > > +/* Destructor */ > > +static inline void odp_timer_des(odp_timer *this) > > { > > - timeout_t *tmo; > > - > > - odp_spinlock_lock(&tick->lock); > > - > > - tmo = tick->list; > > - > > - if (tmo) > > - tick->list = tmo->next; > > - > > - odp_spinlock_unlock(&tick->lock); > > + assert(this->tmo_buf == ODP_BUFFER_INVALID); > > + assert(this->queue == ODP_QUEUE_INVALID); > > + pq_element_des(&this->pqelem); > > +} > > > > - if (tmo) > > - tmo->next = NULL; > > +/* Setup when timer is allocated */ > > +static void setup(odp_timer *this, > > + odp_queue_t _q, > > + void *_up, > > + odp_buffer_t _tmo) > > +{ > > + this->req_tmo = INVALID_PRIORITY; > > + this->tmo_buf = _tmo; > > + this->queue = _q; > > + this->tag = 0; > > + this->user_buf = false; > > + /* Initialise constant fields of timeout event */ > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t *)odp_buf_to_hdr(this->tmo_buf); > > + tmo_hdr->gc = this->gc; > > + tmo_hdr->timer = this; > > + tmo_hdr->user_ptr = _up; > > + /* tmo_hdr->tag set at expiration time */ > > + /* tmo_hdr->expiration set at expiration time */ > > + assert(this->queue != ODP_QUEUE_INVALID); > > +} > > > > - return tmo; > > +/* Teardown when timer is freed */ > > +static odp_buffer_t teardown(odp_timer *this) > > +{ > > + /* Increase generation count to make pending timeout > > orphaned */ > > + ++this->gc; > > + odp_buffer_t buf = this->tmo_buf; > > + this->tmo_buf = ODP_BUFFER_INVALID; > > + this->queue = ODP_QUEUE_INVALID; > > + return buf; > > } > > > > -/** > > - * Search and delete tmo entry from timeout list > > - * return -1 : on error.. handle not in list > > - * 0 : success > > - */ > > -static int find_and_del_tmo(timeout_t **tmo, odp_timer_tmo_t handle) > > +static inline uint32_t get_next_free(odp_timer *this) > > { > > - timeout_t *cur, *prev; > > - prev = NULL; > > + assert(this->queue == ODP_QUEUE_INVALID); > > + return this->tag; > > +} > > > > - for (cur = *tmo; cur != NULL; prev = cur, cur = cur->next) { > > - if (cur->tmo_buf == handle) { > > - if (prev == NULL) > > - *tmo = cur->next; > > - else > > - prev->next = cur->next; > > +static inline void set_next_free(odp_timer *this, uint32_t nf) > > +{ > > + assert(this->queue == ODP_QUEUE_INVALID); > > + this->tag = nf; > > +} > > > > - break; > > +static inline void expire(odp_timer *this, odp_timer_tick_t tick) > > +{ > > + /* Timer expired, is there actually any timeout event */ > > + /* we can enqueue? */ > > + if (odp_likely(this->tmo_buf != ODP_BUFFER_INVALID)) { > > + /* Swap out timeout buffer */ > > + odp_buffer_t buf = this->tmo_buf; > > + this->tmo_buf = ODP_BUFFER_INVALID; > > + if (odp_likely(!this->user_buf)) { > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t > > *)odp_buf_to_hdr(buf); > > + /* Copy tag from timer */ > > + /* and actual expiration tick from timer > > pool */ > > + tmo_hdr->tag = this->tag; > > + tmo_hdr->expiration = tick; > > } > > + /* Else don't touch user-defined buffer */ > > + int rc = odp_queue_enq(this->queue, buf); > > + if (rc != 0) > > + abort(); > > } > > - > > - if (!cur) > > - /* couldn't find tmo in list */ > > - return -1; > > - > > - /* application to free tmo_buf provided by absolute_tmo > > call */ > > - return 0; > > + /* No, timeout event already enqueued */ > > } > > > > -int odp_timer_cancel_tmo(odp_timer_t timer_hdl, odp_timer_tmo_t tmo) > > +typedef struct odp_timer_pool { > > + priority_queue pq; > > + uint64_t tick; > > + bool shared; > > + odp_spinlock_t lock; > > + const char *name; > > + odp_buffer_pool_t buf_pool; > > + uint64_t resolution_ns; > > + uint64_t max_timeout; > > + odp_timer *timers; > > + uint32_t num_alloc;/* Current number of allocated timers */ > > + uint32_t max_timers;/* Max number of timers */ > > + uint32_t first_free;/* 0..max_timers-1 => free timer */ > > + timer_t timerid; > > + odp_timer_pool_clock_source_t clk_src; > > +} odp_timer_pool; > > + > > +/* Forward declarations */ > > +static void timer_init(odp_timer_pool *tp); > > +static void timer_exit(odp_timer_pool *tp); > > + > > +static void odp_timer_pool_con(odp_timer_pool *this, > > + const char *_n, > > + odp_buffer_pool_t _bp, > > + uint64_t _r, > > + uint64_t _m, > > + uint32_t _mt, > > + bool _s, > > + odp_timer_pool_clock_source_t _cs) > > { > > - int id; > > - uint64_t tick_idx; > > - timeout_t *cancel_tmo; > > - odp_timeout_hdr_t *tmo_hdr; > > - tick_t *tick; > > - > > - /* get id */ > > - id = timer_hdl - 1; > > - > > - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo); > > - /* get tmo_buf to cancel */ > > - cancel_tmo = &tmo_hdr->meta; > > - > > - tick_idx = cancel_tmo->tick; > > - tick = &odp_timer.timer[id].tick[tick_idx]; > > - > > - odp_spinlock_lock(&tick->lock); > > - /* search and delete tmo from tick list */ > > - if (find_and_del_tmo(&tick->list, tmo) != 0) { > > - odp_spinlock_unlock(&tick->lock); > > - ODP_DBG("Couldn't find the tmo (%d) in tick > > list\n", (int)tmo); > > - return -1; > > + priority_queue_con(&this->pq, _mt); > > + this->tick = 0; > > + this->shared = _s; > > + this->name = strdup(_n); > > + this->buf_pool = _bp; > > + this->resolution_ns = _r; > > + this->max_timeout = _m; > > + this->num_alloc = 0; > > + this->max_timers = _mt; > > + this->first_free = 0; > > + this->clk_src = _cs; > > + this->timers = malloc(sizeof(odp_timer) * this->max_timers); > > + if (this->timers == NULL) { > > + ODP_ERR("%s: malloc failed\n", _n); > > + abort(); > > } > > - odp_spinlock_unlock(&tick->lock); > > - > > - return 0; > > + uint32_t i; > > + for (i = 0; i < this->max_timers; i++) > > + odp_timer_con(&this->timers[i]); > > + for (i = 0; i < this->max_timers; i++) > > + set_next_free(&this->timers[i], i + 1); > > + odp_spinlock_init(&this->lock); > > + if (this->clk_src == ODP_CLOCK_DEFAULT) > > + timer_init(this); > > + /* Make sure timer pool initialisation is globally > > observable */ > > + /* before we return a pointer to it */ > > + odp_sync_stores(); > > } > > > > -static void notify_function(union sigval sigval) > > +static odp_timer_pool *odp_timer_pool_new( > > + const char *_n, > > + odp_buffer_pool_t _bp, > > + uint64_t _r, > > + uint64_t _m, > > + uint32_t _mt, > > + bool _s, > > + odp_timer_pool_clock_source_t _cs) > > { > > - uint64_t cur_tick; > > - timeout_t *tmo; > > - tick_t *tick; > > - timer_ring_t *timer; > > - > > - timer = sigval.sival_ptr; > > - > > - if (timer->active == 0) { > > - ODP_DBG("Timer (%u) not active\n", timer->timer_hdl); > > - return; > > + odp_timer_pool *this = malloc(sizeof(odp_timer_pool)); > > + if (odp_unlikely(this == NULL)) { > > + ODP_ERR("%s: malloc failed\n", _n); > > + abort(); > > } > > + odp_timer_pool_con(this, _n, _bp, _r, _m, _mt, _s, _cs); > > + return this; > > +} > > > > - /* ODP_DBG("Tick\n"); */ > > +static void odp_timer_pool_des(odp_timer_pool *this) > > +{ > > + if (this->shared) > > + odp_spinlock_lock(&this->lock); > > + if (this->num_alloc != 0) { > > + /* It's a programming error to attempt to destroy a */ > > + /* timer pool which is still in use */ > > + ODP_ERR("%s: timers in use\n", this->name); > > + abort(); > > + } > > + if (this->clk_src == ODP_CLOCK_DEFAULT) > > + timer_exit(this); > > + uint32_t i; > > + for (i = 0; i < this->max_timers; i++) > > + odp_timer_des(&this->timers[i]); > > + free(this->timers); > > + priority_queue_des(&this->pq); > > + odp_sync_stores(); > > +} > > > > - cur_tick = timer->cur_tick++; > > +static void odp_timer_pool_del(odp_timer_pool *this) > > +{ > > + odp_timer_pool_des(this); > > + free(this); > > +} > > > > - odp_sync_stores(); > > +static inline odp_timer *timer_alloc(odp_timer_pool *this, > > + odp_queue_t queue, > > + void *user_ptr, > > + odp_buffer_t tmo_buf) > > +{ > > + odp_timer *tim = ODP_TIMER_INVALID; > > + if (odp_likely(this->shared)) > > + odp_spinlock_lock(&this->lock); > > + if (odp_likely(this->num_alloc < this->max_timers)) { > > + this->num_alloc++; > > + /* Remove first unused timer from free list */ > > + assert(this->first_free != this->max_timers); > > + tim = &this->timers[this->first_free]; > > + this->first_free = get_next_free(tim); > > + /* Insert timer into priority queue */ > > + if (odp_unlikely(!pq_register_element(&this->pq, > > + &tim->pqelem))) { > > + /* Unexpected internal error */ > > + abort(); > > + } > > + /* Create timer */ > > + setup(tim, queue, user_ptr, tmo_buf); > > + } else { > > + errno = ENFILE; /* Reusing file table overvlow */ > > + } > > + if (odp_likely(this->shared)) > > + odp_spinlock_unlock(&this->lock); > > + return tim; > > +} > > > > - tick = &timer->tick[cur_tick % MAX_TICKS]; > > +static inline void timer_free(odp_timer_pool *this, odp_timer *tim) > > +{ > > + if (odp_likely(this->shared)) > > + odp_spinlock_lock(&this->lock); > > + /* Destroy timer */ > > + odp_buffer_t buf = teardown(tim); > > + /* Remove timer from priority queue */ > > + pq_unregister_element(&this->pq, &tim->pqelem); > > + /* Insert timer into free list */ > > + set_next_free(tim, this->first_free); > > + this->first_free = (tim - &this->timers[0]) / > > sizeof(this->timers[0]); > > + assert(this->num_alloc != 0); > > + this->num_alloc--; > > + if (odp_likely(this->shared)) > > + odp_spinlock_unlock(&this->lock); > > + if (buf != ODP_BUFFER_INVALID) > > + odp_buffer_free(buf); > > +} > > > > - while ((tmo = rem_tmo(tick)) != NULL) { > > - odp_queue_t queue; > > - odp_buffer_t buf; > > +static inline void timer_reset(odp_timer_pool *this, > > + odp_timer *tim, > > + odp_timer_tick_t abs_tck) > > +{ > > + if (odp_likely(this->shared)) > > + odp_spinlock_lock(&this->lock); > > + /* Increase timer tag to make any pending timeout stale */ > > + tim->tag++; > > + /* Save requested timeout */ > > + tim->req_tmo = abs_tck; > > + /* Update timer position in priority queue */ > > + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); > > + if (odp_likely(this->shared)) > > + odp_spinlock_unlock(&this->lock); > > +} > > > > - queue = tmo->queue; > > - buf = tmo->buf; > > +static inline void timer_reset_w_buf(odp_timer_pool *this, > > + odp_timer *tim, > > + odp_timer_tick_t abs_tck, > > + odp_buffer_t user_buf) > > +{ > > + if (odp_likely(this->shared)) > > + odp_spinlock_lock(&this->lock); > > + /* Increase timer tag to make any pending timeout stale */ > > + tim->tag++; > > + /* Save requested timeout */ > > + tim->req_tmo = abs_tck; > > + /* Set flag indicating presence of user defined buffer */ > > + tim->user_buf = true; > > + /* Swap in new buffer, get any old buffer pointer */ > > + odp_buffer_t old_buf = tim->tmo_buf; > > + tim->tmo_buf = user_buf; > > + /* Update timer position in priority queue */ > > + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); > > + if (odp_likely(this->shared)) > > + odp_spinlock_unlock(&this->lock); > > + /* Free old buffer if present */ > > + if (odp_unlikely(old_buf != ODP_BUFFER_INVALID)) > > + odp_buffer_free(old_buf); > > +} > > > > - if (buf != tmo->tmo_buf) > > - odp_buffer_free(tmo->tmo_buf); > > +static inline void timer_cancel(odp_timer_pool *this, > > + odp_timer *tim) > > +{ > > + odp_buffer_t tmo_buf = ODP_BUFFER_INVALID; > > + if (odp_likely(this->shared)) > > + odp_spinlock_lock(&this->lock); > > + if (odp_unlikely(tim->user_buf)) { > > + /* Swap out old user buffer */ > > + tmo_buf = tim->tmo_buf; > > + tim->tmo_buf = ODP_BUFFER_INVALID; > > + tim->user_buf = false; > > + } > > + /* Else a normal timer (no user-defined buffer) */ > > + /* Increase timer tag to make any pending timeout stale */ > > + tim->tag++; > > + /* Clear requested timeout */ > > + tim->req_tmo = INVALID_PRIORITY; > > + /* Remove timer from the priority queue */ > > + pq_deactivate_element(&this->pq, &tim->pqelem); > > + if (odp_likely(this->shared)) > > + odp_spinlock_unlock(&this->lock); > > + /* Free user-defined buffer if present */ > > + if (odp_unlikely(tmo_buf != ODP_BUFFER_INVALID)) > > + odp_buffer_free(tmo_buf); > > +} > > > > - odp_queue_enq(queue, buf); > > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, > > odp_timer_tick_t tick) > > +{ > > + odp_spinlock_lock(&tpid->lock); > > + unsigned nexp = 0; > > + odp_timer_t tim; > > + tpid->tick = tick; > > + while ((tim = (odp_timer_t)pq_release_element(&tpid->pq, > > tick)) != > > + ODP_TIMER_INVALID) { > > + assert(get_prio(&tim->pqelem) <= tick); > > + expire(tim, tick); > > + nexp++; > > } > > + odp_spinlock_unlock(&tpid->lock); > > + return nexp; > > } > > > > -static void timer_start(timer_ring_t *timer) > > +/* Functions that use Linux/POSIX per-process timers and related > > facilities */ > > +static void timer_notify(union sigval sigval) > > +{ > > + odp_timer_pool *tp = (odp_timer_pool *)sigval.sival_ptr; > > + uint64_t new_tick = tp->tick + 1; > > + (void)odp_timer_pool_expire(tp, new_tick); > > +} > > + > > +static void timer_init(odp_timer_pool *tp) > > { > > struct sigevent sigev; > > struct itimerspec ispec; > > > > - ODP_DBG("\nTimer (%u) starts\n", timer->timer_hdl); > > + ODP_DBG("Creating POSIX timer for timer pool %s, period %" > > + PRIu64" ns\n", tp->name, tp->resolution_ns); > > > > memset(&sigev, 0, sizeof(sigev)); > > memset(&ispec, 0, sizeof(ispec)); > > > > sigev.sigev_notify = SIGEV_THREAD; > > - sigev.sigev_notify_function = notify_function; > > - sigev.sigev_value.sival_ptr = timer; > > + sigev.sigev_notify_function = timer_notify; > > + sigev.sigev_value.sival_ptr = tp; > > > > - if (timer_create(CLOCK_MONOTONIC, &sigev, &timer->timerid)) { > > - ODP_DBG("Timer create failed\n"); > > - return; > > + if (timer_create(CLOCK_MONOTONIC, &sigev, &tp->timerid)) { > > + perror("timer_create"); > > + abort(); > > } > > > > ispec.it_interval.tv_sec = 0; > > - ispec.it_interval.tv_nsec = RESOLUTION_NS; > > + ispec.it_interval.tv_nsec = tp->resolution_ns; > > ispec.it_value.tv_sec = 0; > > - ispec.it_value.tv_nsec = RESOLUTION_NS; > > + ispec.it_value.tv_nsec = tp->resolution_ns; > > > > - if (timer_settime(timer->timerid, 0, &ispec, NULL)) { > > - ODP_DBG("Timer set failed\n"); > > - return; > > + if (timer_settime(&tp->timerid, 0, &ispec, NULL)) { > > + perror("timer_settime"); > > + abort(); > > } > > - > > - return; > > } > > > > -int odp_timer_init_global(void) > > +static void timer_exit(odp_timer_pool *tp) > > { > > - ODP_DBG("Timer init ..."); > > - > > - memset(&odp_timer, 0, sizeof(timer_global_t)); > > - > > - odp_spinlock_init(&odp_timer.lock); > > - > > - ODP_DBG("done\n"); > > - > > - return 0; > > + if (timer_delete(tp->timerid) != 0) { > > + perror("timer_delete"); > > + abort(); > > + } > > } > > > > -int odp_timer_disarm_all(void) > > +odp_timer_pool_t > > +odp_timer_pool_create(const char *name, > > + odp_buffer_pool_t buf_pool, > > + uint64_t resolution_ns, > > + uint64_t max_timeout, > > + uint32_t num_timers, > > + bool shared, > > + odp_timer_pool_clock_source_t clk_src) > > { > > - int timers; > > - struct itimerspec ispec; > > - > > - odp_spinlock_lock(&odp_timer.lock); > > - > > - timers = odp_timer.num_timers; > > - > > - ispec.it_interval.tv_sec = 0; > > - ispec.it_interval.tv_nsec = 0; > > - ispec.it_value.tv_sec = 0; > > - ispec.it_value.tv_nsec = 0; > > - > > - for (; timers >= 0; timers--) { > > - if (timer_settime(odp_timer.timer[timers].timerid, > > - 0, &ispec, NULL)) { > > - ODP_DBG("Timer reset failed\n"); > > - odp_spinlock_unlock(&odp_timer.lock); > > - return -1; > > - } > > - odp_timer.num_timers--; > > + /* Verify that buffer pool can be used for timeouts */ > > + odp_buffer_t buf = odp_buffer_alloc(buf_pool); > > + if (buf == ODP_BUFFER_INVALID) { > > + ODP_ERR("%s: Failed to allocate buffer\n", name); > > + abort(); > > } > > + if (odp_buffer_type(buf) != ODP_BUFFER_TYPE_TIMEOUT) { > > + ODP_ERR("%s: Buffer pool wrong type\n", name); > > + abort(); > > + } > > + odp_buffer_free(buf); > > + odp_timer_pool_t tp = odp_timer_pool_new(name, buf_pool, > > resolution_ns, > > + max_timeout, num_timers, > > + shared, clk_src); > > + return tp; > > +} > > > > - odp_spinlock_unlock(&odp_timer.lock); > > +void odp_timer_pool_start(void) > > +{ > > + /* Nothing to do here */ > > +} > > > > - return 0; > > +void odp_timer_pool_destroy(odp_timer_pool_t tpid) > > +{ > > + odp_timer_pool_del(tpid); > > } > > > > -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) > > +uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, > > odp_timer_tick_t ticks) > > { > > - uint32_t id; > > - timer_ring_t *timer; > > - odp_timer_t timer_hdl; > > - int i; > > - (void) name; (void) resolution; (void) min_tmo; (void) > > max_tmo; > > + return ticks * tpid->resolution_ns; > > +} > > > > - odp_spinlock_lock(&odp_timer.lock); > > +odp_timer_tick_t odp_timer_ns_to_tick(odp_timer_pool_t tpid, > > uint64_t ns) > > +{ > > + return (odp_timer_tick_t)(ns / tpid->resolution_ns); > > +} > > > > - if (odp_timer.num_timers >= NUM_TIMERS) { > > - odp_spinlock_unlock(&odp_timer.lock); > > - return ODP_TIMER_INVALID; > > - } > > +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid) > > +{ > > + return tpid->tick; > > +} > > > > - for (id = 0; id < NUM_TIMERS; id++) { > > - if (odp_timer.timer[id].allocated == 0) > > - break; > > +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, > > + odp_timer_pool_conf_t item) > > +{ > > + switch (item) { > > + case ODP_TIMER_NAME: > > + return (uint64_t)(tpid->name); > > + case ODP_TIMER_RESOLUTION: > > + return tpid->resolution_ns; > > + case ODP_TIMER_MAX_TMO: > > + return tpid->max_timeout; > > + case ODP_TIMER_NUM_TIMERS: > > + return tpid->max_timers; > > + case ODP_TIMER_SHARED: > > + return tpid->shared; > > + default: > > + return 0; > > } > > +} > > > > - timer = &odp_timer.timer[id]; > > - timer->allocated = 1; > > - odp_timer.num_timers++; > > - > > - odp_spinlock_unlock(&odp_timer.lock); > > - > > - timer_hdl = id + 1; > > - > > - timer->timer_hdl = timer_hdl; > > - timer->pool = pool; > > - timer->resolution_ns = RESOLUTION_NS; > > - timer->max_ticks = MAX_TICKS; > > - > > - for (i = 0; i < MAX_TICKS; i++) { > > - odp_spinlock_init(&timer->tick[i].lock); > > - timer->tick[i].list = NULL; > > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > > + odp_queue_t queue, > > + void *user_ptr) > > +{ > > + /* We check this because ODP_QUEUE_INVALID is used */ > > + /* to indicate a free timer */ > > + if (odp_unlikely(queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("%s: Invalid queue identifier\n", tpid->name); > > + abort(); > > } > > - > > - timer->active = 1; > > - odp_sync_stores(); > > - > > - timer_start(timer); > > - > > - return timer_hdl; > > + odp_buffer_t tmo_buf = odp_buffer_alloc(tpid->buf_pool); > > + if (odp_likely(tmo_buf != ODP_BUFFER_INVALID)) { > > + odp_timer *tim = timer_alloc(tpid, queue, > > user_ptr, tmo_buf); > > + if (tim != ODP_TIMER_INVALID) { > > + /* Success */ > > + assert(tim->queue != ODP_QUEUE_INVALID); > > + return tim; > > + } > > + odp_buffer_free(tmo_buf); > > + } > > + /* Else failed to allocate timeout event */ > > + /* errno set by odp_buffer_alloc() or timer_alloc () */ > > + return ODP_TIMER_INVALID; > > } > > > > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer_hdl, > > uint64_t tmo_tick, > > - odp_queue_t queue, > > odp_buffer_t buf) > > +void odp_timer_free(odp_timer_t tim) > > { > > - int id; > > - uint64_t tick; > > - uint64_t cur_tick; > > - timeout_t *new_tmo; > > - odp_buffer_t tmo_buf; > > - odp_timeout_hdr_t *tmo_hdr; > > - timer_ring_t *timer; > > - > > - id = timer_hdl - 1; > > - timer = &odp_timer.timer[id]; > > - > > - cur_tick = timer->cur_tick; > > - if (tmo_tick <= cur_tick) { > > - ODP_DBG("timeout too close\n"); > > - return ODP_TIMER_TMO_INVALID; > > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("Invalid timer %p\n", tim); > > + abort(); > > } > > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > > + timer_free(tp, tim); > > +} > > > > - tick = tmo_tick - cur_tick; > > - if (tick > MAX_TICKS) { > > - ODP_DBG("timeout too far\n"); > > - return ODP_TIMER_TMO_INVALID; > > +void odp_timer_set_abs_w_buf(odp_timer_t tim, > > + odp_timer_tick_t abs_tck, > > + odp_buffer_t user_buf) > > +{ > > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("Invalid timer %p\n", tim); > > + abort(); > > } > > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > > + timer_reset_w_buf(tp, tim, abs_tck, user_buf); > > +} > > > > - tick = (cur_tick + tick) % MAX_TICKS; > > - > > - tmo_buf = odp_buffer_alloc(timer->pool); > > - if (tmo_buf == ODP_BUFFER_INVALID) { > > - ODP_DBG("alloc failed\n"); > > - return ODP_TIMER_TMO_INVALID; > > +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck) > > +{ > > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("Invalid timer %p\n", tim); > > + abort(); > > } > > - > > - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo_buf); > > - new_tmo = &tmo_hdr->meta; > > - > > - new_tmo->timer_id = id; > > - new_tmo->tick = (int)tick; > > - new_tmo->tmo_tick = tmo_tick; > > - new_tmo->queue = queue; > > - new_tmo->tmo_buf = tmo_buf; > > - > > - if (buf != ODP_BUFFER_INVALID) > > - new_tmo->buf = buf; > > - else > > - new_tmo->buf = tmo_buf; > > - > > - add_tmo(&timer->tick[tick], new_tmo); > > - > > - return tmo_buf; > > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > > + timer_reset(tp, tim, abs_tck); > > } > > > > -uint64_t odp_timer_tick_to_ns(odp_timer_t timer_hdl, uint64_t ticks) > > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck) > > { > > - uint32_t id; > > - > > - id = timer_hdl - 1; > > - return ticks * odp_timer.timer[id].resolution_ns; > > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("Invalid timer %p\n", tim); > > + abort(); > > + } > > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > > + timer_reset(tp, tim, tp->tick + rel_tck); > > } > > > > -uint64_t odp_timer_ns_to_tick(odp_timer_t timer_hdl, uint64_t ns) > > +void odp_timer_cancel(odp_timer_t tim) > > { > > - uint32_t id; > > - > > - id = timer_hdl - 1; > > - return ns / odp_timer.timer[id].resolution_ns; > > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > > + ODP_ERR("Invalid timer %p\n", tim); > > + abort(); > > + } > > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > > + timer_cancel(tp, tim); > > } > > > > -uint64_t odp_timer_resolution(odp_timer_t timer_hdl) > > +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo_buf) > > { > > - uint32_t id; > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > > + odp_timer *tim = tmo_hdr->timer; > > + > > + /* Make sure stores to '*tim' are visible */ > > + odp_sync_stores(); > > > > - id = timer_hdl - 1; > > - return odp_timer.timer[id].resolution_ns; > > + /* Compare generation count (gc) of timeout and parent > > timer (if any)*/ > > + if (odp_unlikely(tim == ODP_TIMER_INVALID || > > + tmo_hdr->gc != tim->gc)) { > > + /* Generation counters differ => timeout is > > orphaned */ > > + return ODP_TMO_ORPHAN; > > + } > > + /* Else gen-cnts match => parent timer exists */ > > + > > + /* Return timeout to timer so that it can be delivered > > again */ > > + tim->tmo_buf = tmo_buf; > > + /* FIXME do we need some kind of synchronisation or > > locking here? */ > > + > > + /* Compare tags of timeout and parent timer */ > > + /* Compare requested and actual timeout time */ > > + if (odp_likely(tim->tag == tmo_hdr->tag && > > + tim->req_tmo <= tmo_hdr->expiration)) { > > + /* Tags match, actual timeout is after requested > > => good! */ > > + return ODP_TMO_FRESH; > > + } else { > > + /* Tags don't match or actual timeout time is > > before */ > > + /* requested */ > > + /* Timer has been reset or cancelled and timeout > > is stale */ > > + /* or timeout expired too early */ > > + if (tim->req_tmo != INVALID_PRIORITY) { > > + /* Reset the timer for requested timeout */ > > + odp_timer_set_abs(tim, tim->req_tmo); > > + } > > + /* Else timer was cancelled, do nothing */ > > + return ODP_TMO_STALE; > > + } > > } > > > > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer_hdl) > > +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo_buf) > > { > > - uint32_t id; > > - > > - id = timer_hdl - 1; > > - return odp_timer.timer[id].max_ticks; > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > > + odp_timer_t tim = tmo_hdr->timer; > > + if (odp_likely(tim != ODP_TIMER_INVALID && tmo_hdr->gc == > > tim->gc)) > > + return tim; > > + else > > + return ODP_TIMER_INVALID; > > } > > > > -uint64_t odp_timer_current_tick(odp_timer_t timer_hdl) > > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo_buf) > > { > > - uint32_t id; > > - > > - id = timer_hdl - 1; > > - return odp_timer.timer[id].cur_tick; > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > > + return tmo_hdr->expiration; > > } > > > > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf) > > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo_buf) > > { > > - return (odp_timeout_t) buf; > > + odp_timeout_hdr_t *tmo_hdr = > > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > > + return tmo_hdr->user_ptr; > > } > > > > -uint64_t odp_timeout_tick(odp_timeout_t tmo) > > +int odp_timer_init_global(void) > > { > > - odp_timeout_hdr_t *tmo_hdr = odp_timeout_hdr(tmo); > > - return tmo_hdr->meta.tmo_tick; > > + return 0; > > } > > diff --git a/platform/linux-generic/priority_queue.c > > b/platform/linux-generic/priority_queue.c > > new file mode 100644 > > index 0000000..ba4ba0e > > --- /dev/null > > +++ b/platform/linux-generic/priority_queue.c > > @@ -0,0 +1,289 @@ > > +#define NDEBUG /* Enabled by default by ODP build system */ > > +#include <assert.h> > > +#include <unistd.h> > > +#include <stdlib.h> > > +#include <string.h> > > +#include <strings.h> > > +#include <odp_hints.h> > > +#include <odp_align.h> > > +#include <odp_debug.h> > > + > > +#include "priority_queue.h" > > + > > + > > +#define NUM_CHILDREN 4 > > +#define CHILD(n) (NUM_CHILDREN * (n) + 1) > > +#define PARENT(n) (((n) - 1) / NUM_CHILDREN) > > + > > +/* Internal nodes in the array */ > > +typedef struct heap_node { > > + pq_element *elem; > > + /* Copy of elem->prio so we avoid unnecessary dereferencing */ > > + pq_priority_t prio; > > +} heap_node; > > + > > +static void pq_assert_heap(priority_queue *this); > > + > > +#define ALIGNMENT(p) (1U << ((unsigned)ffs((int)p) - 1U)) > > + > > +void priority_queue_con(priority_queue *this, uint32_t _max_elems) > > +{ > > + this->max_elems = _max_elems; > > + this->reg_elems = 0; > > + this->num_elems = 0; > > + this->org_ptr = malloc((_max_elems + 64 / sizeof(heap_node)) * > > + sizeof(heap_node)); > > + if (odp_unlikely(this->org_ptr == NULL)) { > > + ODP_ERR("malloc failed\n"); > > + abort(); > > + } > > + this->heap = this->org_ptr; > > + assert((size_t)&this->heap[1] % 8 == 0); > > + /* Increment base address until first child (index 1) is > > cache line */ > > + /* aligned and thus all children (e.g. index 1-4) stored > > in the */ > > + /* same cache line. We are not interested in the alignment > > of */ > > + /* heap[0] as this is a lone node */ > > + while ((size_t)&this->heap[1] % ODP_CACHE_LINE_SIZE != 0) { > > + /* Cast to ptr to struct member with the greatest > > alignment */ > > + /* requirement */ > > + this->heap = (heap_node *)((pq_priority_t > > *)this->heap + 1); > > + } > > +#if 0 > > + printf("Alignment of heap[1]=%u\n", > > ALIGNMENT((size_t)&heap[1])); > > + printf("Alignment of heap[CHILD(1)]=%u\n", > > + ALIGNMENT((size_t)&heap[CHILD(1)])); > > +#endif > > + pq_assert_heap(this); > > +} > > + > > +void priority_queue_des(priority_queue *this) > > +{ > > + pq_assert_heap(this); > > + free(this->org_ptr); > > +} > > + > > +#ifndef NDEBUG > > +static uint32_t > > +pq_assert_elem(priority_queue *this, uint32_t index, bool recurse) > > +{ > > + uint32_t num = 1; > > + const pq_element *elem = this->heap[index].elem; > > + assert(elem->index == index); > > + assert(elem->prio == this->heap[index].prio); > > + uint32_t child = CHILD(index); > > + uint32_t i; > > + for (i = 0; i < NUM_CHILDREN; i++, child++) { > > + if (valid_index(this, child)) { > > + assert(this->heap[child].elem != NULL); > > + assert(this->heap[child].prio >= elem->prio); > > + if (recurse) > > + num += pq_assert_elem(this, child, > > recurse); > > + } > > + } > > + return num; > > +} > > +#endif > > + > > +static void > > +pq_assert_heap(priority_queue *this) > > +{ > > + (void)this; > > +#ifndef NDEBUG > > + uint32_t num = 0; > > + if (odp_likely(this->num_elems != 0)) { > > + assert(this->heap[0].elem != NULL); > > + num += pq_assert_elem(this, 0, true); > > + } > > + assert(num == this->num_elems); > > + unsigned i; > > + for (i = 0; i < this->num_elems; i++) { > > + assert(this->heap[i].elem != NULL); > > + assert(this->heap[i].prio != INVALID_PRIORITY); > > + } > > +#endif > > +} > > + > > +/* Bubble up to proper position */ > > +void > > +pq_bubble_up(priority_queue *this, pq_element *elem) > > +{ > > + assert(this->heap[elem->index].elem == elem); > > + assert(this->heap[elem->index].prio == elem->prio); > > + uint32_t current = elem->index; > > + pq_priority_t prio = elem->prio; > > + assert(current == 0 || this->heap[PARENT(current)].elem != > > NULL); > > + /* Move up into proper position */ > > + while (current != 0 && this->heap[PARENT(current)].prio > > > prio) { > > + uint32_t parent = PARENT(current); > > + assert(this->heap[parent].elem != NULL); > > + /* Swap current with parent */ > > + /* 1) Move parent down */ > > + this->heap[current].elem = this->heap[parent].elem; > > + this->heap[current].prio = this->heap[parent].prio; > > + this->heap[current].elem->index = current; > > + /* 2) Move current up to parent */ > > + this->heap[parent].elem = elem; > > + this->heap[parent].prio = prio; > > + this->heap[parent].elem->index = parent; > > + /* Continue moving elem until it is in the right > > place */ > > + current = parent; > > + } > > + pq_assert_heap(this); > > +} > > + > > +/* Find the smallest child that is smaller than the specified > > priority */ > > +/* TODO very hot function! */ > > +uint32_t pq_smallest_child(priority_queue *this, > > + uint32_t index, > > + pq_priority_t val) > > +{ > > + uint32_t smallest = index; > > + uint32_t child = CHILD(index); > > +#if 1 > > + /* Unroll loop when all children exist */ > > + if (odp_likely(valid_index(this, child + 3))) { > > + if (this->heap[child + 0].prio < val) > > + val = this->heap[smallest = child + 0].prio; > > + if (this->heap[child + 1].prio < val) > > + val = this->heap[smallest = child + 1].prio; > > + if (this->heap[child + 2].prio < val) > > + val = this->heap[smallest = child + 2].prio; > > + if (this->heap[child + 3].prio < val) > > + val = this->heap[smallest = child + 3].prio; > > + return smallest; > > + } > > +#endif > > + uint32_t i; > > + for (i = 0; i < NUM_CHILDREN; i++) { > > + if (odp_unlikely(!valid_index(this, child + i))) > > + break; > > + if (this->heap[child + i].prio < val) { > > + smallest = child + i; > > + val = this->heap[smallest].prio; > > + } > > + } > > + return smallest; > > +} > > + > > +/* TODO very hot function, can it be optimised? */ > > +void > > +pq_bubble_down(priority_queue *this, pq_element *elem) > > +{ > > + assert(this->heap[elem->index].elem == elem); > > + assert(this->heap[elem->index].prio == elem->prio); > > + uint32_t current = elem->index; > > + pq_priority_t prio = elem->prio; > > + for (;;) { > > + uint32_t child = pq_smallest_child(this, current, > > prio); > > + if (current == child) { > > + /* No smaller child, we are done */ > > + pq_assert_heap(this); > > + return; > > + } > > + /* Element larger than smaller child, must move > > down */ > > + assert(this->heap[child].elem != NULL); > > + /* 1) Move child up to current */ > > + this->heap[current].elem = this->heap[child].elem; > > + this->heap[current].prio = this->heap[child].prio; > > + /* 2) Move current down to child */ > > + this->heap[child].elem = elem; > > + this->heap[child].prio = prio; > > + this->heap[child].elem->index = child; > > + > > + this->heap[current].elem->index = current; /* > > cache misses! */ > > + /* Continue moving element until it is in the > > right place */ > > + current = child; > > + } > > +} > > + > > +bool > > +pq_register_element(priority_queue *this, pq_element *elem) > > +{ > > + if (odp_likely(this->reg_elems < this->max_elems)) { > > + elem->pq = this; > > + this->reg_elems++; > > + return true; > > + } > > + return false; > > +} > > + > > +void > > +pq_unregister_element(priority_queue *this, pq_element *elem) > > +{ > > + assert(elem->pq == this); > > + if (is_active(elem)) > > + pq_deactivate_element(this, elem); > > + elem->pq = NULL; > > + this->reg_elems--; > > +} > > + > > +void > > +pq_activate_element(priority_queue *this, pq_element *elem, > > pq_priority_t prio) > > +{ > > + assert(elem->pq == this); > > + /* Insert element at end */ > > + uint32_t index = this->num_elems++; > > + this->heap[index].elem = elem; > > + this->heap[index].prio = prio; > > + elem->index = index; > > + elem->prio = prio; > > + pq_bubble_up(this, elem); > > +} > > + > > +void > > +pq_deactivate_element(priority_queue *this, pq_element *elem) > > +{ > > + assert(elem->pq == this); > > + if (odp_likely(is_active(elem))) { > > + /* Swap element with last element */ > > + uint32_t current = elem->index; > > + uint32_t last = --this->num_elems; > > + if (odp_likely(last != current)) { > > + /* Move last element to current */ > > + this->heap[current].elem = > > this->heap[last].elem; > > + this->heap[current].prio = > > this->heap[last].prio; > > + this->heap[current].elem->index = current; > > + /* Bubble down old 'last' element to its > > proper place*/ > > + if (this->heap[current].prio < elem->prio) > > + pq_bubble_up(this, > > this->heap[current].elem); > > + else > > + pq_bubble_down(this, > > this->heap[current].elem); > > + } > > + elem->index = INVALID_INDEX; > > + pq_assert_heap(this); > > + } > > +} > > + > > +void > > +pq_reset_element(priority_queue *this, pq_element *elem, > > pq_priority_t prio) > > +{ > > + assert(prio != INVALID_PRIORITY); > > + if (odp_likely(is_active(elem))) { > > + assert(prio >= elem->prio); > > + elem->prio = prio; > > + this->heap[elem->index].prio = prio;/* cache > > misses here! */ > > + pq_bubble_down(this, elem); > > + pq_assert_heap(this); > > + } else { > > + pq_activate_element(this, elem, prio); > > + } > > +} > > + > > +pq_priority_t pq_first_priority(const priority_queue *this) > > +{ > > + return this->num_elems != 0 ? this->heap[0].prio : > > INVALID_PRIORITY; > > +} > > + > > +pq_element * > > +pq_release_element(priority_queue *this, pq_priority_t threshold) > > +{ > > + if (odp_likely(this->num_elems != 0 && > > + this->heap[0].prio <= threshold)) { > > + pq_element *elem = this->heap[0].elem; > > + /* Remove element from heap */ > > + pq_deactivate_element(this, elem); > > + assert(elem->prio <= threshold); > > + return elem; > > + } > > + return NULL; > > +} > > diff --git a/platform/linux-generic/priority_queue.h > > b/platform/linux-generic/priority_queue.h > > new file mode 100644 > > index 0000000..c461590 > > --- /dev/null > > +++ b/platform/linux-generic/priority_queue.h > > @@ -0,0 +1,107 @@ > > +#ifndef _PRIORITY_QUEUE_H > > +#define _PRIORITY_QUEUE_H > > + > > +#include <assert.h> > > +#include <stddef.h> > > +#include <stdint.h> > > +#include <stdbool.h> > > + > > +#define INVALID_INDEX ~0U > > +#define INVALID_PRIORITY ((pq_priority_t)~0ULL) > > + > > +typedef uint64_t pq_priority_t; > > + > > +struct heap_node; > > + > > +typedef struct priority_queue { > > + uint32_t max_elems;/* Number of elements in heap */ > > + /* Number of registered elements (active + inactive) */ > > + uint32_t reg_elems; > > + uint32_t num_elems;/* Number of active elements */ > > + struct heap_node *heap; > > + struct heap_node *org_ptr; > > +} priority_queue; > > + > > +/* The user gets a pointer to this structure */ > > +typedef struct { > > + /* Set when pq_element registered with priority queue */ > > + priority_queue *pq; > > + uint32_t index;/* Index into heap array */ > > + pq_priority_t prio; > > +} pq_element; > > + > > +/*** Operations on pq_element ***/ > > + > > +static inline void pq_element_con(pq_element *this) > > +{ > > + this->pq = NULL; > > + this->index = INVALID_INDEX; > > + this->prio = 0U; > > +} > > + > > +static inline void pq_element_des(pq_element *this) > > +{ > > + (void)this; > > + assert(this->index == INVALID_INDEX); > > +} > > + > > +static inline priority_queue *get_pq(const pq_element *this) > > +{ > > + return this->pq; > > +} > > + > > +static inline pq_priority_t get_prio(const pq_element *this) > > +{ > > + return this->prio; > > +} > > + > > +static inline uint32_t get_index(const pq_element *this) > > +{ > > + return this->index; > > +} > > + > > +static inline bool is_active(const pq_element *this) > > +{ > > + return this->index != INVALID_INDEX; > > +} > > + > > +/*** Operations on priority_queue ***/ > > + > > +extern uint32_t pq_smallest_child(priority_queue *, uint32_t, > > pq_priority_t); > > +extern void pq_bubble_down(priority_queue *, pq_element *); > > +extern void pq_bubble_up(priority_queue *, pq_element *); > > + > > +static inline bool valid_index(priority_queue *this, uint32_t idx) > > +{ > > + return idx < this->num_elems; > > +} > > + > > +extern void priority_queue_con(priority_queue *, uint32_t > > _max_elems); > > +extern void priority_queue_des(priority_queue *); > > + > > +/* Register pq_element with priority queue */ > > +/* Return false if priority queue full */ > > +extern bool pq_register_element(priority_queue *, pq_element *); > > + > > +/* Activate and add pq_element to priority queue */ > > +/* Element must be disarmed */ > > +extern void pq_activate_element(priority_queue *, pq_element *, > > pq_priority_t); > > + > > +/* Reset (increase) priority for pq_element */ > > +/* Element may be active or inactive (released) */ > > +extern void pq_reset_element(priority_queue *, pq_element *, > > pq_priority_t); > > + > > +/* Deactivate and remove element from priority queue */ > > +/* Element may be active or inactive (released) */ > > +extern void pq_deactivate_element(priority_queue *, pq_element *); > > + > > +/* Unregister pq_element */ > > +extern void pq_unregister_element(priority_queue *, pq_element *); > > + > > +/* Return priority of first element (lowest numerical value) */ > > +extern pq_priority_t pq_first_priority(const priority_queue *); > > + > > +/* Deactivate and return first element if it's prio is <= > > threshold */ > > +extern pq_element *pq_release_element(priority_queue *, > > pq_priority_t thresh); > > + > > +#endif /* _PRIORITY_QUEUE_H */ > > diff --git a/test/api_test/odp_timer_ping.c > > b/test/api_test/odp_timer_ping.c > > index c1cc255..c4332e3 100644 > > --- a/test/api_test/odp_timer_ping.c > > +++ b/test/api_test/odp_timer_ping.c > > @@ -20,6 +20,7 @@ > > * Otherwise timeout may happen bcz of slow nw speed > > */ > > > > +#include <stdlib.h> > > #include <unistd.h> > > #include <fcntl.h> > > #include <errno.h> > > @@ -43,7 +44,8 @@ > > #define PING_CNT 10 > > #define PING_THRD 2 /* Send and Rx Ping thread */ > > > > -static odp_timer_t test_timer_ping; > > +static odp_timer_pool_t tp; > > +static odp_timer_t test_timer_ping = ODP_TIMER_INVALID; > > static odp_timer_tmo_t test_ping_tmo; > > > > #define PKTSIZE 64 > > @@ -123,15 +125,7 @@ static int listen_to_pingack(void) > > (socklen_t *)&len); > > if (bytes > 0) { > > /* pkt rxvd therefore cancel the > > timeout */ > > - if > > (odp_timer_cancel_tmo(test_timer_ping, > > - test_ping_tmo) != 0) { > > - ODP_ERR("cancel_tmo failed > > ..exiting listner thread\n"); > > - /* avoid exiting from here > > even if tmo > > - * failed for current ping, > > - * allow subsequent > > ping_rx request */ > > - err = -1; > > - > > - } > > + odp_timer_cancel(test_timer_ping); > > /* cruel bad hack used for sender, > > listner ipc.. > > * euwww.. FIXME .. > > */ > > @@ -153,7 +147,7 @@ static int send_ping_request(struct > > sockaddr_in *addr) > > int sd, cnt = 1; > > struct packet pckt; > > > > - uint64_t tick; > > + odp_timer_tick_t tick; > > odp_queue_t queue; > > odp_buffer_t buf; > > > > @@ -179,6 +173,12 @@ static int send_ping_request(struct > > sockaddr_in *addr) > > > > /* get the ping queue */ > > queue = odp_queue_lookup("ping_timer_queue"); > > + test_timer_ping = odp_timer_alloc(tp, queue, NULL); > > + if (test_timer_ping == ODP_TIMER_INVALID) { > > + ODP_ERR("Failed to allocate timer.\n"); > > + err = -1; > > + goto err; > > + } > > > > for (i = 0; i < PING_CNT; i++) { > > /* prepare icmp pkt */ > > @@ -204,12 +204,10 @@ static int send_ping_request(struct > > sockaddr_in *addr) > > printf(" icmp_sent msg_cnt %d\n", i); > > > > /* arm the timer */ > > - tick = odp_timer_current_tick(test_timer_ping); > > + tick = odp_timer_current_tick(tp); > > > > tick += 1000; > > - test_ping_tmo = > > odp_timer_absolute_tmo(test_timer_ping, tick, > > - queue, > > - ODP_BUFFER_INVALID); > > + odp_timer_set_abs(test_timer_ping, tick); > > /* wait for timeout event */ > > while ((buf = odp_queue_deq(queue)) == > > ODP_BUFFER_INVALID) { > > /* flag true means ack rxvd.. a cruel hack > > as I > > @@ -225,16 +223,24 @@ static int send_ping_request(struct > > sockaddr_in *addr) > > } > > } > > > > - /* free tmo_buf for timeout case */ > > - if (buf != ODP_BUFFER_INVALID) { > > + switch (odp_timer_tmo_status(buf)) { > > + case ODP_TMO_FRESH: > > ODP_DBG(" timeout msg_cnt [%i] \n", i); > > /* so to avoid seg fault commented */ > > - odp_buffer_free(buf); > > err = -1; > > + break; > > + case ODP_TMO_STALE: > > + /* Ignore stale timeouts */ > > + break; > > + case ODP_TMO_ORPHAN: > > + ODP_ERR("Received orphaned timeout!\n"); > > + abort(); > > } > > } > > > > err: > > + if (test_timer_ping != ODP_TIMER_INVALID) > > + odp_timer_free(test_timer_ping); > > return err; > > } > > > > @@ -335,7 +341,7 @@ int main(int argc ODP_UNUSED, char *argv[] > > ODP_UNUSED) > > ODP_CACHE_LINE_SIZE, > > ODP_BUFFER_TYPE_RAW); > > if (pool == ODP_BUFFER_POOL_INVALID) { > > - ODP_ERR("Pool create failed.\n"); > > + ODP_ERR("Buffer pool create failed.\n"); > > return -1; > > } > > > > @@ -350,8 +356,18 @@ int main(int argc ODP_UNUSED, char *argv[] > > ODP_UNUSED) > > return -1; > > } > > > > - test_timer_ping = odp_timer_create("ping_timer", pool, > > - 1000000, 1000000, > > 1000000000000UL); > > + /* > > + * Create timer pool > > + */ > > + tp = odp_timer_pool_create("timer_pool", pool, > > + 1000000U, /* 1 millisecond */ > > + 10000000000U, /* 10 seconds */ > > + 1, false, ODP_CLOCK_DEFAULT); > > + if (tp == ODP_TIMER_POOL_INVALID) { > > + ODP_ERR("Timer pool create failed.\n"); > > + return -1; > > + } > > + > > odp_shm_print_all(); > > > > pingarg.thrdarg.testcase = ODP_TIMER_PING_TEST; > > -- > > 1.9.1 > > > > > > > > > >_______________________________________________ > >lng-odp mailing list > >lng-odp@lists.linaro.org > >http://lists.linaro.org/mailman/listinfo/lng-odp > > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp
platform/linux-generic/priority_queue.h Should be moved and renamed to platform/linux-generic/include/odp_priority_queue_internal.h or something similar. All ODP headers have either odp_ or odph_ prefix so that it easy to see with are ODP vs. c lib/linux/posix includes. -Petri
Maxim, Thanks for reviewing. The priority queue is required by the timer implementation but currently not by any other ODP module. It does not have any public API. Why should it be in a separate patch? All the functions in the priority queue are used by the timer implementation so implicitly tested. Because of the lack of public API, we cannot have any ODP-level test program. The timer test program uses the timer API. Do you want some other (more complete) example? I can provide that (the current example/timer/odp_timer_test is more of a test program than an example of using the timer API). But I don't think that should stop this patch from being merged. I will continue to work on a complete timer example and provide that in a separate patch. The // comments are inside a bigger /* */ comment (that's why checkpatch etc does not complain) in order to comment on specific lines of the example. I cannot use /* comments here as they are not recursive. The alternative is to remove the example usage from the header file altogether (but I don't think that is a good idea, it should be easy to find). People might not look at a separate example file when attempting to use this API. Is there some better way of describing example usage in the header file? -- Ola On 3 September 2014 23:01, Maxim Uvarov <maxim.uvarov@linaro.org> wrote: > On 09/03/2014 10:48 PM, Ola Liljedahl wrote: > >> Any volunteers for reviewing this patch? >> >> -- Ola >> >> >> Hello Ola, > > it will be really useful if it's possible to split this big patch. At > least for: > 1. timer api + test example > 2. priority queue > > priority queue also need some test case. > > Also we talked about your patch with Mike and Anders yesterday. You > provided example in doxygen. But it will > be great to move that example to source code. In that case we can check > that it always valid and works. > > A little bit confusing with C++ style comments "//" instead of C style "/* > .... */". But patchwork does not complain about > that. I think we should be consistent with comments too. > > Best regards, > Maxim. > > >> On 2 September 2014 17:26, Ola Liljedahl <ola.liljedahl@linaro.org >> <mailto:ola.liljedahl@linaro.org>> wrote: >> >> Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org >> <mailto:ola.liljedahl@linaro.org>> >> --- >> (This document/code contribution attached is provided under the >> terms of agreement LES-LTM-21309) >> New timer API and corresponding SW implementation for linux-generic. >> Read more about use cases and usage here: >> https://docs.google.com/a/linaro.org/document/d/1bfY_ >> J8ecLJPsFTmYftb0NVmGnB9qkEc_NpcJ87yfaD8/edit# >> >> example/timer/odp_timer_test.c | 93 ++- >> platform/linux-generic/Makefile.am | 1 + >> platform/linux-generic/include/api/odp_timer.h | 478 >> ++++++++++-- >> .../linux-generic/include/odp_timer_internal.h | 71 +- >> platform/linux-generic/odp_timer.c | 823 >> +++++++++++++-------- >> platform/linux-generic/priority_queue.c | 289 ++++++++ >> platform/linux-generic/priority_queue.h | 107 +++ >> test/api_test/odp_timer_ping.c | 58 +- >> 8 files changed, 1455 insertions(+), 465 deletions(-) >> create mode 100644 platform/linux-generic/priority_queue.c >> create mode 100644 platform/linux-generic/priority_queue.h >> >> diff --git a/example/timer/odp_timer_test.c >> b/example/timer/odp_timer_test.c >> index bf1d7df..0b4fedf 100644 >> --- a/example/timer/odp_timer_test.c >> +++ b/example/timer/odp_timer_test.c >> @@ -35,7 +35,6 @@ >> typedef struct { >> int core_count; /**< Core count*/ >> int resolution_us; /**< Timeout resolution in usec*/ >> - int min_us; /**< Minimum timeout in usec*/ >> int max_us; /**< Maximum timeout in usec*/ >> int period_us; /**< Timeout period in usec*/ >> int tmo_count; /**< Timeout count*/ >> @@ -45,18 +44,16 @@ typedef struct { >> /** @private Barrier for test synchronisation */ >> static odp_barrier_t test_barrier; >> >> -/** @private Timer handle*/ >> -static odp_timer_t test_timer; >> +/** @private Timer pool handle*/ >> +static odp_timer_pool_t tp; >> >> >> /** @private test timeout */ >> static void test_abs_timeouts(int thr, test_args_t *args) >> { >> - uint64_t tick; >> - uint64_t period; >> + odp_timer_tick_t period; >> uint64_t period_ns; >> odp_queue_t queue; >> - odp_buffer_t buf; >> int num; >> >> ODP_DBG(" [%i] test_timeouts\n", thr); >> @@ -64,37 +61,51 @@ static void test_abs_timeouts(int thr, >> test_args_t *args) >> queue = odp_queue_lookup("timer_queue"); >> >> period_ns = args->period_us*USEC; >> - period = odp_timer_ns_to_tick(test_timer, period_ns); >> + period = odp_timer_ns_to_tick(tp, period_ns); >> >> ODP_DBG(" [%i] period %"PRIu64" ticks, %"PRIu64" ns\n", thr, >> period, period_ns); >> >> - tick = odp_timer_current_tick(test_timer); >> + ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, >> + odp_timer_current_tick(tp)); >> >> - ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick); >> - >> - tick += period; >> - >> - if (odp_timer_absolute_tmo(test_timer, tick, queue, >> ODP_BUFFER_INVALID) >> - == ODP_TIMER_TMO_INVALID){ >> - ODP_DBG("Timeout request failed\n"); >> + odp_timer_t test_timer; >> + test_timer = odp_timer_alloc(tp, queue, NULL); >> + if (test_timer == ODP_TIMER_INVALID) { >> + ODP_ERR("Failed to allocate timer\n"); >> return; >> } >> + odp_timer_set_rel(test_timer, period); >> >> num = args->tmo_count; >> >> while (1) { >> - odp_timeout_t tmo; >> - >> - buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); >> + /* Local variables because received timeouts may not >> + originate from timer we created above */ >> + odp_timer_tmo_t tmo; >> + odp_timer_tick_t tick; >> + odp_timer_t hdl; >> + >> + /* Get the next ready buffer/timeout */ >> + tmo = odp_schedule_one(&queue, ODP_SCHED_WAIT); >> + switch (odp_timer_tmo_status(tmo)) { >> + case ODP_TMO_FRESH: >> + break; >> + case ODP_TMO_STALE: >> + ODP_DBG("[%i] Stale timeout received\n", thr); >> + break; >> + case ODP_TMO_ORPHAN: >> + ODP_DBG("[%i] Orphaned timeout received\n", >> + thr); >> + odp_buffer_free(tmo); >> + continue; >> + } >> >> - tmo = odp_timeout_from_buffer(buf); >> - tick = odp_timeout_tick(tmo); >> + tick = odp_timer_get_expiry(tmo); >> + hdl = odp_timer_get_handle(tmo); >> >> ODP_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, >> tick); >> >> - odp_buffer_free(buf); >> - >> num--; >> >> if (num == 0) >> @@ -102,10 +113,13 @@ static void test_abs_timeouts(int thr, >> test_args_t *args) >> >> tick += period; >> >> - odp_timer_absolute_tmo(test_timer, tick, >> - queue, ODP_BUFFER_INVALID); >> + if (hdl != ODP_TIMER_INVALID) >> + odp_timer_set_abs(hdl, tick); >> } >> >> + odp_timer_cancel(test_timer); >> + odp_timer_free(test_timer); >> + >> if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) >> odp_schedule_release_atomic(); >> } >> @@ -159,7 +173,6 @@ static void print_usage(void) >> printf("Options:\n"); >> printf(" -c, --count <number> core count, core IDs >> start from 1\n"); >> printf(" -r, --resolution <us> timeout resolution in >> usec\n"); >> - printf(" -m, --min <us> minimum timeout in usec\n"); >> printf(" -x, --max <us> maximum timeout in usec\n"); >> printf(" -p, --period <us> timeout period in usec\n"); >> printf(" -t, --timeouts <count> timeout repeat count\n"); >> @@ -183,7 +196,6 @@ static void parse_args(int argc, char *argv[], >> test_args_t *args) >> static struct option longopts[] = { >> {"count", required_argument, NULL, 'c'}, >> {"resolution", required_argument, NULL, 'r'}, >> - {"min", required_argument, NULL, 'm'}, >> {"max", required_argument, NULL, 'x'}, >> {"period", required_argument, NULL, 'p'}, >> {"timeouts", required_argument, NULL, 't'}, >> @@ -194,14 +206,13 @@ static void parse_args(int argc, char >> *argv[], test_args_t *args) >> /* defaults */ >> args->core_count = 0; /* all cores */ >> args->resolution_us = 10000; >> - args->min_us = args->resolution_us; >> args->max_us = 100000000; >> args->period_us = 1000000; >> args->tmo_count = 30; >> >> while (1) { >> opt = getopt_long(argc, argv, "+c:r:m:x:p:t:h", >> - longopts, &long_index); >> + longopts, &long_index); >> >> if (opt == -1) >> break; /* No more options */ >> @@ -213,9 +224,6 @@ static void parse_args(int argc, char *argv[], >> test_args_t *args) >> case 'r': >> args->resolution_us = atoi(optarg); >> break; >> - case 'm': >> - args->min_us = atoi(optarg); >> - break; >> case 'x': >> args->max_us = atoi(optarg); >> break; >> @@ -299,7 +307,6 @@ int main(int argc, char *argv[]) >> >> printf("first core: %i\n", first_core); >> printf("resolution: %i usec\n", args.resolution_us); >> - printf("min timeout: %i usec\n", args.min_us); >> printf("max timeout: %i usec\n", args.max_us); >> printf("period: %i usec\n", args.period_us); >> printf("timeouts: %i\n", args.tmo_count); >> @@ -323,10 +330,23 @@ int main(int argc, char *argv[]) >> ODP_BUFFER_TYPE_TIMEOUT); >> >> if (pool == ODP_BUFFER_POOL_INVALID) { >> - ODP_ERR("Pool create failed.\n"); >> + ODP_ERR("Buffer pool create failed.\n"); >> return -1; >> } >> >> + tp = odp_timer_pool_create("timer_pool", pool, >> + args.resolution_us*USEC, >> + args.max_us*USEC, >> + num_workers, /* One timer per >> worker */ >> + true, >> + ODP_CLOCK_DEFAULT); >> + if (tp == ODP_TIMER_POOL_INVALID) { >> + ODP_ERR("Timer pool create failed.\n"); >> + return -1; >> + } >> + >> + odp_shm_print_all(); >> + >> /* >> * Create a queue for timer test >> */ >> @@ -342,13 +362,6 @@ int main(int argc, char *argv[]) >> return -1; >> } >> >> - test_timer = odp_timer_create("test_timer", pool, >> - args.resolution_us*USEC, >> - args.min_us*USEC, >> - args.max_us*USEC); >> - >> - odp_shm_print_all(); >> - >> printf("CPU freq %"PRIu64" hz\n", odp_sys_cpu_hz()); >> printf("Cycles vs nanoseconds:\n"); >> ns = 0; >> diff --git a/platform/linux-generic/Makefile.am >> b/platform/linux-generic/Makefile.am >> index f4dfdc1..e2bc1a7 100644 >> --- a/platform/linux-generic/Makefile.am >> +++ b/platform/linux-generic/Makefile.am >> @@ -72,4 +72,5 @@ __LIB__libodp_la_SOURCES = \ >> odp_thread.c \ >> odp_ticketlock.c \ >> odp_time.c \ >> + priority_queue.c \ >> odp_timer.c >> diff --git a/platform/linux-generic/include/api/odp_timer.h >> b/platform/linux-generic/include/api/odp_timer.h >> index 01db839..e5f961c 100644 >> --- a/platform/linux-generic/include/api/odp_timer.h >> +++ b/platform/linux-generic/include/api/odp_timer.h >> @@ -1,4 +1,4 @@ >> -/* Copyright (c) 2013, Linaro Limited >> +/* Copyright (c) 2014, Linaro Limited >> * All rights reserved. >> * >> * SPDX-License-Identifier: BSD-3-Clause >> @@ -8,7 +8,175 @@ >> /** >> * @file >> * >> - * ODP timer >> + * ODP timer service >> + * >> + >> +//Example #1 Retransmission timer (e.g. for reliable connections) >> + >> +//Create timer pool for reliable connections >> +#define SEC 1000000000ULL //1s expressed in nanoseconds >> +odp_timer_pool_t tcp_tpid = >> + odp_timer_pool_create("TCP", >> + buffer_pool, >> + 1000000,//resolution 1ms >> + 7200 * SEC,//max tmo length 2hours >> + 40000,//num_timers >> + true,//shared >> + ODP_CLOCK_DEFAULT >> + ); >> +if (tcp_tpid == ODP_TIMER_POOL_INVALID) >> +{ >> + //Failed to create timer pool => fatal error >> +} >> + >> + >> +//Setting up a new connection >> +//Allocate retransmission timeout (identical for supervision timeout) >> +//The user pointer points back to the connection context >> +conn->ret_tim = odp_timer_alloc(tcp_tpid, queue, conn); >> +//Check if all resources were successfully allocated >> +if (conn->ret_tim == ODP_TIMER_INVALID) >> +{ >> + //Failed to allocate all resources for connection => tear down >> + //Destroy timeout >> + odp_timer_free(conn->ret_tim); >> + //Tear down connection >> + ... >> + return false; >> +} >> +//All necessary resources successfully allocated >> +//Compute initial retransmission length in timer ticks >> +conn->ret_len = odp_timer_ns_to_tick(tcp_tpid, 3 * SEC);//Per RFC1122 >> +//Arm the timer >> +odp_timer_set_rel(conn->ret_tim, conn->ret_len); >> +return true; >> + >> + >> +//A packet for the connection has just been transmitted >> +//Reset the retransmission timer >> +odp_timer_set_rel(conn->ret_tim, conn->ret_len); >> + >> + >> +//A retransmission timeout for the connection has been received >> +//Check if timeout is fresh or stale, for stale timeouts we need >> to reset the >> +//timer >> +switch (odp_timer_tmo_status(tmo)) >> +{ >> + case ODP_TMO_FRESH : >> + //Fresh timeout, last transmitted packet not acked in time => >> + retransmit >> + //Get connection from timeout event >> + conn = odp_timer_get_userptr(tmo); >> + //Retransmit last packet (e.g. TCP segment) >> + ... >> + //Re-arm timer using original delta value >> + odp_timer_set_rel(conn->ret_tim, conn->ret_len); >> + break; >> + case ODP_TMO_STALE : >> + break;//Do nothing >> + case ODP_TMO_ORPHAN : >> + odp_free_buffer(tmo); >> + return;//Get out of here >> +} >> + >> + >> +//Example #2 Periodic tick >> + >> +//Create timer pool for periodic ticks >> +odp_timer_pool_t per_tpid = >> + odp_timer_pool_create("periodic-tick", >> + buffer_pool, >> + 1,//resolution 1ns >> + 1000000000,//maximum timeout length 1s >> + 10,//num_timers >> + false,//not shared >> + ODP_CLOCK_DEFAULT >> + ); >> +if (per_tpid == ODP_TIMER_POOL_INVALID) >> +{ >> + //Failed to create timer pool => fatal error >> +} >> + >> + >> +//Allocate periodic timer >> +tim_1733 = odp_timer_alloc(per_tpid, queue, NULL); >> +//Check if all resources were successfully allocated >> +if (tim_1733 == ODP_TIMER_INVALID) >> +{ >> + //Failed to allocate all resources => tear down >> + //Destroy timeout >> + odp_timer_free(tim_1733); >> + //Tear down other state >> + ... >> + return false; >> +} >> +//All necessary resources successfully allocated >> +//Compute tick period in timer ticks >> +period_1733 = odp_timer_ns_to_tick(per_tpid, 1000000000U / >> 1733U);//1733Hz >> +//Compute when next tick should expire >> +next_1733 = odp_timer_current_tick(per_tpid) + period_1733; >> +//Arm the periodic timer >> +odp_timer_set_abs(tim_1733, next_1733); >> +return true; >> + >> + >> + >> +//A periodic timer timeout has been received >> +//Must call odp_timer_tmo_status() on timeout! >> +ret = odp_timer_tmo_status(tmo); >> +//We expect the timeout is fresh since we are not calling set or >> cancel on >> +//active or expired timers in this example >> +assert(ret == ODP_TMO_FRESH); >> +//Do processing driven by timeout *before* >> +... >> +do { >> + //Compute when the timer should expire next >> + next_1733 += period_1733; >> + //Check that this is in the future >> + if (likely(next_1733 > odp_timer_current_tick(per_tpid)) >> + break;//Yes, done >> + //Else we missed a timeout >> + //Optionally attempt some recovery and/or logging of the >> problem >> + ... >> +} while (0); >> +//Re-arm periodic timer >> +odp_timer_set_abs(tim_1733, next_1733); >> +//Or do processing driven by timeout *after* >> +... >> +return; >> + >> +//Example #3 Tear down of flow >> +//ctx points to flow context data structure owned by application >> +//Free the timer, cancelling any timeout >> +odp_timer_free(ctx->timer);//Any enqueued timeout will be made >> invalid >> +//Continue tearing down and eventually freeing context >> +... >> +return; >> + >> +//A timeout has been received, check status >> +switch (odp_timer_tmo_status(tmo)) >> +{ >> + case ODP_TMO_FRESH : >> + //A flow has timed out, tear it down >> + //Find flow context from timeout >> + ctx = (context *)odp_timer_get_userptr(tmo); >> + //Free the supervision timer, any enqueued timeout will remain >> + odp_timer_free(ctx->tim); >> + //Free other flow related resources >> + ... >> + //Flow torn down >> + break; >> + case ODP_TMO_STALE : >> + //A stale timeout was received, timer automatically reset >> + break; >> + case ODP_TMO_ORPHAN : >> + //Orphaned timeout (from previously torn down flow) >> + //No corresponding timer or flow context >> + //Free the timeout >> + odp_buffer_free(tmo); >> + break; >> +} >> + >> */ >> >> #ifndef ODP_TIMER_H_ >> @@ -23,139 +191,325 @@ extern "C" { >> #include <odp_buffer_pool.h> >> #include <odp_queue.h> >> >> +/** >> +* ODP timer pool handle (platform dependent) >> +*/ >> +struct odp_timer_pool; >> +typedef struct odp_timer_pool *odp_timer_pool_t; >> >> /** >> - * ODP timer handle >> + * Invalid timer pool handle (platform dependent) >> */ >> -typedef uint32_t odp_timer_t; >> +#define ODP_TIMER_POOL_INVALID NULL >> >> -/** Invalid timer */ >> -#define ODP_TIMER_INVALID 0 >> +typedef enum odp_timer_pool_clock_source_e { >> + ODP_CLOCK_DEFAULT = 0, >> + /* Platform dependent which clock sources exist beyond >> + ODP_CLOCK_DEFAULT */ >> + ODP_CLOCK_NONE = 1 >> +} odp_timer_pool_clock_source_t; >> >> +/** >> +* ODP timer handle (platform dependent) >> +*/ >> +struct odp_timer; >> +typedef struct odp_timer *odp_timer_t; >> >> /** >> - * ODP timeout handle >> + * Invalid timer handle (platform dependent) >> */ >> -typedef odp_buffer_t odp_timer_tmo_t; >> - >> -/** Invalid timeout */ >> -#define ODP_TIMER_TMO_INVALID 0 >> +#define ODP_TIMER_INVALID NULL >> >> +/** >> + * ODP timeout event handle >> + */ >> +typedef odp_buffer_t odp_timer_tmo_t; >> >> /** >> - * Timeout notification >> + * ODP timeout status >> */ >> -typedef odp_buffer_t odp_timeout_t; >> +typedef enum odp_timer_tmo_status_e { >> + ODP_TMO_FRESH, /* Timeout is fresh, process it */ >> + ODP_TMO_STALE, /* Timer reset or cancelled, do nothing */ >> + ODP_TMO_ORPHAN,/* Timer deleted, free timeout */ >> +} odp_timer_tmo_status_t; >> + >> +/** >> +* ODP tick value >> +*/ >> +typedef uint64_t odp_timer_tick_t; >> >> >> /** >> - * Create a timer >> + * Create a timer pool >> * >> - * Creates a new timer with requested properties. >> + * Create a new timer pool. >> + * odp_timer_pool_create() is typically called once or a couple >> of times during >> + * application initialisation. >> * >> * @param name Name >> - * @param pool Buffer pool for allocating timeout notifications >> + * @param buf_pool Buffer pool for allocating timers >> * @param resolution Timeout resolution in nanoseconds >> - * @param min_tmo Minimum timeout duration in nanoseconds >> - * @param max_tmo Maximum timeout duration in nanoseconds >> + * @param max_tmo Maximum relative timeout in nanoseconds >> + * @param num_timers Number of supported timers (minimum) >> + * @param shared Shared or private timer pool. >> + * Operations on shared timers will include the >> necessary >> + * mutual exclusion, operations on private timers >> may not >> + * (mutual exclusion is the responsibility of the >> caller). >> + * @param clk_src Clock source to use >> + * >> + * @return Timer pool handle if successful, otherwise >> ODP_TIMER_POOL_INVALID >> + * and errno set >> + */ >> +odp_timer_pool_t >> +odp_timer_pool_create(const char *name, >> + odp_buffer_pool_t buf_pool, >> + uint64_t resolution, >> + uint64_t max_tmo, >> + uint32_t num_timers, >> + bool shared, >> + odp_timer_pool_clock_source_t clk_src); >> + >> +/** >> + * Start a timer pool >> + * >> + * 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 >> * >> - * @return Timer handle if successful, otherwise ODP_TIMER_INVALID >> + * Destroy a timer pool, freeing all resources. >> + * All timers must have been freed. >> + * >> + * @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, >> odp_timer_tick_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); >> +odp_timer_tick_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 >> + */ >> +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid); >> + >> +/** >> + * ODP timer configurations >> */ >> -uint64_t odp_timer_resolution(odp_timer_t timer); >> + >> +typedef enum odp_timer_pool_conf_e { >> + ODP_TIMER_NAME, /* Return name of timer pool */ >> + ODP_TIMER_RESOLUTION,/* Return the timer resolution (in ns) */ >> + ODP_TIMER_MAX_TMO, /* Return the maximum supported >> timeout (in ns) */ >> + ODP_TIMER_NUM_TIMERS,/* Return number of supported timers */ >> + ODP_TIMER_SHARED /* Return shared flag */ >> +} odp_timer_pool_conf_t; >> >> /** >> - * Maximum timeout in timer ticks >> + * Query different timer pool configurations, e.g. >> + * Timer resolution in nanoseconds >> + * Maximum timeout in timer ticks >> + * Number of supported timers >> + * Shared or private timer pool >> * >> - * @param timer Timer >> + * @param tpid Timer pool identifier >> + * @param item Configuration item being queried >> * >> - * @return Maximum timeout in timer ticks >> + * @return the requested piece of information or 0 for unknown item. >> */ >> -uint64_t odp_timer_maximum_tmo(odp_timer_t timer); >> +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, >> + odp_timer_pool_conf_t item); >> >> /** >> - * Current timer tick >> + * Allocate a timer >> * >> - * @param timer Timer >> + * Create a timer (allocating all necessary resources e.g. >> timeout event) from >> + * the timer pool. >> * >> - * @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 (copied to timeouts) >> + * >> + * @return Timer handle if successful, otherwise >> ODP_TIMER_INVALID and >> + * errno set. >> + */ >> +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, >> + odp_queue_t queue, >> + void *user_ptr); >> + >> +/** >> + * Free a timer >> + * >> + * Free (destroy) a timer, freeing all associated resources (e.g. >> default >> + * timeout event). An expired and enqueued timeout event will not >> be freed. >> + * It is the responsibility of the application to free this >> timeout when it >> + * is received. >> + * >> + * @param tim Timer handle >> */ >> -uint64_t odp_timer_current_tick(odp_timer_t timer); >> +void odp_timer_free(odp_timer_t tim); >> >> /** >> - * Request timeout with an absolute timer tick >> + * Set a timer (absolute time) with a user-defined timeout buffer >> * >> - * When tick reaches tmo_tick, the timer enqueues the timeout >> notification into >> - * the destination queue. >> + * Set (arm) the timer to expire at specific time. The user-defined >> + * buffer will be enqueued when the timer expires. >> + * Arming may fail (if the timer is in state EXPIRED), an earlier >> timeout >> + * will then be received. odp_timer_tmo_status() must be used to >> check if >> + * the received timeout is valid. >> * >> - * @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. >> + * Note: any invalid parameters will be treated as programming >> errors and will >> + * cause the application to abort. >> + * Note: a timeout too near in time may be delivered immediately. >> + * Note: a timeout too far away in time (beyond max_timeout) >> might be delivered >> + * early. >> * >> - * @return Timeout handle if successful, otherwise >> ODP_TIMER_TMO_INVALID >> + * @param tim Timer >> + * @param abs_tck Expiration time in absolute timer ticks >> + * @param user_buf The buffer to use as timeout event >> */ >> -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, >> uint64_t tmo_tick, >> - odp_queue_t queue, >> odp_buffer_t buf); >> +void odp_timer_set_abs_w_buf(odp_timer_t tim, >> + odp_timer_tick_t abs_tck, >> + odp_buffer_t user_buf); >> >> /** >> - * Cancel a timeout >> + * Set a timer with an absolute expiration time >> + * >> + * Set (arm) the timer to expire at a specific time. >> + * Arming may fail (if the timer is in state EXPIRED), an earlier >> timeout >> + * will then be received. odp_timer_tmo_status() must be used to >> check if >> + * the received timeout is valid. >> * >> - * @param timer Timer >> - * @param tmo Timeout to cancel >> + * Note: any invalid parameters will be treated as programming >> errors and will >> + * cause the application to abort. >> + * Note: a timeout too near in time may be delivered immediately. >> + * Note: a timeout too far away in time (beyond max_timeout) >> might be delivered >> + * early, it will automatically be reset by odp_timer_tmo_status(). >> * >> - * @return 0 if successful >> + * @param tim Timer >> + * @param abs_tck Expiration time in absolute timer ticks >> */ >> -int odp_timer_cancel_tmo(odp_timer_t timer, odp_timer_tmo_t tmo); >> +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck); >> >> /** >> - * Convert buffer handle to timeout handle >> + * Set a timer with a relative expiration time >> * >> - * @param buf Buffer handle >> + * Set (arm) the timer to expire at a relative future time. >> + * Arming may fail (if the timer is in state EXPIRED), >> + * an earlier timeout will then be received. >> odp_timer_tmo_status() must >> + * be used to check if the received timeout is valid. >> * >> - * @return Timeout buffer handle >> + * Note: any invalid parameters will be treated as programming >> errors and will >> + * cause the application to abort. >> + * Note: a timeout too near in time may be delivered immediately. >> + * Note: a timeout too far away in time (beyond max_timeout) >> might be delivered >> + * early, it will automatically be reset by odp_timer_tmo_status(). >> + * >> + * @param tim Timer >> + * @param rel_tck Expiration time in timer ticks relative to >> current time of >> + * the timer pool the timer belongs to >> */ >> -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf); >> +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck); >> >> /** >> - * Return absolute timeout tick >> + * Cancel a timer >> + * >> + * Cancel a timer, preventing future expiration and delivery. >> + * >> + * A timer that has already expired and been enqueued for >> delivery may be >> + * impossible to cancel and will instead be delivered to the >> destination queue. >> + * Use odp_timer_tmo_status() the check whether a received >> timeout is fresh or >> + * stale (cancelled). Stale timeouts will automatically be recycled. >> * >> - * @param tmo Timeout buffer handle >> + * Note: any invalid parameters will be treated as programming >> errors and will >> + * cause the application to abort. >> * >> - * @return Absolute timeout tick >> + * @param tim Timer handle >> */ >> -uint64_t odp_timeout_tick(odp_timeout_t tmo); >> +void odp_timer_cancel(odp_timer_t tim); >> + >> +/** >> + * Return fresh/stale/orphan status of timeout. >> + * >> + * Check a received timeout for orphaness (i.e. parent timer >> freed) and >> + * staleness (i.e. parent timer has been reset or cancelled after >> timeout >> + * was enqueued). >> + * If the timeout is fresh, it should be processed. >> + * If the timeout is stale, the timer will automatically be reset >> unless it >> + * was cancelled. >> + * If the timeout is orphaned, it should be freed (by the caller). >> + * >> + * Note: odp_timer_tmo_status() must be called on all received (not >> + * user-defined) timeouts! >> + * >> + * @param tmo Timeout >> + * >> + * @return ODP_TMO_FRESH, ODP_TMO_STALE, ODP_TMO_ORPHAN >> + */ >> +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo); >> + >> +/** >> + * Get timer handle >> + * >> + * Return Handle of parent timer. >> + * >> + * @param tmo Timeout >> + * >> + * @return Timer handle or ODP_TIMER_INVALID for orphaned timeouts >> + */ >> +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo); >> + >> +/** >> + * Get expiration time >> + * >> + * Return (actual) expiration time of timeout. >> + * >> + * @param tmo Timeout >> + * >> + * @return Expiration time >> + */ >> +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo); >> + >> +/** >> + * Get user pointer >> + * >> + * Return User pointer of timer associated with timeout. >> + * The user pointer is often used to point to some associated >> context. >> + * >> + * @param tmo Timeout >> + * >> + * @return User pointer >> + */ >> +void *odp_timer_get_userptr(odp_timer_tmo_t tmo); >> + >> +/* Helper functions */ >> +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, >> odp_timer_tick_t tick); >> >> #ifdef __cplusplus >> } >> diff --git a/platform/linux-generic/include/odp_timer_internal.h >> b/platform/linux-generic/include/odp_timer_internal.h >> index ad28f53..ff8f209 100644 >> --- a/platform/linux-generic/include/odp_timer_internal.h >> +++ b/platform/linux-generic/include/odp_timer_internal.h >> @@ -1,4 +1,4 @@ >> -/* Copyright (c) 2013, Linaro Limited >> +/* Copyright (c) 2014, Linaro Limited >> * All rights reserved. >> * >> * SPDX-License-Identifier: BSD-3-Clause >> @@ -8,72 +8,53 @@ >> /** >> * @file >> * >> - * ODP timer timeout descriptor - implementation internal >> + * ODP timeout descriptor - implementation internal >> */ >> >> #ifndef ODP_TIMER_INTERNAL_H_ >> #define ODP_TIMER_INTERNAL_H_ >> >> -#ifdef __cplusplus >> -extern "C" { >> -#endif >> - >> -#include <odp_std_types.h> >> -#include <odp_queue.h> >> -#include <odp_buffer.h> >> +#include <odp_align.h> >> +#include <odp_debug.h> >> #include <odp_buffer_internal.h> >> #include <odp_buffer_pool_internal.h> >> #include <odp_timer.h> >> >> -struct timeout_t; >> - >> -typedef struct timeout_t { >> - struct timeout_t *next; >> - int timer_id; >> - int tick; >> - uint64_t tmo_tick; >> - odp_queue_t queue; >> - odp_buffer_t buf; >> - odp_buffer_t tmo_buf; >> -} timeout_t; >> - >> - >> -struct odp_timeout_hdr_t; >> - >> /** >> - * Timeout notification header >> + * Internal Timeout header >> */ >> -typedef struct odp_timeout_hdr_t { >> +typedef struct { >> + /* common buffer header */ >> odp_buffer_hdr_t buf_hdr; >> >> - timeout_t meta; >> - >> + /* Requested expiration time */ >> + odp_timer_tick_t expiration; >> + /* User ptr inherited from parent timer */ >> + void *user_ptr; >> + /* Parent timer */ >> + odp_timer_t timer; >> + /* Tag inherited from parent timer at time of expiration */ >> + uint32_t tag; >> + /* Gen-cnt inherited from parent timer at time of creation */ >> + uint32_t gc; >> uint8_t buf_data[]; >> } odp_timeout_hdr_t; >> >> - >> - >> +/* C++ doesn't allow offsetof() on "non-POD" datatypes. Don't >> know why >> + odp_timeout_hdr_t is classified as non-POD, perhaps because of the >> + inheritance? */ >> ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) == >> - ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), >> - "ODP_TIMEOUT_HDR_T__SIZE_ERR"); >> - >> + ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), >> + "sizeof(odp_timeout_hdr_t) == >> ODP_OFFSETOF(odp_timeout_hdr_t, buf_data)"); >> ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == 0, >> - "ODP_TIMEOUT_HDR_T__SIZE_ERR2"); >> - >> + "sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == >> 0"); >> >> /** >> - * Return timeout header >> + * Return the timeout header >> */ >> -static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_timeout_t tmo) >> +static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_buffer_t buf) >> { >> - odp_buffer_hdr_t *buf_hdr = odp_buf_to_hdr((odp_buffer_t) >> tmo); >> - return (odp_timeout_hdr_t *)(uintptr_t)buf_hdr; >> -} >> - >> - >> - >> -#ifdef __cplusplus >> + return (odp_timeout_hdr_t *)odp_buf_to_hdr(buf); >> } >> -#endif >> >> #endif >> diff --git a/platform/linux-generic/odp_timer.c >> b/platform/linux-generic/odp_timer.c >> index 1bf37f9..cea47a3 100644 >> --- a/platform/linux-generic/odp_timer.c >> +++ b/platform/linux-generic/odp_timer.c >> @@ -1,402 +1,631 @@ >> -/* Copyright (c) 2013, Linaro Limited >> +/* Copyright (c) 2014, Linaro Limited >> * All rights reserved. >> * >> * SPDX-License-Identifier: BSD-3-Clause >> */ >> >> -#include <odp_timer.h> >> -#include <odp_timer_internal.h> >> -#include <odp_buffer_pool_internal.h> >> -#include <odp_internal.h> >> -#include <odp_atomic.h> >> -#include <odp_spinlock.h> >> -#include <odp_sync.h> >> -#include <odp_debug.h> >> >> -#include <signal.h> >> -#include <time.h> >> +/** >> + * @file >> + * >> + * ODP timer service >> + * >> + */ >> >> +#include <assert.h> >> +#include <errno.h> >> #include <string.h> >> - >> -#define NUM_TIMERS 1 >> -#define MAX_TICKS 1024 >> -#define RESOLUTION_NS 1000000 >> - >> - >> -typedef struct { >> - odp_spinlock_t lock; >> - timeout_t *list; >> -} tick_t; >> - >> -typedef struct { >> - int allocated; >> - volatile int active; >> - volatile uint64_t cur_tick; >> - timer_t timerid; >> - odp_timer_t timer_hdl; >> - odp_buffer_pool_t pool; >> - uint64_t resolution_ns; >> - uint64_t max_ticks; >> - tick_t tick[MAX_TICKS]; >> - >> -} timer_ring_t; >> - >> -typedef struct { >> - odp_spinlock_t lock; >> - int num_timers; >> - timer_ring_t timer[NUM_TIMERS]; >> - >> -} timer_global_t; >> - >> -/* Global */ >> -static timer_global_t odp_timer; >> - >> -static void add_tmo(tick_t *tick, timeout_t *tmo) >> +#include <stdlib.h> >> +#include <time.h> >> +#include <signal.h> >> +#include "odp_std_types.h" >> +#include "odp_buffer.h" >> +#include "odp_buffer_pool.h" >> +#include "odp_queue.h" >> +#include "odp_hints.h" >> +#include "odp_sync.h" >> +#include "odp_spinlock.h" >> +#include "odp_debug.h" >> +#include "odp_align.h" >> +#include "odp_shared_memory.h" >> +#include "odp_hints.h" >> +#include "odp_internal.h" >> +#include "odp_timer.h" >> +#include "odp_timer_internal.h" >> +#include "priority_queue.h" >> + >> +typedef struct odp_timer { >> + pq_element pqelem;/* Base class */ >> + odp_timer_tick_t req_tmo;/* Requested timeout tick */ >> + odp_buffer_t tmo_buf;/* ODP_BUFFER_INVALID if timeout >> enqueued */ >> + odp_queue_t queue;/* ODP_QUEUE_INVALID if timer is free */ >> + uint32_t tag;/* Reusing tag as next pointer/index when >> timer is free */ >> + uint32_t gc; >> + bool user_buf; /* User-defined buffer? */ >> +} odp_timer; >> + >> +/* Constructor for array of objects */ >> +static inline void odp_timer_con(odp_timer *this) >> { >> - odp_spinlock_lock(&tick->lock); >> - >> - tmo->next = tick->list; >> - tick->list = tmo; >> - >> - odp_spinlock_unlock(&tick->lock); >> + pq_element_con(&this->pqelem); >> + this->tmo_buf = ODP_BUFFER_INVALID; >> + this->queue = ODP_QUEUE_INVALID; >> + this->gc = 0; >> } >> >> -static timeout_t *rem_tmo(tick_t *tick) >> +/* Destructor */ >> +static inline void odp_timer_des(odp_timer *this) >> { >> - timeout_t *tmo; >> - >> - odp_spinlock_lock(&tick->lock); >> - >> - tmo = tick->list; >> - >> - if (tmo) >> - tick->list = tmo->next; >> - >> - odp_spinlock_unlock(&tick->lock); >> + assert(this->tmo_buf == ODP_BUFFER_INVALID); >> + assert(this->queue == ODP_QUEUE_INVALID); >> + pq_element_des(&this->pqelem); >> +} >> >> - if (tmo) >> - tmo->next = NULL; >> +/* Setup when timer is allocated */ >> +static void setup(odp_timer *this, >> + odp_queue_t _q, >> + void *_up, >> + odp_buffer_t _tmo) >> +{ >> + this->req_tmo = INVALID_PRIORITY; >> + this->tmo_buf = _tmo; >> + this->queue = _q; >> + this->tag = 0; >> + this->user_buf = false; >> + /* Initialise constant fields of timeout event */ >> + odp_timeout_hdr_t *tmo_hdr = >> + (odp_timeout_hdr_t *)odp_buf_to_hdr(this->tmo_buf); >> + tmo_hdr->gc = this->gc; >> + tmo_hdr->timer = this; >> + tmo_hdr->user_ptr = _up; >> + /* tmo_hdr->tag set at expiration time */ >> + /* tmo_hdr->expiration set at expiration time */ >> + assert(this->queue != ODP_QUEUE_INVALID); >> +} >> >> - return tmo; >> +/* Teardown when timer is freed */ >> +static odp_buffer_t teardown(odp_timer *this) >> +{ >> + /* Increase generation count to make pending timeout >> orphaned */ >> + ++this->gc; >> + odp_buffer_t buf = this->tmo_buf; >> + this->tmo_buf = ODP_BUFFER_INVALID; >> + this->queue = ODP_QUEUE_INVALID; >> + return buf; >> } >> >> -/** >> - * Search and delete tmo entry from timeout list >> - * return -1 : on error.. handle not in list >> - * 0 : success >> - */ >> -static int find_and_del_tmo(timeout_t **tmo, odp_timer_tmo_t handle) >> +static inline uint32_t get_next_free(odp_timer *this) >> { >> - timeout_t *cur, *prev; >> - prev = NULL; >> + assert(this->queue == ODP_QUEUE_INVALID); >> + return this->tag; >> +} >> >> - for (cur = *tmo; cur != NULL; prev = cur, cur = cur->next) { >> - if (cur->tmo_buf == handle) { >> - if (prev == NULL) >> - *tmo = cur->next; >> - else >> - prev->next = cur->next; >> +static inline void set_next_free(odp_timer *this, uint32_t nf) >> +{ >> + assert(this->queue == ODP_QUEUE_INVALID); >> + this->tag = nf; >> +} >> >> - break; >> +static inline void expire(odp_timer *this, odp_timer_tick_t tick) >> +{ >> + /* Timer expired, is there actually any timeout event */ >> + /* we can enqueue? */ >> + if (odp_likely(this->tmo_buf != ODP_BUFFER_INVALID)) { >> + /* Swap out timeout buffer */ >> + odp_buffer_t buf = this->tmo_buf; >> + this->tmo_buf = ODP_BUFFER_INVALID; >> + if (odp_likely(!this->user_buf)) { >> + odp_timeout_hdr_t *tmo_hdr = >> + (odp_timeout_hdr_t >> *)odp_buf_to_hdr(buf); >> + /* Copy tag from timer */ >> + /* and actual expiration tick from timer >> pool */ >> + tmo_hdr->tag = this->tag; >> + tmo_hdr->expiration = tick; >> } >> + /* Else don't touch user-defined buffer */ >> + int rc = odp_queue_enq(this->queue, buf); >> + if (rc != 0) >> + abort(); >> } >> - >> - if (!cur) >> - /* couldn't find tmo in list */ >> - return -1; >> - >> - /* application to free tmo_buf provided by absolute_tmo >> call */ >> - return 0; >> + /* No, timeout event already enqueued */ >> } >> >> -int odp_timer_cancel_tmo(odp_timer_t timer_hdl, odp_timer_tmo_t tmo) >> +typedef struct odp_timer_pool { >> + priority_queue pq; >> + uint64_t tick; >> + bool shared; >> + odp_spinlock_t lock; >> + const char *name; >> + odp_buffer_pool_t buf_pool; >> + uint64_t resolution_ns; >> + uint64_t max_timeout; >> + odp_timer *timers; >> + uint32_t num_alloc;/* Current number of allocated timers */ >> + uint32_t max_timers;/* Max number of timers */ >> + uint32_t first_free;/* 0..max_timers-1 => free timer */ >> + timer_t timerid; >> + odp_timer_pool_clock_source_t clk_src; >> +} odp_timer_pool; >> + >> +/* Forward declarations */ >> +static void timer_init(odp_timer_pool *tp); >> +static void timer_exit(odp_timer_pool *tp); >> + >> +static void odp_timer_pool_con(odp_timer_pool *this, >> + const char *_n, >> + odp_buffer_pool_t _bp, >> + uint64_t _r, >> + uint64_t _m, >> + uint32_t _mt, >> + bool _s, >> + odp_timer_pool_clock_source_t _cs) >> { >> - int id; >> - uint64_t tick_idx; >> - timeout_t *cancel_tmo; >> - odp_timeout_hdr_t *tmo_hdr; >> - tick_t *tick; >> - >> - /* get id */ >> - id = timer_hdl - 1; >> - >> - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo); >> - /* get tmo_buf to cancel */ >> - cancel_tmo = &tmo_hdr->meta; >> - >> - tick_idx = cancel_tmo->tick; >> - tick = &odp_timer.timer[id].tick[tick_idx]; >> - >> - odp_spinlock_lock(&tick->lock); >> - /* search and delete tmo from tick list */ >> - if (find_and_del_tmo(&tick->list, tmo) != 0) { >> - odp_spinlock_unlock(&tick->lock); >> - ODP_DBG("Couldn't find the tmo (%d) in tick >> list\n", (int)tmo); >> - return -1; >> + priority_queue_con(&this->pq, _mt); >> + this->tick = 0; >> + this->shared = _s; >> + this->name = strdup(_n); >> + this->buf_pool = _bp; >> + this->resolution_ns = _r; >> + this->max_timeout = _m; >> + this->num_alloc = 0; >> + this->max_timers = _mt; >> + this->first_free = 0; >> + this->clk_src = _cs; >> + this->timers = malloc(sizeof(odp_timer) * this->max_timers); >> + if (this->timers == NULL) { >> + ODP_ERR("%s: malloc failed\n", _n); >> + abort(); >> } >> - odp_spinlock_unlock(&tick->lock); >> - >> - return 0; >> + uint32_t i; >> + for (i = 0; i < this->max_timers; i++) >> + odp_timer_con(&this->timers[i]); >> + for (i = 0; i < this->max_timers; i++) >> + set_next_free(&this->timers[i], i + 1); >> + odp_spinlock_init(&this->lock); >> + if (this->clk_src == ODP_CLOCK_DEFAULT) >> + timer_init(this); >> + /* Make sure timer pool initialisation is globally >> observable */ >> + /* before we return a pointer to it */ >> + odp_sync_stores(); >> } >> >> -static void notify_function(union sigval sigval) >> +static odp_timer_pool *odp_timer_pool_new( >> + const char *_n, >> + odp_buffer_pool_t _bp, >> + uint64_t _r, >> + uint64_t _m, >> + uint32_t _mt, >> + bool _s, >> + odp_timer_pool_clock_source_t _cs) >> { >> - uint64_t cur_tick; >> - timeout_t *tmo; >> - tick_t *tick; >> - timer_ring_t *timer; >> - >> - timer = sigval.sival_ptr; >> - >> - if (timer->active == 0) { >> - ODP_DBG("Timer (%u) not active\n", timer->timer_hdl); >> - return; >> + odp_timer_pool *this = malloc(sizeof(odp_timer_pool)); >> + if (odp_unlikely(this == NULL)) { >> + ODP_ERR("%s: malloc failed\n", _n); >> + abort(); >> } >> + odp_timer_pool_con(this, _n, _bp, _r, _m, _mt, _s, _cs); >> + return this; >> +} >> >> - /* ODP_DBG("Tick\n"); */ >> +static void odp_timer_pool_des(odp_timer_pool *this) >> +{ >> + if (this->shared) >> + odp_spinlock_lock(&this->lock); >> + if (this->num_alloc != 0) { >> + /* It's a programming error to attempt to destroy a */ >> + /* timer pool which is still in use */ >> + ODP_ERR("%s: timers in use\n", this->name); >> + abort(); >> + } >> + if (this->clk_src == ODP_CLOCK_DEFAULT) >> + timer_exit(this); >> + uint32_t i; >> + for (i = 0; i < this->max_timers; i++) >> + odp_timer_des(&this->timers[i]); >> + free(this->timers); >> + priority_queue_des(&this->pq); >> + odp_sync_stores(); >> +} >> >> - cur_tick = timer->cur_tick++; >> +static void odp_timer_pool_del(odp_timer_pool *this) >> +{ >> + odp_timer_pool_des(this); >> + free(this); >> +} >> >> - odp_sync_stores(); >> +static inline odp_timer *timer_alloc(odp_timer_pool *this, >> + odp_queue_t queue, >> + void *user_ptr, >> + odp_buffer_t tmo_buf) >> +{ >> + odp_timer *tim = ODP_TIMER_INVALID; >> + if (odp_likely(this->shared)) >> + odp_spinlock_lock(&this->lock); >> + if (odp_likely(this->num_alloc < this->max_timers)) { >> + this->num_alloc++; >> + /* Remove first unused timer from free list */ >> + assert(this->first_free != this->max_timers); >> + tim = &this->timers[this->first_free]; >> + this->first_free = get_next_free(tim); >> + /* Insert timer into priority queue */ >> + if (odp_unlikely(!pq_register_element(&this->pq, >> + &tim->pqelem))) { >> + /* Unexpected internal error */ >> + abort(); >> + } >> + /* Create timer */ >> + setup(tim, queue, user_ptr, tmo_buf); >> + } else { >> + errno = ENFILE; /* Reusing file table overvlow */ >> + } >> + if (odp_likely(this->shared)) >> + odp_spinlock_unlock(&this->lock); >> + return tim; >> +} >> >> - tick = &timer->tick[cur_tick % MAX_TICKS]; >> +static inline void timer_free(odp_timer_pool *this, odp_timer *tim) >> +{ >> + if (odp_likely(this->shared)) >> + odp_spinlock_lock(&this->lock); >> + /* Destroy timer */ >> + odp_buffer_t buf = teardown(tim); >> + /* Remove timer from priority queue */ >> + pq_unregister_element(&this->pq, &tim->pqelem); >> + /* Insert timer into free list */ >> + set_next_free(tim, this->first_free); >> + this->first_free = (tim - &this->timers[0]) / >> sizeof(this->timers[0]); >> + assert(this->num_alloc != 0); >> + this->num_alloc--; >> + if (odp_likely(this->shared)) >> + odp_spinlock_unlock(&this->lock); >> + if (buf != ODP_BUFFER_INVALID) >> + odp_buffer_free(buf); >> +} >> >> - while ((tmo = rem_tmo(tick)) != NULL) { >> - odp_queue_t queue; >> - odp_buffer_t buf; >> +static inline void timer_reset(odp_timer_pool *this, >> + odp_timer *tim, >> + odp_timer_tick_t abs_tck) >> +{ >> + if (odp_likely(this->shared)) >> + odp_spinlock_lock(&this->lock); >> + /* Increase timer tag to make any pending timeout stale */ >> + tim->tag++; >> + /* Save requested timeout */ >> + tim->req_tmo = abs_tck; >> + /* Update timer position in priority queue */ >> + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); >> + if (odp_likely(this->shared)) >> + odp_spinlock_unlock(&this->lock); >> +} >> >> - queue = tmo->queue; >> - buf = tmo->buf; >> +static inline void timer_reset_w_buf(odp_timer_pool *this, >> + odp_timer *tim, >> + odp_timer_tick_t abs_tck, >> + odp_buffer_t user_buf) >> +{ >> + if (odp_likely(this->shared)) >> + odp_spinlock_lock(&this->lock); >> + /* Increase timer tag to make any pending timeout stale */ >> + tim->tag++; >> + /* Save requested timeout */ >> + tim->req_tmo = abs_tck; >> + /* Set flag indicating presence of user defined buffer */ >> + tim->user_buf = true; >> + /* Swap in new buffer, get any old buffer pointer */ >> + odp_buffer_t old_buf = tim->tmo_buf; >> + tim->tmo_buf = user_buf; >> + /* Update timer position in priority queue */ >> + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); >> + if (odp_likely(this->shared)) >> + odp_spinlock_unlock(&this->lock); >> + /* Free old buffer if present */ >> + if (odp_unlikely(old_buf != ODP_BUFFER_INVALID)) >> + odp_buffer_free(old_buf); >> +} >> >> - if (buf != tmo->tmo_buf) >> - odp_buffer_free(tmo->tmo_buf); >> +static inline void timer_cancel(odp_timer_pool *this, >> + odp_timer *tim) >> +{ >> + odp_buffer_t tmo_buf = ODP_BUFFER_INVALID; >> + if (odp_likely(this->shared)) >> + odp_spinlock_lock(&this->lock); >> + if (odp_unlikely(tim->user_buf)) { >> + /* Swap out old user buffer */ >> + tmo_buf = tim->tmo_buf; >> + tim->tmo_buf = ODP_BUFFER_INVALID; >> + tim->user_buf = false; >> + } >> + /* Else a normal timer (no user-defined buffer) */ >> + /* Increase timer tag to make any pending timeout stale */ >> + tim->tag++; >> + /* Clear requested timeout */ >> + tim->req_tmo = INVALID_PRIORITY; >> + /* Remove timer from the priority queue */ >> + pq_deactivate_element(&this->pq, &tim->pqelem); >> + if (odp_likely(this->shared)) >> + odp_spinlock_unlock(&this->lock); >> + /* Free user-defined buffer if present */ >> + if (odp_unlikely(tmo_buf != ODP_BUFFER_INVALID)) >> + odp_buffer_free(tmo_buf); >> +} >> >> - odp_queue_enq(queue, buf); >> +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, >> odp_timer_tick_t tick) >> +{ >> + odp_spinlock_lock(&tpid->lock); >> + unsigned nexp = 0; >> + odp_timer_t tim; >> + tpid->tick = tick; >> + while ((tim = (odp_timer_t)pq_release_element(&tpid->pq, >> tick)) != >> + ODP_TIMER_INVALID) { >> + assert(get_prio(&tim->pqelem) <= tick); >> + expire(tim, tick); >> + nexp++; >> } >> + odp_spinlock_unlock(&tpid->lock); >> + return nexp; >> } >> >> -static void timer_start(timer_ring_t *timer) >> +/* Functions that use Linux/POSIX per-process timers and related >> facilities */ >> +static void timer_notify(union sigval sigval) >> +{ >> + odp_timer_pool *tp = (odp_timer_pool *)sigval.sival_ptr; >> + uint64_t new_tick = tp->tick + 1; >> + (void)odp_timer_pool_expire(tp, new_tick); >> +} >> + >> +static void timer_init(odp_timer_pool *tp) >> { >> struct sigevent sigev; >> struct itimerspec ispec; >> >> - ODP_DBG("\nTimer (%u) starts\n", timer->timer_hdl); >> + ODP_DBG("Creating POSIX timer for timer pool %s, period %" >> + PRIu64" ns\n", tp->name, tp->resolution_ns); >> >> memset(&sigev, 0, sizeof(sigev)); >> memset(&ispec, 0, sizeof(ispec)); >> >> sigev.sigev_notify = SIGEV_THREAD; >> - sigev.sigev_notify_function = notify_function; >> - sigev.sigev_value.sival_ptr = timer; >> + sigev.sigev_notify_function = timer_notify; >> + sigev.sigev_value.sival_ptr = tp; >> >> - if (timer_create(CLOCK_MONOTONIC, &sigev, &timer->timerid)) { >> - ODP_DBG("Timer create failed\n"); > >
We are requiring all internal header files to have "odp_" prefix? Isn't it enough that they are included using "" and not <> which is used for system header files? The priority_queue is not an ODP construct, it is completely generic. It is like a library function except it does not exist in any external library. Maybe it should be located in a separate directory though, e.g. platform/linux-generic/utility or even on a top-level utility library? And why putting .h files in a separate directory? They are part of the implementation just like the .c files (so private, not public). Definitions move between the .c and the .h files as needed. Why are the .c files located in the same directory as the Makefile and a bunch of TEXT files? What makes the internal header files special compared to all other internal files? -- Ola On 4 September 2014 09:58, Savolainen, Petri (NSN - FI/Espoo) < petri.savolainen@nsn.com> wrote: > > platform/linux-generic/priority_queue.h > > Should be moved and renamed to > > platform/linux-generic/include/odp_priority_queue_internal.h > > or something similar. All ODP headers have either odp_ or odph_ prefix so > that it easy to see with are ODP vs. c lib/linux/posix includes. > > > -Petri > > > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp >
From: ext Ola Liljedahl [mailto:ola.liljedahl@linaro.org]
Sent: Thursday, September 04, 2014 1:08 PM
To: Savolainen, Petri (NSN - FI/Espoo)
Cc: ext Anders Roxell; Maxim Uvarov; lng-odp@lists.linaro.org
Subject: Re: [lng-odp] [PATCH] Timer API and and priority queue-based implementation
We are requiring all internal header files to have "odp_" prefix? Isn't it enough that they are included using "" and not <> which is used for system header files?
[Petri] With odp_ prefix, it's clear where it comes from. E.g. C++ seems to have "priority_queue" class, which gcc implements in priority_queue.hpp. If "" search fails (due to makefile problems, etc), the search may continue as <>... Again, those name space confusions just waste time when reading/debugging makefiles and code.
The priority_queue is not an ODP construct, it is completely generic. It is like a library function except it does not exist in any external library. Maybe it should be located in a separate directory though, e.g. platform/linux-generic/utility or even on a top-level utility library?
[Petri] Better to keep it under linux-generic.
And why putting .h files in a separate directory? They are part of the implementation just like the .c files (so private, not public). Definitions move between the .c and the .h files as needed. Why are the .c files located in the same directory as the Makefile and a bunch of TEXT files? What makes the internal header files special compared to all other internal files?
[Petri] Linux-generic have already over 30 implementation files (+ over 30 API files). It's easier to find a .c or .h file when those are in different directories. odp_xyz_internal.h are implementation specifics of API files (vs. odp_xyz.h). True, _internal tag is not good but may be something else that highlights that's not an API.
-Petri
-- Ola
On 4 September 2014 09:58, Savolainen, Petri (NSN - FI/Espoo) <petri.savolainen@nsn.com> wrote:
platform/linux-generic/priority_queue.h
Should be moved and renamed to
platform/linux-generic/include/odp_priority_queue_internal.h
or something similar. All ODP headers have either odp_ or odph_ prefix so that it easy to see with are ODP vs. c lib/linux/posix includes.
-Petri
Hi Ola, I know that you are reworking the patch. See inline comments On 2014-09-02 17:26, Ola Liljedahl wrote: > Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org> > --- > (This document/code contribution attached is provided under the terms of agreement LES-LTM-21309) > New timer API and corresponding SW implementation for linux-generic. > Read more about use cases and usage here: > https://docs.google.com/a/linaro.org/document/d/1bfY_J8ecLJPsFTmYftb0NVmGnB9qkEc_NpcJ87yfaD8/edit# > > example/timer/odp_timer_test.c | 93 ++- > platform/linux-generic/Makefile.am | 1 + > platform/linux-generic/include/api/odp_timer.h | 478 ++++++++++-- > .../linux-generic/include/odp_timer_internal.h | 71 +- > platform/linux-generic/odp_timer.c | 823 +++++++++++++-------- > platform/linux-generic/priority_queue.c | 289 ++++++++ > platform/linux-generic/priority_queue.h | 107 +++ > test/api_test/odp_timer_ping.c | 58 +- > 8 files changed, 1455 insertions(+), 465 deletions(-) > create mode 100644 platform/linux-generic/priority_queue.c > create mode 100644 platform/linux-generic/priority_queue.h > > diff --git a/example/timer/odp_timer_test.c b/example/timer/odp_timer_test.c > index bf1d7df..0b4fedf 100644 > --- a/example/timer/odp_timer_test.c > +++ b/example/timer/odp_timer_test.c > @@ -35,7 +35,6 @@ > typedef struct { > int core_count; /**< Core count*/ > int resolution_us; /**< Timeout resolution in usec*/ > - int min_us; /**< Minimum timeout in usec*/ > int max_us; /**< Maximum timeout in usec*/ > int period_us; /**< Timeout period in usec*/ > int tmo_count; /**< Timeout count*/ > @@ -45,18 +44,16 @@ typedef struct { > /** @private Barrier for test synchronisation */ > static odp_barrier_t test_barrier; > > -/** @private Timer handle*/ > -static odp_timer_t test_timer; > +/** @private Timer pool handle*/ > +static odp_timer_pool_t tp; > > > /** @private test timeout */ > static void test_abs_timeouts(int thr, test_args_t *args) > { > - uint64_t tick; > - uint64_t period; > + odp_timer_tick_t period; > uint64_t period_ns; > odp_queue_t queue; > - odp_buffer_t buf; > int num; > > ODP_DBG(" [%i] test_timeouts\n", thr); > @@ -64,37 +61,51 @@ static void test_abs_timeouts(int thr, test_args_t *args) > queue = odp_queue_lookup("timer_queue"); > > period_ns = args->period_us*USEC; > - period = odp_timer_ns_to_tick(test_timer, period_ns); > + period = odp_timer_ns_to_tick(tp, period_ns); > > ODP_DBG(" [%i] period %"PRIu64" ticks, %"PRIu64" ns\n", thr, > period, period_ns); > > - tick = odp_timer_current_tick(test_timer); > + ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, > + odp_timer_current_tick(tp)); > > - ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick); > - > - tick += period; > - > - if (odp_timer_absolute_tmo(test_timer, tick, queue, ODP_BUFFER_INVALID) > - == ODP_TIMER_TMO_INVALID){ > - ODP_DBG("Timeout request failed\n"); > + odp_timer_t test_timer; > + test_timer = odp_timer_alloc(tp, queue, NULL); > + if (test_timer == ODP_TIMER_INVALID) { > + ODP_ERR("Failed to allocate timer\n"); > return; > } > + odp_timer_set_rel(test_timer, period); > > num = args->tmo_count; > > while (1) { > - odp_timeout_t tmo; > - > - buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); > + /* Local variables because received timeouts may not > + originate from timer we created above */ > + odp_timer_tmo_t tmo; > + odp_timer_tick_t tick; > + odp_timer_t hdl; > + > + /* Get the next ready buffer/timeout */ > + tmo = odp_schedule_one(&queue, ODP_SCHED_WAIT); > + switch (odp_timer_tmo_status(tmo)) { > + case ODP_TMO_FRESH: > + break; > + case ODP_TMO_STALE: > + ODP_DBG("[%i] Stale timeout received\n", thr); > + break; > + case ODP_TMO_ORPHAN: > + ODP_DBG("[%i] Orphaned timeout received\n", > + thr); > + odp_buffer_free(tmo); > + continue; > + } > > - tmo = odp_timeout_from_buffer(buf); > - tick = odp_timeout_tick(tmo); > + tick = odp_timer_get_expiry(tmo); > + hdl = odp_timer_get_handle(tmo); > > ODP_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, tick); > > - odp_buffer_free(buf); > - > num--; > > if (num == 0) > @@ -102,10 +113,13 @@ static void test_abs_timeouts(int thr, test_args_t *args) > > tick += period; > > - odp_timer_absolute_tmo(test_timer, tick, > - queue, ODP_BUFFER_INVALID); > + if (hdl != ODP_TIMER_INVALID) > + odp_timer_set_abs(hdl, tick); > } > > + odp_timer_cancel(test_timer); > + odp_timer_free(test_timer); > + > if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) > odp_schedule_release_atomic(); > } > @@ -159,7 +173,6 @@ static void print_usage(void) > printf("Options:\n"); > printf(" -c, --count <number> core count, core IDs start from 1\n"); > printf(" -r, --resolution <us> timeout resolution in usec\n"); > - printf(" -m, --min <us> minimum timeout in usec\n"); > printf(" -x, --max <us> maximum timeout in usec\n"); > printf(" -p, --period <us> timeout period in usec\n"); > printf(" -t, --timeouts <count> timeout repeat count\n"); > @@ -183,7 +196,6 @@ static void parse_args(int argc, char *argv[], test_args_t *args) > static struct option longopts[] = { > {"count", required_argument, NULL, 'c'}, > {"resolution", required_argument, NULL, 'r'}, > - {"min", required_argument, NULL, 'm'}, > {"max", required_argument, NULL, 'x'}, > {"period", required_argument, NULL, 'p'}, > {"timeouts", required_argument, NULL, 't'}, > @@ -194,14 +206,13 @@ static void parse_args(int argc, char *argv[], test_args_t *args) > /* defaults */ > args->core_count = 0; /* all cores */ > args->resolution_us = 10000; > - args->min_us = args->resolution_us; > args->max_us = 100000000; > args->period_us = 1000000; > args->tmo_count = 30; > > while (1) { > opt = getopt_long(argc, argv, "+c:r:m:x:p:t:h", > - longopts, &long_index); > + longopts, &long_index); > > if (opt == -1) > break; /* No more options */ > @@ -213,9 +224,6 @@ static void parse_args(int argc, char *argv[], test_args_t *args) > case 'r': > args->resolution_us = atoi(optarg); > break; > - case 'm': > - args->min_us = atoi(optarg); > - break; > case 'x': > args->max_us = atoi(optarg); > break; > @@ -299,7 +307,6 @@ int main(int argc, char *argv[]) > > printf("first core: %i\n", first_core); > printf("resolution: %i usec\n", args.resolution_us); > - printf("min timeout: %i usec\n", args.min_us); > printf("max timeout: %i usec\n", args.max_us); > printf("period: %i usec\n", args.period_us); > printf("timeouts: %i\n", args.tmo_count); > @@ -323,10 +330,23 @@ int main(int argc, char *argv[]) > ODP_BUFFER_TYPE_TIMEOUT); > > if (pool == ODP_BUFFER_POOL_INVALID) { > - ODP_ERR("Pool create failed.\n"); > + ODP_ERR("Buffer pool create failed.\n"); > return -1; > } > > + tp = odp_timer_pool_create("timer_pool", pool, > + args.resolution_us*USEC, > + args.max_us*USEC, > + num_workers, /* One timer per worker */ > + true, > + ODP_CLOCK_DEFAULT); > + if (tp == ODP_TIMER_POOL_INVALID) { > + ODP_ERR("Timer pool create failed.\n"); > + return -1; > + } > + > + odp_shm_print_all(); > + > /* > * Create a queue for timer test > */ > @@ -342,13 +362,6 @@ int main(int argc, char *argv[]) > return -1; > } > > - test_timer = odp_timer_create("test_timer", pool, > - args.resolution_us*USEC, > - args.min_us*USEC, > - args.max_us*USEC); > - > - odp_shm_print_all(); > - > printf("CPU freq %"PRIu64" hz\n", odp_sys_cpu_hz()); > printf("Cycles vs nanoseconds:\n"); > ns = 0; > diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am > index f4dfdc1..e2bc1a7 100644 > --- a/platform/linux-generic/Makefile.am > +++ b/platform/linux-generic/Makefile.am > @@ -72,4 +72,5 @@ __LIB__libodp_la_SOURCES = \ > odp_thread.c \ > odp_ticketlock.c \ > odp_time.c \ > + priority_queue.c \ When you change the name of this file put it in alphabetic order if possible. > odp_timer.c > diff --git a/platform/linux-generic/include/api/odp_timer.h b/platform/linux-generic/include/api/odp_timer.h > index 01db839..e5f961c 100644 > --- a/platform/linux-generic/include/api/odp_timer.h > +++ b/platform/linux-generic/include/api/odp_timer.h > @@ -1,4 +1,4 @@ > -/* Copyright (c) 2013, Linaro Limited > +/* Copyright (c) 2014, Linaro Limited > * All rights reserved. > * > * SPDX-License-Identifier: BSD-3-Clause > @@ -8,7 +8,175 @@ > /** > * @file > * > - * ODP timer > + * ODP timer service > + * > + > +//Example #1 Retransmission timer (e.g. for reliable connections) > + > +//Create timer pool for reliable connections > +#define SEC 1000000000ULL //1s expressed in nanoseconds > +odp_timer_pool_t tcp_tpid = > + odp_timer_pool_create("TCP", > + buffer_pool, > + 1000000,//resolution 1ms > + 7200 * SEC,//max tmo length 2hours > + 40000,//num_timers > + true,//shared > + ODP_CLOCK_DEFAULT > + ); > +if (tcp_tpid == ODP_TIMER_POOL_INVALID) > +{ > + //Failed to create timer pool => fatal error > +} > + > + > +//Setting up a new connection > +//Allocate retransmission timeout (identical for supervision timeout) > +//The user pointer points back to the connection context > +conn->ret_tim = odp_timer_alloc(tcp_tpid, queue, conn); > +//Check if all resources were successfully allocated > +if (conn->ret_tim == ODP_TIMER_INVALID) > +{ > + //Failed to allocate all resources for connection => tear down > + //Destroy timeout > + odp_timer_free(conn->ret_tim); > + //Tear down connection > + ... > + return false; > +} > +//All necessary resources successfully allocated > +//Compute initial retransmission length in timer ticks > +conn->ret_len = odp_timer_ns_to_tick(tcp_tpid, 3 * SEC);//Per RFC1122 > +//Arm the timer > +odp_timer_set_rel(conn->ret_tim, conn->ret_len); > +return true; > + > + > +//A packet for the connection has just been transmitted > +//Reset the retransmission timer > +odp_timer_set_rel(conn->ret_tim, conn->ret_len); > + > + > +//A retransmission timeout for the connection has been received > +//Check if timeout is fresh or stale, for stale timeouts we need to reset the > +//timer > +switch (odp_timer_tmo_status(tmo)) > +{ > + case ODP_TMO_FRESH : > + //Fresh timeout, last transmitted packet not acked in time => > + retransmit > + //Get connection from timeout event > + conn = odp_timer_get_userptr(tmo); > + //Retransmit last packet (e.g. TCP segment) > + ... > + //Re-arm timer using original delta value > + odp_timer_set_rel(conn->ret_tim, conn->ret_len); > + break; > + case ODP_TMO_STALE : > + break;//Do nothing > + case ODP_TMO_ORPHAN : > + odp_free_buffer(tmo); > + return;//Get out of here > +} > + > + > +//Example #2 Periodic tick > + > +//Create timer pool for periodic ticks > +odp_timer_pool_t per_tpid = > + odp_timer_pool_create("periodic-tick", > + buffer_pool, > + 1,//resolution 1ns > + 1000000000,//maximum timeout length 1s > + 10,//num_timers > + false,//not shared > + ODP_CLOCK_DEFAULT > + ); > +if (per_tpid == ODP_TIMER_POOL_INVALID) > +{ > + //Failed to create timer pool => fatal error > +} > + > + > +//Allocate periodic timer > +tim_1733 = odp_timer_alloc(per_tpid, queue, NULL); > +//Check if all resources were successfully allocated > +if (tim_1733 == ODP_TIMER_INVALID) > +{ > + //Failed to allocate all resources => tear down > + //Destroy timeout > + odp_timer_free(tim_1733); > + //Tear down other state > + ... > + return false; > +} > +//All necessary resources successfully allocated > +//Compute tick period in timer ticks > +period_1733 = odp_timer_ns_to_tick(per_tpid, 1000000000U / 1733U);//1733Hz > +//Compute when next tick should expire > +next_1733 = odp_timer_current_tick(per_tpid) + period_1733; > +//Arm the periodic timer > +odp_timer_set_abs(tim_1733, next_1733); > +return true; > + > + > + > +//A periodic timer timeout has been received > +//Must call odp_timer_tmo_status() on timeout! > +ret = odp_timer_tmo_status(tmo); > +//We expect the timeout is fresh since we are not calling set or cancel on > +//active or expired timers in this example > +assert(ret == ODP_TMO_FRESH); > +//Do processing driven by timeout *before* > +... > +do { > + //Compute when the timer should expire next > + next_1733 += period_1733; > + //Check that this is in the future > + if (likely(next_1733 > odp_timer_current_tick(per_tpid)) > + break;//Yes, done > + //Else we missed a timeout > + //Optionally attempt some recovery and/or logging of the problem > + ... > +} while (0); > +//Re-arm periodic timer > +odp_timer_set_abs(tim_1733, next_1733); > +//Or do processing driven by timeout *after* > +... > +return; > + > +//Example #3 Tear down of flow > +//ctx points to flow context data structure owned by application > +//Free the timer, cancelling any timeout > +odp_timer_free(ctx->timer);//Any enqueued timeout will be made invalid > +//Continue tearing down and eventually freeing context > +... > +return; > + > +//A timeout has been received, check status > +switch (odp_timer_tmo_status(tmo)) > +{ > + case ODP_TMO_FRESH : > + //A flow has timed out, tear it down > + //Find flow context from timeout > + ctx = (context *)odp_timer_get_userptr(tmo); > + //Free the supervision timer, any enqueued timeout will remain > + odp_timer_free(ctx->tim); > + //Free other flow related resources > + ... > + //Flow torn down > + break; > + case ODP_TMO_STALE : > + //A stale timeout was received, timer automatically reset > + break; > + case ODP_TMO_ORPHAN : > + //Orphaned timeout (from previously torn down flow) > + //No corresponding timer or flow context > + //Free the timeout > + odp_buffer_free(tmo); > + break; > +} > + > */ > > #ifndef ODP_TIMER_H_ > @@ -23,139 +191,325 @@ extern "C" { > #include <odp_buffer_pool.h> > #include <odp_queue.h> > > +/** > +* ODP timer pool handle (platform dependent) > +*/ > +struct odp_timer_pool; > +typedef struct odp_timer_pool *odp_timer_pool_t; > > /** > - * ODP timer handle > + * Invalid timer pool handle (platform dependent) > */ > -typedef uint32_t odp_timer_t; > +#define ODP_TIMER_POOL_INVALID NULL > > -/** Invalid timer */ > -#define ODP_TIMER_INVALID 0 > +typedef enum odp_timer_pool_clock_source_e { > + ODP_CLOCK_DEFAULT = 0, > + /* Platform dependent which clock sources exist beyond > + ODP_CLOCK_DEFAULT */ > + ODP_CLOCK_NONE = 1 > +} odp_timer_pool_clock_source_t; > > +/** > +* ODP timer handle (platform dependent) > +*/ > +struct odp_timer; > +typedef struct odp_timer *odp_timer_t; > > /** > - * ODP timeout handle > + * Invalid timer handle (platform dependent) > */ > -typedef odp_buffer_t odp_timer_tmo_t; > - > -/** Invalid timeout */ > -#define ODP_TIMER_TMO_INVALID 0 > +#define ODP_TIMER_INVALID NULL > > +/** > + * ODP timeout event handle > + */ > +typedef odp_buffer_t odp_timer_tmo_t; > > /** > - * Timeout notification > + * ODP timeout status > */ > -typedef odp_buffer_t odp_timeout_t; > +typedef enum odp_timer_tmo_status_e { > + ODP_TMO_FRESH, /* Timeout is fresh, process it */ > + ODP_TMO_STALE, /* Timer reset or cancelled, do nothing */ > + ODP_TMO_ORPHAN,/* Timer deleted, free timeout */ > +} odp_timer_tmo_status_t; > + > +/** > +* ODP tick value > +*/ > +typedef uint64_t odp_timer_tick_t; > > > /** > - * Create a timer > + * Create a timer pool > * > - * Creates a new timer with requested properties. > + * Create a new timer pool. > + * odp_timer_pool_create() is typically called once or a couple of times during > + * application initialisation. > * > * @param name Name > - * @param pool Buffer pool for allocating timeout notifications > + * @param buf_pool Buffer pool for allocating timers > * @param resolution Timeout resolution in nanoseconds > - * @param min_tmo Minimum timeout duration in nanoseconds > - * @param max_tmo Maximum timeout duration in nanoseconds > + * @param max_tmo Maximum relative timeout in nanoseconds > + * @param num_timers Number of supported timers (minimum) > + * @param shared Shared or private timer pool. > + * Operations on shared timers will include the necessary > + * mutual exclusion, operations on private timers may not > + * (mutual exclusion is the responsibility of the caller). > + * @param clk_src Clock source to use > + * > + * @return Timer pool handle if successful, otherwise ODP_TIMER_POOL_INVALID > + * and errno set > + */ > +odp_timer_pool_t > +odp_timer_pool_create(const char *name, > + odp_buffer_pool_t buf_pool, > + uint64_t resolution, > + uint64_t max_tmo, > + uint32_t num_timers, > + bool shared, > + odp_timer_pool_clock_source_t clk_src); > + > +/** > + * Start a timer pool > + * > + * 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 > * > - * @return Timer handle if successful, otherwise ODP_TIMER_INVALID > + * Destroy a timer pool, freeing all resources. > + * All timers must have been freed. > + * > + * @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, odp_timer_tick_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); > +odp_timer_tick_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 > + */ > +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid); > + > +/** > + * ODP timer configurations > */ > -uint64_t odp_timer_resolution(odp_timer_t timer); > + > +typedef enum odp_timer_pool_conf_e { > + ODP_TIMER_NAME, /* Return name of timer pool */ > + ODP_TIMER_RESOLUTION,/* Return the timer resolution (in ns) */ > + ODP_TIMER_MAX_TMO, /* Return the maximum supported timeout (in ns) */ > + ODP_TIMER_NUM_TIMERS,/* Return number of supported timers */ > + ODP_TIMER_SHARED /* Return shared flag */ > +} odp_timer_pool_conf_t; > > /** > - * Maximum timeout in timer ticks > + * Query different timer pool configurations, e.g. > + * Timer resolution in nanoseconds > + * Maximum timeout in timer ticks > + * Number of supported timers > + * Shared or private timer pool > * > - * @param timer Timer > + * @param tpid Timer pool identifier > + * @param item Configuration item being queried > * > - * @return Maximum timeout in timer ticks > + * @return the requested piece of information or 0 for unknown item. > */ > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer); > +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, > + odp_timer_pool_conf_t item); > > /** > - * Current timer tick > + * Allocate a timer > * > - * @param timer Timer > + * Create a timer (allocating all necessary resources e.g. timeout event) from > + * the timer pool. > * > - * @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 (copied to timeouts) > + * > + * @return Timer handle if successful, otherwise ODP_TIMER_INVALID and > + * errno set. > + */ > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > + odp_queue_t queue, > + void *user_ptr); > + > +/** > + * Free a timer > + * > + * Free (destroy) a timer, freeing all associated resources (e.g. default > + * timeout event). An expired and enqueued timeout event will not be freed. > + * It is the responsibility of the application to free this timeout when it > + * is received. > + * > + * @param tim Timer handle > */ > -uint64_t odp_timer_current_tick(odp_timer_t timer); > +void odp_timer_free(odp_timer_t tim); > > /** > - * Request timeout with an absolute timer tick > + * Set a timer (absolute time) with a user-defined timeout buffer > * > - * When tick reaches tmo_tick, the timer enqueues the timeout notification into > - * the destination queue. > + * Set (arm) the timer to expire at specific time. The user-defined > + * buffer will be enqueued when the timer expires. > + * Arming may fail (if the timer is in state EXPIRED), an earlier timeout > + * will then be received. odp_timer_tmo_status() must be used to check if > + * the received timeout is valid. > * > - * @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. > + * Note: any invalid parameters will be treated as programming errors and will > + * cause the application to abort. > + * Note: a timeout too near in time may be delivered immediately. > + * Note: a timeout too far away in time (beyond max_timeout) might be delivered > + * early. > * > - * @return Timeout handle if successful, otherwise ODP_TIMER_TMO_INVALID > + * @param tim Timer > + * @param abs_tck Expiration time in absolute timer ticks > + * @param user_buf The buffer to use as timeout event > */ > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, uint64_t tmo_tick, > - odp_queue_t queue, odp_buffer_t buf); > +void odp_timer_set_abs_w_buf(odp_timer_t tim, > + odp_timer_tick_t abs_tck, > + odp_buffer_t user_buf); > > /** > - * Cancel a timeout > + * Set a timer with an absolute expiration time > + * > + * Set (arm) the timer to expire at a specific time. > + * Arming may fail (if the timer is in state EXPIRED), an earlier timeout > + * will then be received. odp_timer_tmo_status() must be used to check if > + * the received timeout is valid. > * > - * @param timer Timer > - * @param tmo Timeout to cancel > + * Note: any invalid parameters will be treated as programming errors and will > + * cause the application to abort. > + * Note: a timeout too near in time may be delivered immediately. > + * Note: a timeout too far away in time (beyond max_timeout) might be delivered > + * early, it will automatically be reset by odp_timer_tmo_status(). > * > - * @return 0 if successful > + * @param tim Timer > + * @param abs_tck Expiration time in absolute timer ticks > */ > -int odp_timer_cancel_tmo(odp_timer_t timer, odp_timer_tmo_t tmo); > +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck); > > /** > - * Convert buffer handle to timeout handle > + * Set a timer with a relative expiration time > * > - * @param buf Buffer handle > + * Set (arm) the timer to expire at a relative future time. > + * Arming may fail (if the timer is in state EXPIRED), > + * an earlier timeout will then be received. odp_timer_tmo_status() must > + * be used to check if the received timeout is valid. > * > - * @return Timeout buffer handle > + * Note: any invalid parameters will be treated as programming errors and will > + * cause the application to abort. > + * Note: a timeout too near in time may be delivered immediately. > + * Note: a timeout too far away in time (beyond max_timeout) might be delivered > + * early, it will automatically be reset by odp_timer_tmo_status(). > + * > + * @param tim Timer > + * @param rel_tck Expiration time in timer ticks relative to current time of > + * the timer pool the timer belongs to > */ > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf); > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck); > > /** > - * Return absolute timeout tick > + * Cancel a timer > + * > + * Cancel a timer, preventing future expiration and delivery. > + * > + * A timer that has already expired and been enqueued for delivery may be > + * impossible to cancel and will instead be delivered to the destination queue. > + * Use odp_timer_tmo_status() the check whether a received timeout is fresh or > + * stale (cancelled). Stale timeouts will automatically be recycled. > * > - * @param tmo Timeout buffer handle > + * Note: any invalid parameters will be treated as programming errors and will > + * cause the application to abort. > * > - * @return Absolute timeout tick > + * @param tim Timer handle > */ > -uint64_t odp_timeout_tick(odp_timeout_t tmo); > +void odp_timer_cancel(odp_timer_t tim); > + > +/** > + * Return fresh/stale/orphan status of timeout. > + * > + * Check a received timeout for orphaness (i.e. parent timer freed) and > + * staleness (i.e. parent timer has been reset or cancelled after timeout > + * was enqueued). > + * If the timeout is fresh, it should be processed. > + * If the timeout is stale, the timer will automatically be reset unless it > + * was cancelled. > + * If the timeout is orphaned, it should be freed (by the caller). > + * > + * Note: odp_timer_tmo_status() must be called on all received (not > + * user-defined) timeouts! > + * > + * @param tmo Timeout > + * > + * @return ODP_TMO_FRESH, ODP_TMO_STALE, ODP_TMO_ORPHAN > + */ > +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo); > + > +/** > + * Get timer handle > + * > + * Return Handle of parent timer. > + * > + * @param tmo Timeout > + * > + * @return Timer handle or ODP_TIMER_INVALID for orphaned timeouts > + */ > +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo); > + > +/** > + * Get expiration time > + * > + * Return (actual) expiration time of timeout. > + * > + * @param tmo Timeout > + * > + * @return Expiration time > + */ > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo); > + > +/** > + * Get user pointer > + * > + * Return User pointer of timer associated with timeout. > + * The user pointer is often used to point to some associated context. > + * > + * @param tmo Timeout > + * > + * @return User pointer > + */ > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo); > + > +/* Helper functions */ > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, odp_timer_tick_t tick); > > #ifdef __cplusplus > } > diff --git a/platform/linux-generic/include/odp_timer_internal.h b/platform/linux-generic/include/odp_timer_internal.h > index ad28f53..ff8f209 100644 > --- a/platform/linux-generic/include/odp_timer_internal.h > +++ b/platform/linux-generic/include/odp_timer_internal.h > @@ -1,4 +1,4 @@ > -/* Copyright (c) 2013, Linaro Limited > +/* Copyright (c) 2014, Linaro Limited > * All rights reserved. > * > * SPDX-License-Identifier: BSD-3-Clause > @@ -8,72 +8,53 @@ > /** > * @file > * > - * ODP timer timeout descriptor - implementation internal > + * ODP timeout descriptor - implementation internal > */ > > #ifndef ODP_TIMER_INTERNAL_H_ > #define ODP_TIMER_INTERNAL_H_ > > -#ifdef __cplusplus > -extern "C" { > -#endif > - > -#include <odp_std_types.h> > -#include <odp_queue.h> > -#include <odp_buffer.h> > +#include <odp_align.h> > +#include <odp_debug.h> > #include <odp_buffer_internal.h> > #include <odp_buffer_pool_internal.h> > #include <odp_timer.h> > > -struct timeout_t; > - > -typedef struct timeout_t { > - struct timeout_t *next; > - int timer_id; > - int tick; > - uint64_t tmo_tick; > - odp_queue_t queue; > - odp_buffer_t buf; > - odp_buffer_t tmo_buf; > -} timeout_t; > - > - > -struct odp_timeout_hdr_t; > - > /** > - * Timeout notification header > + * Internal Timeout header > */ > -typedef struct odp_timeout_hdr_t { > +typedef struct { > + /* common buffer header */ > odp_buffer_hdr_t buf_hdr; > > - timeout_t meta; > - > + /* Requested expiration time */ > + odp_timer_tick_t expiration; > + /* User ptr inherited from parent timer */ > + void *user_ptr; > + /* Parent timer */ > + odp_timer_t timer; > + /* Tag inherited from parent timer at time of expiration */ > + uint32_t tag; > + /* Gen-cnt inherited from parent timer at time of creation */ > + uint32_t gc; > uint8_t buf_data[]; > } odp_timeout_hdr_t; > > - > - > +/* C++ doesn't allow offsetof() on "non-POD" datatypes. Don't know why > + odp_timeout_hdr_t is classified as non-POD, perhaps because of the > + inheritance? */ > ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) == > - ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), > - "ODP_TIMEOUT_HDR_T__SIZE_ERR"); > - > + ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), > + "sizeof(odp_timeout_hdr_t) == ODP_OFFSETOF(odp_timeout_hdr_t, buf_data)"); > ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == 0, > - "ODP_TIMEOUT_HDR_T__SIZE_ERR2"); > - > + "sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == 0"); > > /** > - * Return timeout header > + * Return the timeout header > */ > -static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_timeout_t tmo) > +static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_buffer_t buf) > { > - odp_buffer_hdr_t *buf_hdr = odp_buf_to_hdr((odp_buffer_t)tmo); > - return (odp_timeout_hdr_t *)(uintptr_t)buf_hdr; > -} > - > - > - > -#ifdef __cplusplus > + return (odp_timeout_hdr_t *)odp_buf_to_hdr(buf); > } > -#endif > > #endif > diff --git a/platform/linux-generic/odp_timer.c b/platform/linux-generic/odp_timer.c > index 1bf37f9..cea47a3 100644 > --- a/platform/linux-generic/odp_timer.c > +++ b/platform/linux-generic/odp_timer.c > @@ -1,402 +1,631 @@ > -/* Copyright (c) 2013, Linaro Limited > +/* Copyright (c) 2014, Linaro Limited > * All rights reserved. > * > * SPDX-License-Identifier: BSD-3-Clause > */ > > -#include <odp_timer.h> > -#include <odp_timer_internal.h> > -#include <odp_buffer_pool_internal.h> > -#include <odp_internal.h> > -#include <odp_atomic.h> > -#include <odp_spinlock.h> > -#include <odp_sync.h> > -#include <odp_debug.h> > > -#include <signal.h> > -#include <time.h> > +/** > + * @file > + * > + * ODP timer service > + * > + */ > > +#include <assert.h> > +#include <errno.h> > #include <string.h> > - > -#define NUM_TIMERS 1 > -#define MAX_TICKS 1024 > -#define RESOLUTION_NS 1000000 > - > - > -typedef struct { > - odp_spinlock_t lock; > - timeout_t *list; > -} tick_t; > - > -typedef struct { > - int allocated; > - volatile int active; > - volatile uint64_t cur_tick; > - timer_t timerid; > - odp_timer_t timer_hdl; > - odp_buffer_pool_t pool; > - uint64_t resolution_ns; > - uint64_t max_ticks; > - tick_t tick[MAX_TICKS]; > - > -} timer_ring_t; > - > -typedef struct { > - odp_spinlock_t lock; > - int num_timers; > - timer_ring_t timer[NUM_TIMERS]; > - > -} timer_global_t; > - > -/* Global */ > -static timer_global_t odp_timer; > - > -static void add_tmo(tick_t *tick, timeout_t *tmo) > +#include <stdlib.h> > +#include <time.h> > +#include <signal.h> > +#include "odp_std_types.h" > +#include "odp_buffer.h" > +#include "odp_buffer_pool.h" > +#include "odp_queue.h" > +#include "odp_hints.h" > +#include "odp_sync.h" > +#include "odp_spinlock.h" > +#include "odp_debug.h" > +#include "odp_align.h" > +#include "odp_shared_memory.h" > +#include "odp_hints.h" > +#include "odp_internal.h" > +#include "odp_timer.h" > +#include "odp_timer_internal.h" > +#include "priority_queue.h" > + > +typedef struct odp_timer { > + pq_element pqelem;/* Base class */ > + odp_timer_tick_t req_tmo;/* Requested timeout tick */ > + odp_buffer_t tmo_buf;/* ODP_BUFFER_INVALID if timeout enqueued */ > + odp_queue_t queue;/* ODP_QUEUE_INVALID if timer is free */ > + uint32_t tag;/* Reusing tag as next pointer/index when timer is free */ > + uint32_t gc; > + bool user_buf; /* User-defined buffer? */ > +} odp_timer; > + > +/* Constructor for array of objects */ > +static inline void odp_timer_con(odp_timer *this) > { > - odp_spinlock_lock(&tick->lock); > - > - tmo->next = tick->list; > - tick->list = tmo; > - > - odp_spinlock_unlock(&tick->lock); > + pq_element_con(&this->pqelem); > + this->tmo_buf = ODP_BUFFER_INVALID; > + this->queue = ODP_QUEUE_INVALID; > + this->gc = 0; > } > > -static timeout_t *rem_tmo(tick_t *tick) > +/* Destructor */ > +static inline void odp_timer_des(odp_timer *this) > { > - timeout_t *tmo; > - > - odp_spinlock_lock(&tick->lock); > - > - tmo = tick->list; > - > - if (tmo) > - tick->list = tmo->next; > - > - odp_spinlock_unlock(&tick->lock); > + assert(this->tmo_buf == ODP_BUFFER_INVALID); > + assert(this->queue == ODP_QUEUE_INVALID); > + pq_element_des(&this->pqelem); > +} > > - if (tmo) > - tmo->next = NULL; > +/* Setup when timer is allocated */ > +static void setup(odp_timer *this, > + odp_queue_t _q, > + void *_up, > + odp_buffer_t _tmo) > +{ > + this->req_tmo = INVALID_PRIORITY; > + this->tmo_buf = _tmo; > + this->queue = _q; > + this->tag = 0; > + this->user_buf = false; > + /* Initialise constant fields of timeout event */ > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(this->tmo_buf); > + tmo_hdr->gc = this->gc; > + tmo_hdr->timer = this; > + tmo_hdr->user_ptr = _up; > + /* tmo_hdr->tag set at expiration time */ > + /* tmo_hdr->expiration set at expiration time */ > + assert(this->queue != ODP_QUEUE_INVALID); > +} > > - return tmo; > +/* Teardown when timer is freed */ > +static odp_buffer_t teardown(odp_timer *this) > +{ > + /* Increase generation count to make pending timeout orphaned */ > + ++this->gc; > + odp_buffer_t buf = this->tmo_buf; > + this->tmo_buf = ODP_BUFFER_INVALID; > + this->queue = ODP_QUEUE_INVALID; > + return buf; > } > > -/** > - * Search and delete tmo entry from timeout list > - * return -1 : on error.. handle not in list > - * 0 : success > - */ > -static int find_and_del_tmo(timeout_t **tmo, odp_timer_tmo_t handle) > +static inline uint32_t get_next_free(odp_timer *this) > { > - timeout_t *cur, *prev; > - prev = NULL; > + assert(this->queue == ODP_QUEUE_INVALID); > + return this->tag; > +} > > - for (cur = *tmo; cur != NULL; prev = cur, cur = cur->next) { > - if (cur->tmo_buf == handle) { > - if (prev == NULL) > - *tmo = cur->next; > - else > - prev->next = cur->next; > +static inline void set_next_free(odp_timer *this, uint32_t nf) > +{ > + assert(this->queue == ODP_QUEUE_INVALID); > + this->tag = nf; > +} > > - break; > +static inline void expire(odp_timer *this, odp_timer_tick_t tick) > +{ > + /* Timer expired, is there actually any timeout event */ > + /* we can enqueue? */ > + if (odp_likely(this->tmo_buf != ODP_BUFFER_INVALID)) { > + /* Swap out timeout buffer */ > + odp_buffer_t buf = this->tmo_buf; > + this->tmo_buf = ODP_BUFFER_INVALID; > + if (odp_likely(!this->user_buf)) { > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(buf); > + /* Copy tag from timer */ > + /* and actual expiration tick from timer pool */ > + tmo_hdr->tag = this->tag; > + tmo_hdr->expiration = tick; > } > + /* Else don't touch user-defined buffer */ > + int rc = odp_queue_enq(this->queue, buf); > + if (rc != 0) > + abort(); > } > - > - if (!cur) > - /* couldn't find tmo in list */ > - return -1; > - > - /* application to free tmo_buf provided by absolute_tmo call */ > - return 0; > + /* No, timeout event already enqueued */ > } > > -int odp_timer_cancel_tmo(odp_timer_t timer_hdl, odp_timer_tmo_t tmo) > +typedef struct odp_timer_pool { > + priority_queue pq; > + uint64_t tick; > + bool shared; > + odp_spinlock_t lock; > + const char *name; > + odp_buffer_pool_t buf_pool; > + uint64_t resolution_ns; > + uint64_t max_timeout; > + odp_timer *timers; > + uint32_t num_alloc;/* Current number of allocated timers */ > + uint32_t max_timers;/* Max number of timers */ > + uint32_t first_free;/* 0..max_timers-1 => free timer */ > + timer_t timerid; > + odp_timer_pool_clock_source_t clk_src; > +} odp_timer_pool; > + > +/* Forward declarations */ > +static void timer_init(odp_timer_pool *tp); > +static void timer_exit(odp_timer_pool *tp); > + > +static void odp_timer_pool_con(odp_timer_pool *this, > + const char *_n, > + odp_buffer_pool_t _bp, > + uint64_t _r, > + uint64_t _m, > + uint32_t _mt, > + bool _s, > + odp_timer_pool_clock_source_t _cs) > { > - int id; > - uint64_t tick_idx; > - timeout_t *cancel_tmo; > - odp_timeout_hdr_t *tmo_hdr; > - tick_t *tick; > - > - /* get id */ > - id = timer_hdl - 1; > - > - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo); > - /* get tmo_buf to cancel */ > - cancel_tmo = &tmo_hdr->meta; > - > - tick_idx = cancel_tmo->tick; > - tick = &odp_timer.timer[id].tick[tick_idx]; > - > - odp_spinlock_lock(&tick->lock); > - /* search and delete tmo from tick list */ > - if (find_and_del_tmo(&tick->list, tmo) != 0) { > - odp_spinlock_unlock(&tick->lock); > - ODP_DBG("Couldn't find the tmo (%d) in tick list\n", (int)tmo); > - return -1; > + priority_queue_con(&this->pq, _mt); > + this->tick = 0; > + this->shared = _s; > + this->name = strdup(_n); > + this->buf_pool = _bp; > + this->resolution_ns = _r; > + this->max_timeout = _m; > + this->num_alloc = 0; > + this->max_timers = _mt; > + this->first_free = 0; > + this->clk_src = _cs; > + this->timers = malloc(sizeof(odp_timer) * this->max_timers); > + if (this->timers == NULL) { > + ODP_ERR("%s: malloc failed\n", _n); > + abort(); > } > - odp_spinlock_unlock(&tick->lock); > - > - return 0; > + uint32_t i; > + for (i = 0; i < this->max_timers; i++) > + odp_timer_con(&this->timers[i]); > + for (i = 0; i < this->max_timers; i++) > + set_next_free(&this->timers[i], i + 1); > + odp_spinlock_init(&this->lock); > + if (this->clk_src == ODP_CLOCK_DEFAULT) > + timer_init(this); > + /* Make sure timer pool initialisation is globally observable */ > + /* before we return a pointer to it */ > + odp_sync_stores(); > } > > -static void notify_function(union sigval sigval) > +static odp_timer_pool *odp_timer_pool_new( > + const char *_n, > + odp_buffer_pool_t _bp, > + uint64_t _r, > + uint64_t _m, > + uint32_t _mt, > + bool _s, > + odp_timer_pool_clock_source_t _cs) > { > - uint64_t cur_tick; > - timeout_t *tmo; > - tick_t *tick; > - timer_ring_t *timer; > - > - timer = sigval.sival_ptr; > - > - if (timer->active == 0) { > - ODP_DBG("Timer (%u) not active\n", timer->timer_hdl); > - return; > + odp_timer_pool *this = malloc(sizeof(odp_timer_pool)); > + if (odp_unlikely(this == NULL)) { > + ODP_ERR("%s: malloc failed\n", _n); > + abort(); > } > + odp_timer_pool_con(this, _n, _bp, _r, _m, _mt, _s, _cs); > + return this; > +} > > - /* ODP_DBG("Tick\n"); */ > +static void odp_timer_pool_des(odp_timer_pool *this) > +{ > + if (this->shared) > + odp_spinlock_lock(&this->lock); > + if (this->num_alloc != 0) { > + /* It's a programming error to attempt to destroy a */ > + /* timer pool which is still in use */ > + ODP_ERR("%s: timers in use\n", this->name); > + abort(); > + } > + if (this->clk_src == ODP_CLOCK_DEFAULT) > + timer_exit(this); > + uint32_t i; > + for (i = 0; i < this->max_timers; i++) > + odp_timer_des(&this->timers[i]); > + free(this->timers); > + priority_queue_des(&this->pq); > + odp_sync_stores(); > +} > > - cur_tick = timer->cur_tick++; > +static void odp_timer_pool_del(odp_timer_pool *this) > +{ > + odp_timer_pool_des(this); > + free(this); > +} > > - odp_sync_stores(); > +static inline odp_timer *timer_alloc(odp_timer_pool *this, > + odp_queue_t queue, > + void *user_ptr, > + odp_buffer_t tmo_buf) > +{ > + odp_timer *tim = ODP_TIMER_INVALID; > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + if (odp_likely(this->num_alloc < this->max_timers)) { > + this->num_alloc++; > + /* Remove first unused timer from free list */ > + assert(this->first_free != this->max_timers); > + tim = &this->timers[this->first_free]; > + this->first_free = get_next_free(tim); > + /* Insert timer into priority queue */ > + if (odp_unlikely(!pq_register_element(&this->pq, > + &tim->pqelem))) { > + /* Unexpected internal error */ > + abort(); > + } > + /* Create timer */ > + setup(tim, queue, user_ptr, tmo_buf); > + } else { > + errno = ENFILE; /* Reusing file table overvlow */ > + } > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + return tim; > +} > > - tick = &timer->tick[cur_tick % MAX_TICKS]; > +static inline void timer_free(odp_timer_pool *this, odp_timer *tim) > +{ > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + /* Destroy timer */ > + odp_buffer_t buf = teardown(tim); > + /* Remove timer from priority queue */ > + pq_unregister_element(&this->pq, &tim->pqelem); > + /* Insert timer into free list */ > + set_next_free(tim, this->first_free); > + this->first_free = (tim - &this->timers[0]) / sizeof(this->timers[0]); > + assert(this->num_alloc != 0); > + this->num_alloc--; > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + if (buf != ODP_BUFFER_INVALID) > + odp_buffer_free(buf); > +} > > - while ((tmo = rem_tmo(tick)) != NULL) { > - odp_queue_t queue; > - odp_buffer_t buf; > +static inline void timer_reset(odp_timer_pool *this, > + odp_timer *tim, > + odp_timer_tick_t abs_tck) > +{ > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + /* Increase timer tag to make any pending timeout stale */ > + tim->tag++; > + /* Save requested timeout */ > + tim->req_tmo = abs_tck; > + /* Update timer position in priority queue */ > + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > +} > > - queue = tmo->queue; > - buf = tmo->buf; > +static inline void timer_reset_w_buf(odp_timer_pool *this, > + odp_timer *tim, > + odp_timer_tick_t abs_tck, > + odp_buffer_t user_buf) > +{ > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + /* Increase timer tag to make any pending timeout stale */ > + tim->tag++; > + /* Save requested timeout */ > + tim->req_tmo = abs_tck; > + /* Set flag indicating presence of user defined buffer */ > + tim->user_buf = true; > + /* Swap in new buffer, get any old buffer pointer */ > + odp_buffer_t old_buf = tim->tmo_buf; > + tim->tmo_buf = user_buf; > + /* Update timer position in priority queue */ > + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + /* Free old buffer if present */ > + if (odp_unlikely(old_buf != ODP_BUFFER_INVALID)) > + odp_buffer_free(old_buf); > +} > > - if (buf != tmo->tmo_buf) > - odp_buffer_free(tmo->tmo_buf); > +static inline void timer_cancel(odp_timer_pool *this, > + odp_timer *tim) > +{ > + odp_buffer_t tmo_buf = ODP_BUFFER_INVALID; > + if (odp_likely(this->shared)) > + odp_spinlock_lock(&this->lock); > + if (odp_unlikely(tim->user_buf)) { > + /* Swap out old user buffer */ > + tmo_buf = tim->tmo_buf; > + tim->tmo_buf = ODP_BUFFER_INVALID; > + tim->user_buf = false; > + } > + /* Else a normal timer (no user-defined buffer) */ > + /* Increase timer tag to make any pending timeout stale */ > + tim->tag++; > + /* Clear requested timeout */ > + tim->req_tmo = INVALID_PRIORITY; > + /* Remove timer from the priority queue */ > + pq_deactivate_element(&this->pq, &tim->pqelem); > + if (odp_likely(this->shared)) > + odp_spinlock_unlock(&this->lock); > + /* Free user-defined buffer if present */ > + if (odp_unlikely(tmo_buf != ODP_BUFFER_INVALID)) > + odp_buffer_free(tmo_buf); > +} > > - odp_queue_enq(queue, buf); > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, odp_timer_tick_t tick) > +{ > + odp_spinlock_lock(&tpid->lock); > + unsigned nexp = 0; > + odp_timer_t tim; > + tpid->tick = tick; > + while ((tim = (odp_timer_t)pq_release_element(&tpid->pq, tick)) != > + ODP_TIMER_INVALID) { > + assert(get_prio(&tim->pqelem) <= tick); > + expire(tim, tick); > + nexp++; > } > + odp_spinlock_unlock(&tpid->lock); > + return nexp; > } > > -static void timer_start(timer_ring_t *timer) > +/* Functions that use Linux/POSIX per-process timers and related facilities */ > +static void timer_notify(union sigval sigval) > +{ > + odp_timer_pool *tp = (odp_timer_pool *)sigval.sival_ptr; > + uint64_t new_tick = tp->tick + 1; > + (void)odp_timer_pool_expire(tp, new_tick); > +} > + > +static void timer_init(odp_timer_pool *tp) > { > struct sigevent sigev; > struct itimerspec ispec; > > - ODP_DBG("\nTimer (%u) starts\n", timer->timer_hdl); > + ODP_DBG("Creating POSIX timer for timer pool %s, period %" > + PRIu64" ns\n", tp->name, tp->resolution_ns); > > memset(&sigev, 0, sizeof(sigev)); > memset(&ispec, 0, sizeof(ispec)); > > sigev.sigev_notify = SIGEV_THREAD; > - sigev.sigev_notify_function = notify_function; > - sigev.sigev_value.sival_ptr = timer; > + sigev.sigev_notify_function = timer_notify; > + sigev.sigev_value.sival_ptr = tp; > > - if (timer_create(CLOCK_MONOTONIC, &sigev, &timer->timerid)) { > - ODP_DBG("Timer create failed\n"); > - return; > + if (timer_create(CLOCK_MONOTONIC, &sigev, &tp->timerid)) { > + perror("timer_create"); > + abort(); > } > > ispec.it_interval.tv_sec = 0; > - ispec.it_interval.tv_nsec = RESOLUTION_NS; > + ispec.it_interval.tv_nsec = tp->resolution_ns; > ispec.it_value.tv_sec = 0; > - ispec.it_value.tv_nsec = RESOLUTION_NS; > + ispec.it_value.tv_nsec = tp->resolution_ns; > > - if (timer_settime(timer->timerid, 0, &ispec, NULL)) { > - ODP_DBG("Timer set failed\n"); > - return; > + if (timer_settime(&tp->timerid, 0, &ispec, NULL)) { > + perror("timer_settime"); > + abort(); > } > - > - return; > } > > -int odp_timer_init_global(void) > +static void timer_exit(odp_timer_pool *tp) > { > - ODP_DBG("Timer init ..."); > - > - memset(&odp_timer, 0, sizeof(timer_global_t)); > - > - odp_spinlock_init(&odp_timer.lock); > - > - ODP_DBG("done\n"); > - > - return 0; > + if (timer_delete(tp->timerid) != 0) { > + perror("timer_delete"); > + abort(); > + } > } > > -int odp_timer_disarm_all(void) > +odp_timer_pool_t > +odp_timer_pool_create(const char *name, > + odp_buffer_pool_t buf_pool, > + uint64_t resolution_ns, > + uint64_t max_timeout, > + uint32_t num_timers, > + bool shared, > + odp_timer_pool_clock_source_t clk_src) > { > - int timers; > - struct itimerspec ispec; > - > - odp_spinlock_lock(&odp_timer.lock); > - > - timers = odp_timer.num_timers; > - > - ispec.it_interval.tv_sec = 0; > - ispec.it_interval.tv_nsec = 0; > - ispec.it_value.tv_sec = 0; > - ispec.it_value.tv_nsec = 0; > - > - for (; timers >= 0; timers--) { > - if (timer_settime(odp_timer.timer[timers].timerid, > - 0, &ispec, NULL)) { > - ODP_DBG("Timer reset failed\n"); > - odp_spinlock_unlock(&odp_timer.lock); > - return -1; > - } > - odp_timer.num_timers--; > + /* Verify that buffer pool can be used for timeouts */ > + odp_buffer_t buf = odp_buffer_alloc(buf_pool); > + if (buf == ODP_BUFFER_INVALID) { > + ODP_ERR("%s: Failed to allocate buffer\n", name); > + abort(); > } > + if (odp_buffer_type(buf) != ODP_BUFFER_TYPE_TIMEOUT) { > + ODP_ERR("%s: Buffer pool wrong type\n", name); > + abort(); > + } > + odp_buffer_free(buf); > + odp_timer_pool_t tp = odp_timer_pool_new(name, buf_pool, resolution_ns, > + max_timeout, num_timers, > + shared, clk_src); > + return tp; > +} > > - odp_spinlock_unlock(&odp_timer.lock); > +void odp_timer_pool_start(void) > +{ > + /* Nothing to do here */ > +} > > - return 0; > +void odp_timer_pool_destroy(odp_timer_pool_t tpid) > +{ > + odp_timer_pool_del(tpid); > } > > -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) > +uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, odp_timer_tick_t ticks) > { > - uint32_t id; > - timer_ring_t *timer; > - odp_timer_t timer_hdl; > - int i; > - (void) name; (void) resolution; (void) min_tmo; (void) max_tmo; > + return ticks * tpid->resolution_ns; > +} > > - odp_spinlock_lock(&odp_timer.lock); > +odp_timer_tick_t odp_timer_ns_to_tick(odp_timer_pool_t tpid, uint64_t ns) > +{ > + return (odp_timer_tick_t)(ns / tpid->resolution_ns); > +} > > - if (odp_timer.num_timers >= NUM_TIMERS) { > - odp_spinlock_unlock(&odp_timer.lock); > - return ODP_TIMER_INVALID; > - } > +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid) > +{ > + return tpid->tick; > +} > > - for (id = 0; id < NUM_TIMERS; id++) { > - if (odp_timer.timer[id].allocated == 0) > - break; > +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, > + odp_timer_pool_conf_t item) > +{ > + switch (item) { > + case ODP_TIMER_NAME: > + return (uint64_t)(tpid->name); > + case ODP_TIMER_RESOLUTION: > + return tpid->resolution_ns; > + case ODP_TIMER_MAX_TMO: > + return tpid->max_timeout; > + case ODP_TIMER_NUM_TIMERS: > + return tpid->max_timers; > + case ODP_TIMER_SHARED: > + return tpid->shared; > + default: > + return 0; > } > +} > > - timer = &odp_timer.timer[id]; > - timer->allocated = 1; > - odp_timer.num_timers++; > - > - odp_spinlock_unlock(&odp_timer.lock); > - > - timer_hdl = id + 1; > - > - timer->timer_hdl = timer_hdl; > - timer->pool = pool; > - timer->resolution_ns = RESOLUTION_NS; > - timer->max_ticks = MAX_TICKS; > - > - for (i = 0; i < MAX_TICKS; i++) { > - odp_spinlock_init(&timer->tick[i].lock); > - timer->tick[i].list = NULL; > +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, > + odp_queue_t queue, > + void *user_ptr) > +{ > + /* We check this because ODP_QUEUE_INVALID is used */ > + /* to indicate a free timer */ > + if (odp_unlikely(queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("%s: Invalid queue identifier\n", tpid->name); > + abort(); > } > - > - timer->active = 1; > - odp_sync_stores(); > - > - timer_start(timer); > - > - return timer_hdl; > + odp_buffer_t tmo_buf = odp_buffer_alloc(tpid->buf_pool); > + if (odp_likely(tmo_buf != ODP_BUFFER_INVALID)) { > + odp_timer *tim = timer_alloc(tpid, queue, user_ptr, tmo_buf); > + if (tim != ODP_TIMER_INVALID) { > + /* Success */ > + assert(tim->queue != ODP_QUEUE_INVALID); > + return tim; > + } > + odp_buffer_free(tmo_buf); > + } > + /* Else failed to allocate timeout event */ > + /* errno set by odp_buffer_alloc() or timer_alloc () */ > + return ODP_TIMER_INVALID; > } > > -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer_hdl, uint64_t tmo_tick, > - odp_queue_t queue, odp_buffer_t buf) > +void odp_timer_free(odp_timer_t tim) > { > - int id; > - uint64_t tick; > - uint64_t cur_tick; > - timeout_t *new_tmo; > - odp_buffer_t tmo_buf; > - odp_timeout_hdr_t *tmo_hdr; > - timer_ring_t *timer; > - > - id = timer_hdl - 1; > - timer = &odp_timer.timer[id]; > - > - cur_tick = timer->cur_tick; > - if (tmo_tick <= cur_tick) { > - ODP_DBG("timeout too close\n"); > - return ODP_TIMER_TMO_INVALID; > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_free(tp, tim); > +} > > - tick = tmo_tick - cur_tick; > - if (tick > MAX_TICKS) { > - ODP_DBG("timeout too far\n"); > - return ODP_TIMER_TMO_INVALID; > +void odp_timer_set_abs_w_buf(odp_timer_t tim, > + odp_timer_tick_t abs_tck, > + odp_buffer_t user_buf) > +{ > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_reset_w_buf(tp, tim, abs_tck, user_buf); > +} > > - tick = (cur_tick + tick) % MAX_TICKS; > - > - tmo_buf = odp_buffer_alloc(timer->pool); > - if (tmo_buf == ODP_BUFFER_INVALID) { > - ODP_DBG("alloc failed\n"); > - return ODP_TIMER_TMO_INVALID; > +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck) > +{ > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > } > - > - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo_buf); > - new_tmo = &tmo_hdr->meta; > - > - new_tmo->timer_id = id; > - new_tmo->tick = (int)tick; > - new_tmo->tmo_tick = tmo_tick; > - new_tmo->queue = queue; > - new_tmo->tmo_buf = tmo_buf; > - > - if (buf != ODP_BUFFER_INVALID) > - new_tmo->buf = buf; > - else > - new_tmo->buf = tmo_buf; > - > - add_tmo(&timer->tick[tick], new_tmo); > - > - return tmo_buf; > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_reset(tp, tim, abs_tck); > } > > -uint64_t odp_timer_tick_to_ns(odp_timer_t timer_hdl, uint64_t ticks) > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return ticks * odp_timer.timer[id].resolution_ns; > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > + } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_reset(tp, tim, tp->tick + rel_tck); > } > > -uint64_t odp_timer_ns_to_tick(odp_timer_t timer_hdl, uint64_t ns) > +void odp_timer_cancel(odp_timer_t tim) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return ns / odp_timer.timer[id].resolution_ns; > + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { > + ODP_ERR("Invalid timer %p\n", tim); > + abort(); > + } > + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); > + timer_cancel(tp, tim); > } > > -uint64_t odp_timer_resolution(odp_timer_t timer_hdl) > +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo_buf) > { > - uint32_t id; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + odp_timer *tim = tmo_hdr->timer; > + > + /* Make sure stores to '*tim' are visible */ > + odp_sync_stores(); > > - id = timer_hdl - 1; > - return odp_timer.timer[id].resolution_ns; > + /* Compare generation count (gc) of timeout and parent timer (if any)*/ > + if (odp_unlikely(tim == ODP_TIMER_INVALID || > + tmo_hdr->gc != tim->gc)) { > + /* Generation counters differ => timeout is orphaned */ > + return ODP_TMO_ORPHAN; > + } > + /* Else gen-cnts match => parent timer exists */ > + > + /* Return timeout to timer so that it can be delivered again */ > + tim->tmo_buf = tmo_buf; > + /* FIXME do we need some kind of synchronisation or locking here? */ > + > + /* Compare tags of timeout and parent timer */ > + /* Compare requested and actual timeout time */ > + if (odp_likely(tim->tag == tmo_hdr->tag && > + tim->req_tmo <= tmo_hdr->expiration)) { > + /* Tags match, actual timeout is after requested => good! */ > + return ODP_TMO_FRESH; > + } else { > + /* Tags don't match or actual timeout time is before */ > + /* requested */ > + /* Timer has been reset or cancelled and timeout is stale */ > + /* or timeout expired too early */ > + if (tim->req_tmo != INVALID_PRIORITY) { > + /* Reset the timer for requested timeout */ > + odp_timer_set_abs(tim, tim->req_tmo); > + } > + /* Else timer was cancelled, do nothing */ > + return ODP_TMO_STALE; > + } > } > > -uint64_t odp_timer_maximum_tmo(odp_timer_t timer_hdl) > +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo_buf) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return odp_timer.timer[id].max_ticks; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + odp_timer_t tim = tmo_hdr->timer; > + if (odp_likely(tim != ODP_TIMER_INVALID && tmo_hdr->gc == tim->gc)) > + return tim; > + else > + return ODP_TIMER_INVALID; > } > > -uint64_t odp_timer_current_tick(odp_timer_t timer_hdl) > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo_buf) > { > - uint32_t id; > - > - id = timer_hdl - 1; > - return odp_timer.timer[id].cur_tick; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + return tmo_hdr->expiration; > } > > -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf) > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo_buf) > { > - return (odp_timeout_t) buf; > + odp_timeout_hdr_t *tmo_hdr = > + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); > + return tmo_hdr->user_ptr; > } > > -uint64_t odp_timeout_tick(odp_timeout_t tmo) > +int odp_timer_init_global(void) > { > - odp_timeout_hdr_t *tmo_hdr = odp_timeout_hdr(tmo); > - return tmo_hdr->meta.tmo_tick; > + return 0; > } > diff --git a/platform/linux-generic/priority_queue.c b/platform/linux-generic/priority_queue.c > new file mode 100644 > index 0000000..ba4ba0e > --- /dev/null > +++ b/platform/linux-generic/priority_queue.c > @@ -0,0 +1,289 @@ > +#define NDEBUG /* Enabled by default by ODP build system */ > +#include <assert.h> > +#include <unistd.h> > +#include <stdlib.h> > +#include <string.h> > +#include <strings.h> > +#include <odp_hints.h> > +#include <odp_align.h> > +#include <odp_debug.h> > + > +#include "priority_queue.h" > + > + > +#define NUM_CHILDREN 4 > +#define CHILD(n) (NUM_CHILDREN * (n) + 1) > +#define PARENT(n) (((n) - 1) / NUM_CHILDREN) > + > +/* Internal nodes in the array */ > +typedef struct heap_node { > + pq_element *elem; > + /* Copy of elem->prio so we avoid unnecessary dereferencing */ > + pq_priority_t prio; > +} heap_node; > + > +static void pq_assert_heap(priority_queue *this); > + > +#define ALIGNMENT(p) (1U << ((unsigned)ffs((int)p) - 1U)) > + > +void priority_queue_con(priority_queue *this, uint32_t _max_elems) > +{ > + this->max_elems = _max_elems; > + this->reg_elems = 0; > + this->num_elems = 0; > + this->org_ptr = malloc((_max_elems + 64 / sizeof(heap_node)) * > + sizeof(heap_node)); > + if (odp_unlikely(this->org_ptr == NULL)) { > + ODP_ERR("malloc failed\n"); > + abort(); > + } > + this->heap = this->org_ptr; > + assert((size_t)&this->heap[1] % 8 == 0); > + /* Increment base address until first child (index 1) is cache line */ > + /* aligned and thus all children (e.g. index 1-4) stored in the */ > + /* same cache line. We are not interested in the alignment of */ > + /* heap[0] as this is a lone node */ > + while ((size_t)&this->heap[1] % ODP_CACHE_LINE_SIZE != 0) { > + /* Cast to ptr to struct member with the greatest alignment */ > + /* requirement */ > + this->heap = (heap_node *)((pq_priority_t *)this->heap + 1); > + } > +#if 0 > + printf("Alignment of heap[1]=%u\n", ALIGNMENT((size_t)&heap[1])); > + printf("Alignment of heap[CHILD(1)]=%u\n", > + ALIGNMENT((size_t)&heap[CHILD(1)])); > +#endif Ta bort. > + pq_assert_heap(this); > +} > + > +void priority_queue_des(priority_queue *this) > +{ > + pq_assert_heap(this); > + free(this->org_ptr); > +} > + > +#ifndef NDEBUG > +static uint32_t > +pq_assert_elem(priority_queue *this, uint32_t index, bool recurse) > +{ > + uint32_t num = 1; > + const pq_element *elem = this->heap[index].elem; > + assert(elem->index == index); > + assert(elem->prio == this->heap[index].prio); > + uint32_t child = CHILD(index); > + uint32_t i; > + for (i = 0; i < NUM_CHILDREN; i++, child++) { > + if (valid_index(this, child)) { > + assert(this->heap[child].elem != NULL); > + assert(this->heap[child].prio >= elem->prio); > + if (recurse) > + num += pq_assert_elem(this, child, recurse); > + } > + } > + return num; > +} > +#endif > + > +static void > +pq_assert_heap(priority_queue *this) > +{ > + (void)this; > +#ifndef NDEBUG > + uint32_t num = 0; > + if (odp_likely(this->num_elems != 0)) { > + assert(this->heap[0].elem != NULL); > + num += pq_assert_elem(this, 0, true); > + } > + assert(num == this->num_elems); > + unsigned i; > + for (i = 0; i < this->num_elems; i++) { > + assert(this->heap[i].elem != NULL); > + assert(this->heap[i].prio != INVALID_PRIORITY); > + } > +#endif > +} > + > +/* Bubble up to proper position */ > +void > +pq_bubble_up(priority_queue *this, pq_element *elem) > +{ > + assert(this->heap[elem->index].elem == elem); > + assert(this->heap[elem->index].prio == elem->prio); > + uint32_t current = elem->index; > + pq_priority_t prio = elem->prio; > + assert(current == 0 || this->heap[PARENT(current)].elem != NULL); > + /* Move up into proper position */ > + while (current != 0 && this->heap[PARENT(current)].prio > prio) { > + uint32_t parent = PARENT(current); > + assert(this->heap[parent].elem != NULL); > + /* Swap current with parent */ > + /* 1) Move parent down */ > + this->heap[current].elem = this->heap[parent].elem; > + this->heap[current].prio = this->heap[parent].prio; > + this->heap[current].elem->index = current; > + /* 2) Move current up to parent */ > + this->heap[parent].elem = elem; > + this->heap[parent].prio = prio; > + this->heap[parent].elem->index = parent; > + /* Continue moving elem until it is in the right place */ > + current = parent; > + } > + pq_assert_heap(this); > +} > + > +/* Find the smallest child that is smaller than the specified priority */ > +/* TODO very hot function! */ What do you want to say with this TODO? > +uint32_t pq_smallest_child(priority_queue *this, > + uint32_t index, > + pq_priority_t val) > +{ > + uint32_t smallest = index; > + uint32_t child = CHILD(index); > +#if 1 > + /* Unroll loop when all children exist */ > + if (odp_likely(valid_index(this, child + 3))) { > + if (this->heap[child + 0].prio < val) > + val = this->heap[smallest = child + 0].prio; > + if (this->heap[child + 1].prio < val) > + val = this->heap[smallest = child + 1].prio; > + if (this->heap[child + 2].prio < val) > + val = this->heap[smallest = child + 2].prio; > + if (this->heap[child + 3].prio < val) > + val = this->heap[smallest = child + 3].prio; Unneeded assignment of "val". > + return smallest; > + } > +#endif Remove "#if 1" > + uint32_t i; > + for (i = 0; i < NUM_CHILDREN; i++) { > + if (odp_unlikely(!valid_index(this, child + i))) > + break; > + if (this->heap[child + i].prio < val) { > + smallest = child + i; > + val = this->heap[smallest].prio; > + } > + } > + return smallest; > +} > + > +/* TODO very hot function, can it be optimised? */ > +void > +pq_bubble_down(priority_queue *this, pq_element *elem) > +{ > + assert(this->heap[elem->index].elem == elem); > + assert(this->heap[elem->index].prio == elem->prio); Haven't this been checked before so we don't need to check them every time we are in this function? > + uint32_t current = elem->index; > + pq_priority_t prio = elem->prio; > + for (;;) { > + uint32_t child = pq_smallest_child(this, current, prio); > + if (current == child) { > + /* No smaller child, we are done */ > + pq_assert_heap(this); > + return; > + } > + /* Element larger than smaller child, must move down */ > + assert(this->heap[child].elem != NULL); > + /* 1) Move child up to current */ > + this->heap[current].elem = this->heap[child].elem; > + this->heap[current].prio = this->heap[child].prio; > + /* 2) Move current down to child */ > + this->heap[child].elem = elem; > + this->heap[child].prio = prio; > + this->heap[child].elem->index = child; > + > + this->heap[current].elem->index = current; /* cache misses! */ > + /* Continue moving element until it is in the right place */ > + current = child; > + } > +} > + > +bool > +pq_register_element(priority_queue *this, pq_element *elem) > +{ > + if (odp_likely(this->reg_elems < this->max_elems)) { > + elem->pq = this; > + this->reg_elems++; > + return true; > + } > + return false; > +} > + > +void > +pq_unregister_element(priority_queue *this, pq_element *elem) > +{ > + assert(elem->pq == this); > + if (is_active(elem)) > + pq_deactivate_element(this, elem); > + elem->pq = NULL; > + this->reg_elems--; > +} > + > +void > +pq_activate_element(priority_queue *this, pq_element *elem, pq_priority_t prio) > +{ > + assert(elem->pq == this); > + /* Insert element at end */ > + uint32_t index = this->num_elems++; > + this->heap[index].elem = elem; > + this->heap[index].prio = prio; > + elem->index = index; > + elem->prio = prio; > + pq_bubble_up(this, elem); > +} > + > +void > +pq_deactivate_element(priority_queue *this, pq_element *elem) > +{ > + assert(elem->pq == this); > + if (odp_likely(is_active(elem))) { > + /* Swap element with last element */ > + uint32_t current = elem->index; > + uint32_t last = --this->num_elems; > + if (odp_likely(last != current)) { > + /* Move last element to current */ > + this->heap[current].elem = this->heap[last].elem; > + this->heap[current].prio = this->heap[last].prio; > + this->heap[current].elem->index = current; > + /* Bubble down old 'last' element to its proper place*/ > + if (this->heap[current].prio < elem->prio) > + pq_bubble_up(this, this->heap[current].elem); > + else > + pq_bubble_down(this, this->heap[current].elem); > + } > + elem->index = INVALID_INDEX; > + pq_assert_heap(this); > + } > +} > + > +void > +pq_reset_element(priority_queue *this, pq_element *elem, pq_priority_t prio) > +{ > + assert(prio != INVALID_PRIORITY); > + if (odp_likely(is_active(elem))) { > + assert(prio >= elem->prio); > + elem->prio = prio; > + this->heap[elem->index].prio = prio;/* cache misses here! */ > + pq_bubble_down(this, elem); > + pq_assert_heap(this); > + } else { > + pq_activate_element(this, elem, prio); > + } > +} > + > +pq_priority_t pq_first_priority(const priority_queue *this) > +{ > + return this->num_elems != 0 ? this->heap[0].prio : INVALID_PRIORITY; > +} > + > +pq_element * > +pq_release_element(priority_queue *this, pq_priority_t threshold) > +{ > + if (odp_likely(this->num_elems != 0 && > + this->heap[0].prio <= threshold)) { > + pq_element *elem = this->heap[0].elem; > + /* Remove element from heap */ > + pq_deactivate_element(this, elem); > + assert(elem->prio <= threshold); > + return elem; > + } > + return NULL; > +} > diff --git a/platform/linux-generic/priority_queue.h b/platform/linux-generic/priority_queue.h > new file mode 100644 > index 0000000..c461590 > --- /dev/null > +++ b/platform/linux-generic/priority_queue.h > @@ -0,0 +1,107 @@ > +#ifndef _PRIORITY_QUEUE_H > +#define _PRIORITY_QUEUE_H > + > +#include <assert.h> > +#include <stddef.h> > +#include <stdint.h> > +#include <stdbool.h> > + > +#define INVALID_INDEX ~0U > +#define INVALID_PRIORITY ((pq_priority_t)~0ULL) > + > +typedef uint64_t pq_priority_t; > + > +struct heap_node; > + > +typedef struct priority_queue { > + uint32_t max_elems;/* Number of elements in heap */ > + /* Number of registered elements (active + inactive) */ > + uint32_t reg_elems; > + uint32_t num_elems;/* Number of active elements */ > + struct heap_node *heap; > + struct heap_node *org_ptr; > +} priority_queue; > + > +/* The user gets a pointer to this structure */ > +typedef struct { > + /* Set when pq_element registered with priority queue */ > + priority_queue *pq; > + uint32_t index;/* Index into heap array */ > + pq_priority_t prio; > +} pq_element; > + > +/*** Operations on pq_element ***/ > + > +static inline void pq_element_con(pq_element *this) > +{ > + this->pq = NULL; > + this->index = INVALID_INDEX; > + this->prio = 0U; > +} > + > +static inline void pq_element_des(pq_element *this) > +{ > + (void)this; > + assert(this->index == INVALID_INDEX); > +} > + > +static inline priority_queue *get_pq(const pq_element *this) > +{ > + return this->pq; > +} > + > +static inline pq_priority_t get_prio(const pq_element *this) > +{ > + return this->prio; > +} > + > +static inline uint32_t get_index(const pq_element *this) > +{ > + return this->index; > +} > + > +static inline bool is_active(const pq_element *this) > +{ > + return this->index != INVALID_INDEX; > +} > + > +/*** Operations on priority_queue ***/ > + > +extern uint32_t pq_smallest_child(priority_queue *, uint32_t, pq_priority_t); > +extern void pq_bubble_down(priority_queue *, pq_element *); > +extern void pq_bubble_up(priority_queue *, pq_element *); > + > +static inline bool valid_index(priority_queue *this, uint32_t idx) > +{ > + return idx < this->num_elems; > +} > + > +extern void priority_queue_con(priority_queue *, uint32_t _max_elems); > +extern void priority_queue_des(priority_queue *); > + > +/* Register pq_element with priority queue */ > +/* Return false if priority queue full */ > +extern bool pq_register_element(priority_queue *, pq_element *); > + > +/* Activate and add pq_element to priority queue */ > +/* Element must be disarmed */ > +extern void pq_activate_element(priority_queue *, pq_element *, pq_priority_t); > + > +/* Reset (increase) priority for pq_element */ > +/* Element may be active or inactive (released) */ > +extern void pq_reset_element(priority_queue *, pq_element *, pq_priority_t); > + > +/* Deactivate and remove element from priority queue */ > +/* Element may be active or inactive (released) */ > +extern void pq_deactivate_element(priority_queue *, pq_element *); > + > +/* Unregister pq_element */ > +extern void pq_unregister_element(priority_queue *, pq_element *); > + > +/* Return priority of first element (lowest numerical value) */ > +extern pq_priority_t pq_first_priority(const priority_queue *); > + > +/* Deactivate and return first element if it's prio is <= threshold */ > +extern pq_element *pq_release_element(priority_queue *, pq_priority_t thresh); > + > +#endif /* _PRIORITY_QUEUE_H */ > diff --git a/test/api_test/odp_timer_ping.c b/test/api_test/odp_timer_ping.c > index c1cc255..c4332e3 100644 > --- a/test/api_test/odp_timer_ping.c > +++ b/test/api_test/odp_timer_ping.c > @@ -20,6 +20,7 @@ > * Otherwise timeout may happen bcz of slow nw speed > */ > > +#include <stdlib.h> > #include <unistd.h> > #include <fcntl.h> > #include <errno.h> > @@ -43,7 +44,8 @@ > #define PING_CNT 10 > #define PING_THRD 2 /* Send and Rx Ping thread */ Change the comment to: TX and RX ping thread Or: Send and receive ping thread Cheers, Anders > > -static odp_timer_t test_timer_ping; > +static odp_timer_pool_t tp; > +static odp_timer_t test_timer_ping = ODP_TIMER_INVALID; > static odp_timer_tmo_t test_ping_tmo; > > #define PKTSIZE 64 > @@ -123,15 +125,7 @@ static int listen_to_pingack(void) > (socklen_t *)&len); > if (bytes > 0) { > /* pkt rxvd therefore cancel the timeout */ > - if (odp_timer_cancel_tmo(test_timer_ping, > - test_ping_tmo) != 0) { > - ODP_ERR("cancel_tmo failed ..exiting listner thread\n"); > - /* avoid exiting from here even if tmo > - * failed for current ping, > - * allow subsequent ping_rx request */ > - err = -1; > - > - } > + odp_timer_cancel(test_timer_ping); > /* cruel bad hack used for sender, listner ipc.. > * euwww.. FIXME .. > */ > @@ -153,7 +147,7 @@ static int send_ping_request(struct sockaddr_in *addr) > int sd, cnt = 1; > struct packet pckt; > > - uint64_t tick; > + odp_timer_tick_t tick; > odp_queue_t queue; > odp_buffer_t buf; > > @@ -179,6 +173,12 @@ static int send_ping_request(struct sockaddr_in *addr) > > /* get the ping queue */ > queue = odp_queue_lookup("ping_timer_queue"); > + test_timer_ping = odp_timer_alloc(tp, queue, NULL); > + if (test_timer_ping == ODP_TIMER_INVALID) { > + ODP_ERR("Failed to allocate timer.\n"); > + err = -1; > + goto err; > + } > > for (i = 0; i < PING_CNT; i++) { > /* prepare icmp pkt */ > @@ -204,12 +204,10 @@ static int send_ping_request(struct sockaddr_in *addr) > printf(" icmp_sent msg_cnt %d\n", i); > > /* arm the timer */ > - tick = odp_timer_current_tick(test_timer_ping); > + tick = odp_timer_current_tick(tp); > > tick += 1000; > - test_ping_tmo = odp_timer_absolute_tmo(test_timer_ping, tick, > - queue, > - ODP_BUFFER_INVALID); > + odp_timer_set_abs(test_timer_ping, tick); > /* wait for timeout event */ > while ((buf = odp_queue_deq(queue)) == ODP_BUFFER_INVALID) { > /* flag true means ack rxvd.. a cruel hack as I > @@ -225,16 +223,24 @@ static int send_ping_request(struct sockaddr_in *addr) > } > } > > - /* free tmo_buf for timeout case */ > - if (buf != ODP_BUFFER_INVALID) { > + switch (odp_timer_tmo_status(buf)) { > + case ODP_TMO_FRESH: > ODP_DBG(" timeout msg_cnt [%i] \n", i); > /* so to avoid seg fault commented */ > - odp_buffer_free(buf); > err = -1; > + break; > + case ODP_TMO_STALE: > + /* Ignore stale timeouts */ > + break; > + case ODP_TMO_ORPHAN: > + ODP_ERR("Received orphaned timeout!\n"); > + abort(); > } > } > > err: > + if (test_timer_ping != ODP_TIMER_INVALID) > + odp_timer_free(test_timer_ping); > return err; > } > > @@ -335,7 +341,7 @@ int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED) > ODP_CACHE_LINE_SIZE, > ODP_BUFFER_TYPE_RAW); > if (pool == ODP_BUFFER_POOL_INVALID) { > - ODP_ERR("Pool create failed.\n"); > + ODP_ERR("Buffer pool create failed.\n"); > return -1; > } > > @@ -350,8 +356,18 @@ int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED) > return -1; > } > > - test_timer_ping = odp_timer_create("ping_timer", pool, > - 1000000, 1000000, 1000000000000UL); > + /* > + * Create timer pool > + */ > + tp = odp_timer_pool_create("timer_pool", pool, > + 1000000U, /* 1 millisecond */ > + 10000000000U, /* 10 seconds */ > + 1, false, ODP_CLOCK_DEFAULT); > + if (tp == ODP_TIMER_POOL_INVALID) { > + ODP_ERR("Timer pool create failed.\n"); > + return -1; > + } > + > odp_shm_print_all(); > > pingarg.thrdarg.testcase = ODP_TIMER_PING_TEST; > -- > 1.9.1 > > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp
diff --git a/example/timer/odp_timer_test.c b/example/timer/odp_timer_test.c index bf1d7df..0b4fedf 100644 --- a/example/timer/odp_timer_test.c +++ b/example/timer/odp_timer_test.c @@ -35,7 +35,6 @@ typedef struct { int core_count; /**< Core count*/ int resolution_us; /**< Timeout resolution in usec*/ - int min_us; /**< Minimum timeout in usec*/ int max_us; /**< Maximum timeout in usec*/ int period_us; /**< Timeout period in usec*/ int tmo_count; /**< Timeout count*/ @@ -45,18 +44,16 @@ typedef struct { /** @private Barrier for test synchronisation */ static odp_barrier_t test_barrier; -/** @private Timer handle*/ -static odp_timer_t test_timer; +/** @private Timer pool handle*/ +static odp_timer_pool_t tp; /** @private test timeout */ static void test_abs_timeouts(int thr, test_args_t *args) { - uint64_t tick; - uint64_t period; + odp_timer_tick_t period; uint64_t period_ns; odp_queue_t queue; - odp_buffer_t buf; int num; ODP_DBG(" [%i] test_timeouts\n", thr); @@ -64,37 +61,51 @@ static void test_abs_timeouts(int thr, test_args_t *args) queue = odp_queue_lookup("timer_queue"); period_ns = args->period_us*USEC; - period = odp_timer_ns_to_tick(test_timer, period_ns); + period = odp_timer_ns_to_tick(tp, period_ns); ODP_DBG(" [%i] period %"PRIu64" ticks, %"PRIu64" ns\n", thr, period, period_ns); - tick = odp_timer_current_tick(test_timer); + ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, + odp_timer_current_tick(tp)); - ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick); - - tick += period; - - if (odp_timer_absolute_tmo(test_timer, tick, queue, ODP_BUFFER_INVALID) - == ODP_TIMER_TMO_INVALID){ - ODP_DBG("Timeout request failed\n"); + odp_timer_t test_timer; + test_timer = odp_timer_alloc(tp, queue, NULL); + if (test_timer == ODP_TIMER_INVALID) { + ODP_ERR("Failed to allocate timer\n"); return; } + odp_timer_set_rel(test_timer, period); num = args->tmo_count; while (1) { - odp_timeout_t tmo; - - buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); + /* Local variables because received timeouts may not + originate from timer we created above */ + odp_timer_tmo_t tmo; + odp_timer_tick_t tick; + odp_timer_t hdl; + + /* Get the next ready buffer/timeout */ + tmo = odp_schedule_one(&queue, ODP_SCHED_WAIT); + switch (odp_timer_tmo_status(tmo)) { + case ODP_TMO_FRESH: + break; + case ODP_TMO_STALE: + ODP_DBG("[%i] Stale timeout received\n", thr); + break; + case ODP_TMO_ORPHAN: + ODP_DBG("[%i] Orphaned timeout received\n", + thr); + odp_buffer_free(tmo); + continue; + } - tmo = odp_timeout_from_buffer(buf); - tick = odp_timeout_tick(tmo); + tick = odp_timer_get_expiry(tmo); + hdl = odp_timer_get_handle(tmo); ODP_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, tick); - odp_buffer_free(buf); - num--; if (num == 0) @@ -102,10 +113,13 @@ static void test_abs_timeouts(int thr, test_args_t *args) tick += period; - odp_timer_absolute_tmo(test_timer, tick, - queue, ODP_BUFFER_INVALID); + if (hdl != ODP_TIMER_INVALID) + odp_timer_set_abs(hdl, tick); } + odp_timer_cancel(test_timer); + odp_timer_free(test_timer); + if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) odp_schedule_release_atomic(); } @@ -159,7 +173,6 @@ static void print_usage(void) printf("Options:\n"); printf(" -c, --count <number> core count, core IDs start from 1\n"); printf(" -r, --resolution <us> timeout resolution in usec\n"); - printf(" -m, --min <us> minimum timeout in usec\n"); printf(" -x, --max <us> maximum timeout in usec\n"); printf(" -p, --period <us> timeout period in usec\n"); printf(" -t, --timeouts <count> timeout repeat count\n"); @@ -183,7 +196,6 @@ static void parse_args(int argc, char *argv[], test_args_t *args) static struct option longopts[] = { {"count", required_argument, NULL, 'c'}, {"resolution", required_argument, NULL, 'r'}, - {"min", required_argument, NULL, 'm'}, {"max", required_argument, NULL, 'x'}, {"period", required_argument, NULL, 'p'}, {"timeouts", required_argument, NULL, 't'}, @@ -194,14 +206,13 @@ static void parse_args(int argc, char *argv[], test_args_t *args) /* defaults */ args->core_count = 0; /* all cores */ args->resolution_us = 10000; - args->min_us = args->resolution_us; args->max_us = 100000000; args->period_us = 1000000; args->tmo_count = 30; while (1) { opt = getopt_long(argc, argv, "+c:r:m:x:p:t:h", - longopts, &long_index); + longopts, &long_index); if (opt == -1) break; /* No more options */ @@ -213,9 +224,6 @@ static void parse_args(int argc, char *argv[], test_args_t *args) case 'r': args->resolution_us = atoi(optarg); break; - case 'm': - args->min_us = atoi(optarg); - break; case 'x': args->max_us = atoi(optarg); break; @@ -299,7 +307,6 @@ int main(int argc, char *argv[]) printf("first core: %i\n", first_core); printf("resolution: %i usec\n", args.resolution_us); - printf("min timeout: %i usec\n", args.min_us); printf("max timeout: %i usec\n", args.max_us); printf("period: %i usec\n", args.period_us); printf("timeouts: %i\n", args.tmo_count); @@ -323,10 +330,23 @@ int main(int argc, char *argv[]) ODP_BUFFER_TYPE_TIMEOUT); if (pool == ODP_BUFFER_POOL_INVALID) { - ODP_ERR("Pool create failed.\n"); + ODP_ERR("Buffer pool create failed.\n"); return -1; } + tp = odp_timer_pool_create("timer_pool", pool, + args.resolution_us*USEC, + args.max_us*USEC, + num_workers, /* One timer per worker */ + true, + ODP_CLOCK_DEFAULT); + if (tp == ODP_TIMER_POOL_INVALID) { + ODP_ERR("Timer pool create failed.\n"); + return -1; + } + + odp_shm_print_all(); + /* * Create a queue for timer test */ @@ -342,13 +362,6 @@ int main(int argc, char *argv[]) return -1; } - test_timer = odp_timer_create("test_timer", pool, - args.resolution_us*USEC, - args.min_us*USEC, - args.max_us*USEC); - - odp_shm_print_all(); - printf("CPU freq %"PRIu64" hz\n", odp_sys_cpu_hz()); printf("Cycles vs nanoseconds:\n"); ns = 0; diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am index f4dfdc1..e2bc1a7 100644 --- a/platform/linux-generic/Makefile.am +++ b/platform/linux-generic/Makefile.am @@ -72,4 +72,5 @@ __LIB__libodp_la_SOURCES = \ odp_thread.c \ odp_ticketlock.c \ odp_time.c \ + priority_queue.c \ odp_timer.c diff --git a/platform/linux-generic/include/api/odp_timer.h b/platform/linux-generic/include/api/odp_timer.h index 01db839..e5f961c 100644 --- a/platform/linux-generic/include/api/odp_timer.h +++ b/platform/linux-generic/include/api/odp_timer.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013, Linaro Limited +/* Copyright (c) 2014, Linaro Limited * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -8,7 +8,175 @@ /** * @file * - * ODP timer + * ODP timer service + * + +//Example #1 Retransmission timer (e.g. for reliable connections) + +//Create timer pool for reliable connections +#define SEC 1000000000ULL //1s expressed in nanoseconds +odp_timer_pool_t tcp_tpid = + odp_timer_pool_create("TCP", + buffer_pool, + 1000000,//resolution 1ms + 7200 * SEC,//max tmo length 2hours + 40000,//num_timers + true,//shared + ODP_CLOCK_DEFAULT + ); +if (tcp_tpid == ODP_TIMER_POOL_INVALID) +{ + //Failed to create timer pool => fatal error +} + + +//Setting up a new connection +//Allocate retransmission timeout (identical for supervision timeout) +//The user pointer points back to the connection context +conn->ret_tim = odp_timer_alloc(tcp_tpid, queue, conn); +//Check if all resources were successfully allocated +if (conn->ret_tim == ODP_TIMER_INVALID) +{ + //Failed to allocate all resources for connection => tear down + //Destroy timeout + odp_timer_free(conn->ret_tim); + //Tear down connection + ... + return false; +} +//All necessary resources successfully allocated +//Compute initial retransmission length in timer ticks +conn->ret_len = odp_timer_ns_to_tick(tcp_tpid, 3 * SEC);//Per RFC1122 +//Arm the timer +odp_timer_set_rel(conn->ret_tim, conn->ret_len); +return true; + + +//A packet for the connection has just been transmitted +//Reset the retransmission timer +odp_timer_set_rel(conn->ret_tim, conn->ret_len); + + +//A retransmission timeout for the connection has been received +//Check if timeout is fresh or stale, for stale timeouts we need to reset the +//timer +switch (odp_timer_tmo_status(tmo)) +{ + case ODP_TMO_FRESH : + //Fresh timeout, last transmitted packet not acked in time => + retransmit + //Get connection from timeout event + conn = odp_timer_get_userptr(tmo); + //Retransmit last packet (e.g. TCP segment) + ... + //Re-arm timer using original delta value + odp_timer_set_rel(conn->ret_tim, conn->ret_len); + break; + case ODP_TMO_STALE : + break;//Do nothing + case ODP_TMO_ORPHAN : + odp_free_buffer(tmo); + return;//Get out of here +} + + +//Example #2 Periodic tick + +//Create timer pool for periodic ticks +odp_timer_pool_t per_tpid = + odp_timer_pool_create("periodic-tick", + buffer_pool, + 1,//resolution 1ns + 1000000000,//maximum timeout length 1s + 10,//num_timers + false,//not shared + ODP_CLOCK_DEFAULT + ); +if (per_tpid == ODP_TIMER_POOL_INVALID) +{ + //Failed to create timer pool => fatal error +} + + +//Allocate periodic timer +tim_1733 = odp_timer_alloc(per_tpid, queue, NULL); +//Check if all resources were successfully allocated +if (tim_1733 == ODP_TIMER_INVALID) +{ + //Failed to allocate all resources => tear down + //Destroy timeout + odp_timer_free(tim_1733); + //Tear down other state + ... + return false; +} +//All necessary resources successfully allocated +//Compute tick period in timer ticks +period_1733 = odp_timer_ns_to_tick(per_tpid, 1000000000U / 1733U);//1733Hz +//Compute when next tick should expire +next_1733 = odp_timer_current_tick(per_tpid) + period_1733; +//Arm the periodic timer +odp_timer_set_abs(tim_1733, next_1733); +return true; + + + +//A periodic timer timeout has been received +//Must call odp_timer_tmo_status() on timeout! +ret = odp_timer_tmo_status(tmo); +//We expect the timeout is fresh since we are not calling set or cancel on +//active or expired timers in this example +assert(ret == ODP_TMO_FRESH); +//Do processing driven by timeout *before* +... +do { + //Compute when the timer should expire next + next_1733 += period_1733; + //Check that this is in the future + if (likely(next_1733 > odp_timer_current_tick(per_tpid)) + break;//Yes, done + //Else we missed a timeout + //Optionally attempt some recovery and/or logging of the problem + ... +} while (0); +//Re-arm periodic timer +odp_timer_set_abs(tim_1733, next_1733); +//Or do processing driven by timeout *after* +... +return; + +//Example #3 Tear down of flow +//ctx points to flow context data structure owned by application +//Free the timer, cancelling any timeout +odp_timer_free(ctx->timer);//Any enqueued timeout will be made invalid +//Continue tearing down and eventually freeing context +... +return; + +//A timeout has been received, check status +switch (odp_timer_tmo_status(tmo)) +{ + case ODP_TMO_FRESH : + //A flow has timed out, tear it down + //Find flow context from timeout + ctx = (context *)odp_timer_get_userptr(tmo); + //Free the supervision timer, any enqueued timeout will remain + odp_timer_free(ctx->tim); + //Free other flow related resources + ... + //Flow torn down + break; + case ODP_TMO_STALE : + //A stale timeout was received, timer automatically reset + break; + case ODP_TMO_ORPHAN : + //Orphaned timeout (from previously torn down flow) + //No corresponding timer or flow context + //Free the timeout + odp_buffer_free(tmo); + break; +} + */ #ifndef ODP_TIMER_H_ @@ -23,139 +191,325 @@ extern "C" { #include <odp_buffer_pool.h> #include <odp_queue.h> +/** +* ODP timer pool handle (platform dependent) +*/ +struct odp_timer_pool; +typedef struct odp_timer_pool *odp_timer_pool_t; /** - * ODP timer handle + * Invalid timer pool handle (platform dependent) */ -typedef uint32_t odp_timer_t; +#define ODP_TIMER_POOL_INVALID NULL -/** Invalid timer */ -#define ODP_TIMER_INVALID 0 +typedef enum odp_timer_pool_clock_source_e { + ODP_CLOCK_DEFAULT = 0, + /* Platform dependent which clock sources exist beyond + ODP_CLOCK_DEFAULT */ + ODP_CLOCK_NONE = 1 +} odp_timer_pool_clock_source_t; +/** +* ODP timer handle (platform dependent) +*/ +struct odp_timer; +typedef struct odp_timer *odp_timer_t; /** - * ODP timeout handle + * Invalid timer handle (platform dependent) */ -typedef odp_buffer_t odp_timer_tmo_t; - -/** Invalid timeout */ -#define ODP_TIMER_TMO_INVALID 0 +#define ODP_TIMER_INVALID NULL +/** + * ODP timeout event handle + */ +typedef odp_buffer_t odp_timer_tmo_t; /** - * Timeout notification + * ODP timeout status */ -typedef odp_buffer_t odp_timeout_t; +typedef enum odp_timer_tmo_status_e { + ODP_TMO_FRESH, /* Timeout is fresh, process it */ + ODP_TMO_STALE, /* Timer reset or cancelled, do nothing */ + ODP_TMO_ORPHAN,/* Timer deleted, free timeout */ +} odp_timer_tmo_status_t; + +/** +* ODP tick value +*/ +typedef uint64_t odp_timer_tick_t; /** - * Create a timer + * Create a timer pool * - * Creates a new timer with requested properties. + * Create a new timer pool. + * odp_timer_pool_create() is typically called once or a couple of times during + * application initialisation. * * @param name Name - * @param pool Buffer pool for allocating timeout notifications + * @param buf_pool Buffer pool for allocating timers * @param resolution Timeout resolution in nanoseconds - * @param min_tmo Minimum timeout duration in nanoseconds - * @param max_tmo Maximum timeout duration in nanoseconds + * @param max_tmo Maximum relative timeout in nanoseconds + * @param num_timers Number of supported timers (minimum) + * @param shared Shared or private timer pool. + * Operations on shared timers will include the necessary + * mutual exclusion, operations on private timers may not + * (mutual exclusion is the responsibility of the caller). + * @param clk_src Clock source to use + * + * @return Timer pool handle if successful, otherwise ODP_TIMER_POOL_INVALID + * and errno set + */ +odp_timer_pool_t +odp_timer_pool_create(const char *name, + odp_buffer_pool_t buf_pool, + uint64_t resolution, + uint64_t max_tmo, + uint32_t num_timers, + bool shared, + odp_timer_pool_clock_source_t clk_src); + +/** + * Start a timer pool + * + * 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 * - * @return Timer handle if successful, otherwise ODP_TIMER_INVALID + * Destroy a timer pool, freeing all resources. + * All timers must have been freed. + * + * @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, odp_timer_tick_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); +odp_timer_tick_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 + */ +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid); + +/** + * ODP timer configurations */ -uint64_t odp_timer_resolution(odp_timer_t timer); + +typedef enum odp_timer_pool_conf_e { + ODP_TIMER_NAME, /* Return name of timer pool */ + ODP_TIMER_RESOLUTION,/* Return the timer resolution (in ns) */ + ODP_TIMER_MAX_TMO, /* Return the maximum supported timeout (in ns) */ + ODP_TIMER_NUM_TIMERS,/* Return number of supported timers */ + ODP_TIMER_SHARED /* Return shared flag */ +} odp_timer_pool_conf_t; /** - * Maximum timeout in timer ticks + * Query different timer pool configurations, e.g. + * Timer resolution in nanoseconds + * Maximum timeout in timer ticks + * Number of supported timers + * Shared or private timer pool * - * @param timer Timer + * @param tpid Timer pool identifier + * @param item Configuration item being queried * - * @return Maximum timeout in timer ticks + * @return the requested piece of information or 0 for unknown item. */ -uint64_t odp_timer_maximum_tmo(odp_timer_t timer); +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, + odp_timer_pool_conf_t item); /** - * Current timer tick + * Allocate a timer * - * @param timer Timer + * Create a timer (allocating all necessary resources e.g. timeout event) from + * the timer pool. * - * @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 (copied to timeouts) + * + * @return Timer handle if successful, otherwise ODP_TIMER_INVALID and + * errno set. + */ +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, + odp_queue_t queue, + void *user_ptr); + +/** + * Free a timer + * + * Free (destroy) a timer, freeing all associated resources (e.g. default + * timeout event). An expired and enqueued timeout event will not be freed. + * It is the responsibility of the application to free this timeout when it + * is received. + * + * @param tim Timer handle */ -uint64_t odp_timer_current_tick(odp_timer_t timer); +void odp_timer_free(odp_timer_t tim); /** - * Request timeout with an absolute timer tick + * Set a timer (absolute time) with a user-defined timeout buffer * - * When tick reaches tmo_tick, the timer enqueues the timeout notification into - * the destination queue. + * Set (arm) the timer to expire at specific time. The user-defined + * buffer will be enqueued when the timer expires. + * Arming may fail (if the timer is in state EXPIRED), an earlier timeout + * will then be received. odp_timer_tmo_status() must be used to check if + * the received timeout is valid. * - * @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. + * Note: any invalid parameters will be treated as programming errors and will + * cause the application to abort. + * Note: a timeout too near in time may be delivered immediately. + * Note: a timeout too far away in time (beyond max_timeout) might be delivered + * early. * - * @return Timeout handle if successful, otherwise ODP_TIMER_TMO_INVALID + * @param tim Timer + * @param abs_tck Expiration time in absolute timer ticks + * @param user_buf The buffer to use as timeout event */ -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, uint64_t tmo_tick, - odp_queue_t queue, odp_buffer_t buf); +void odp_timer_set_abs_w_buf(odp_timer_t tim, + odp_timer_tick_t abs_tck, + odp_buffer_t user_buf); /** - * Cancel a timeout + * Set a timer with an absolute expiration time + * + * Set (arm) the timer to expire at a specific time. + * Arming may fail (if the timer is in state EXPIRED), an earlier timeout + * will then be received. odp_timer_tmo_status() must be used to check if + * the received timeout is valid. * - * @param timer Timer - * @param tmo Timeout to cancel + * Note: any invalid parameters will be treated as programming errors and will + * cause the application to abort. + * Note: a timeout too near in time may be delivered immediately. + * Note: a timeout too far away in time (beyond max_timeout) might be delivered + * early, it will automatically be reset by odp_timer_tmo_status(). * - * @return 0 if successful + * @param tim Timer + * @param abs_tck Expiration time in absolute timer ticks */ -int odp_timer_cancel_tmo(odp_timer_t timer, odp_timer_tmo_t tmo); +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck); /** - * Convert buffer handle to timeout handle + * Set a timer with a relative expiration time * - * @param buf Buffer handle + * Set (arm) the timer to expire at a relative future time. + * Arming may fail (if the timer is in state EXPIRED), + * an earlier timeout will then be received. odp_timer_tmo_status() must + * be used to check if the received timeout is valid. * - * @return Timeout buffer handle + * Note: any invalid parameters will be treated as programming errors and will + * cause the application to abort. + * Note: a timeout too near in time may be delivered immediately. + * Note: a timeout too far away in time (beyond max_timeout) might be delivered + * early, it will automatically be reset by odp_timer_tmo_status(). + * + * @param tim Timer + * @param rel_tck Expiration time in timer ticks relative to current time of + * the timer pool the timer belongs to */ -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf); +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck); /** - * Return absolute timeout tick + * Cancel a timer + * + * Cancel a timer, preventing future expiration and delivery. + * + * A timer that has already expired and been enqueued for delivery may be + * impossible to cancel and will instead be delivered to the destination queue. + * Use odp_timer_tmo_status() the check whether a received timeout is fresh or + * stale (cancelled). Stale timeouts will automatically be recycled. * - * @param tmo Timeout buffer handle + * Note: any invalid parameters will be treated as programming errors and will + * cause the application to abort. * - * @return Absolute timeout tick + * @param tim Timer handle */ -uint64_t odp_timeout_tick(odp_timeout_t tmo); +void odp_timer_cancel(odp_timer_t tim); + +/** + * Return fresh/stale/orphan status of timeout. + * + * Check a received timeout for orphaness (i.e. parent timer freed) and + * staleness (i.e. parent timer has been reset or cancelled after timeout + * was enqueued). + * If the timeout is fresh, it should be processed. + * If the timeout is stale, the timer will automatically be reset unless it + * was cancelled. + * If the timeout is orphaned, it should be freed (by the caller). + * + * Note: odp_timer_tmo_status() must be called on all received (not + * user-defined) timeouts! + * + * @param tmo Timeout + * + * @return ODP_TMO_FRESH, ODP_TMO_STALE, ODP_TMO_ORPHAN + */ +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo); + +/** + * Get timer handle + * + * Return Handle of parent timer. + * + * @param tmo Timeout + * + * @return Timer handle or ODP_TIMER_INVALID for orphaned timeouts + */ +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo); + +/** + * Get expiration time + * + * Return (actual) expiration time of timeout. + * + * @param tmo Timeout + * + * @return Expiration time + */ +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo); + +/** + * Get user pointer + * + * Return User pointer of timer associated with timeout. + * The user pointer is often used to point to some associated context. + * + * @param tmo Timeout + * + * @return User pointer + */ +void *odp_timer_get_userptr(odp_timer_tmo_t tmo); + +/* Helper functions */ +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, odp_timer_tick_t tick); #ifdef __cplusplus } diff --git a/platform/linux-generic/include/odp_timer_internal.h b/platform/linux-generic/include/odp_timer_internal.h index ad28f53..ff8f209 100644 --- a/platform/linux-generic/include/odp_timer_internal.h +++ b/platform/linux-generic/include/odp_timer_internal.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013, Linaro Limited +/* Copyright (c) 2014, Linaro Limited * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -8,72 +8,53 @@ /** * @file * - * ODP timer timeout descriptor - implementation internal + * ODP timeout descriptor - implementation internal */ #ifndef ODP_TIMER_INTERNAL_H_ #define ODP_TIMER_INTERNAL_H_ -#ifdef __cplusplus -extern "C" { -#endif - -#include <odp_std_types.h> -#include <odp_queue.h> -#include <odp_buffer.h> +#include <odp_align.h> +#include <odp_debug.h> #include <odp_buffer_internal.h> #include <odp_buffer_pool_internal.h> #include <odp_timer.h> -struct timeout_t; - -typedef struct timeout_t { - struct timeout_t *next; - int timer_id; - int tick; - uint64_t tmo_tick; - odp_queue_t queue; - odp_buffer_t buf; - odp_buffer_t tmo_buf; -} timeout_t; - - -struct odp_timeout_hdr_t; - /** - * Timeout notification header + * Internal Timeout header */ -typedef struct odp_timeout_hdr_t { +typedef struct { + /* common buffer header */ odp_buffer_hdr_t buf_hdr; - timeout_t meta; - + /* Requested expiration time */ + odp_timer_tick_t expiration; + /* User ptr inherited from parent timer */ + void *user_ptr; + /* Parent timer */ + odp_timer_t timer; + /* Tag inherited from parent timer at time of expiration */ + uint32_t tag; + /* Gen-cnt inherited from parent timer at time of creation */ + uint32_t gc; uint8_t buf_data[]; } odp_timeout_hdr_t; - - +/* C++ doesn't allow offsetof() on "non-POD" datatypes. Don't know why + odp_timeout_hdr_t is classified as non-POD, perhaps because of the + inheritance? */ ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) == - ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), - "ODP_TIMEOUT_HDR_T__SIZE_ERR"); - + ODP_OFFSETOF(odp_timeout_hdr_t, buf_data), + "sizeof(odp_timeout_hdr_t) == ODP_OFFSETOF(odp_timeout_hdr_t, buf_data)"); ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == 0, - "ODP_TIMEOUT_HDR_T__SIZE_ERR2"); - + "sizeof(odp_timeout_hdr_t) % sizeof(uint64_t) == 0"); /** - * Return timeout header + * Return the timeout header */ -static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_timeout_t tmo) +static inline odp_timeout_hdr_t *odp_timeout_hdr(odp_buffer_t buf) { - odp_buffer_hdr_t *buf_hdr = odp_buf_to_hdr((odp_buffer_t)tmo); - return (odp_timeout_hdr_t *)(uintptr_t)buf_hdr; -} - - - -#ifdef __cplusplus + return (odp_timeout_hdr_t *)odp_buf_to_hdr(buf); } -#endif #endif diff --git a/platform/linux-generic/odp_timer.c b/platform/linux-generic/odp_timer.c index 1bf37f9..cea47a3 100644 --- a/platform/linux-generic/odp_timer.c +++ b/platform/linux-generic/odp_timer.c @@ -1,402 +1,631 @@ -/* Copyright (c) 2013, Linaro Limited +/* Copyright (c) 2014, Linaro Limited * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#include <odp_timer.h> -#include <odp_timer_internal.h> -#include <odp_buffer_pool_internal.h> -#include <odp_internal.h> -#include <odp_atomic.h> -#include <odp_spinlock.h> -#include <odp_sync.h> -#include <odp_debug.h> -#include <signal.h> -#include <time.h> +/** + * @file + * + * ODP timer service + * + */ +#include <assert.h> +#include <errno.h> #include <string.h> - -#define NUM_TIMERS 1 -#define MAX_TICKS 1024 -#define RESOLUTION_NS 1000000 - - -typedef struct { - odp_spinlock_t lock; - timeout_t *list; -} tick_t; - -typedef struct { - int allocated; - volatile int active; - volatile uint64_t cur_tick; - timer_t timerid; - odp_timer_t timer_hdl; - odp_buffer_pool_t pool; - uint64_t resolution_ns; - uint64_t max_ticks; - tick_t tick[MAX_TICKS]; - -} timer_ring_t; - -typedef struct { - odp_spinlock_t lock; - int num_timers; - timer_ring_t timer[NUM_TIMERS]; - -} timer_global_t; - -/* Global */ -static timer_global_t odp_timer; - -static void add_tmo(tick_t *tick, timeout_t *tmo) +#include <stdlib.h> +#include <time.h> +#include <signal.h> +#include "odp_std_types.h" +#include "odp_buffer.h" +#include "odp_buffer_pool.h" +#include "odp_queue.h" +#include "odp_hints.h" +#include "odp_sync.h" +#include "odp_spinlock.h" +#include "odp_debug.h" +#include "odp_align.h" +#include "odp_shared_memory.h" +#include "odp_hints.h" +#include "odp_internal.h" +#include "odp_timer.h" +#include "odp_timer_internal.h" +#include "priority_queue.h" + +typedef struct odp_timer { + pq_element pqelem;/* Base class */ + odp_timer_tick_t req_tmo;/* Requested timeout tick */ + odp_buffer_t tmo_buf;/* ODP_BUFFER_INVALID if timeout enqueued */ + odp_queue_t queue;/* ODP_QUEUE_INVALID if timer is free */ + uint32_t tag;/* Reusing tag as next pointer/index when timer is free */ + uint32_t gc; + bool user_buf; /* User-defined buffer? */ +} odp_timer; + +/* Constructor for array of objects */ +static inline void odp_timer_con(odp_timer *this) { - odp_spinlock_lock(&tick->lock); - - tmo->next = tick->list; - tick->list = tmo; - - odp_spinlock_unlock(&tick->lock); + pq_element_con(&this->pqelem); + this->tmo_buf = ODP_BUFFER_INVALID; + this->queue = ODP_QUEUE_INVALID; + this->gc = 0; } -static timeout_t *rem_tmo(tick_t *tick) +/* Destructor */ +static inline void odp_timer_des(odp_timer *this) { - timeout_t *tmo; - - odp_spinlock_lock(&tick->lock); - - tmo = tick->list; - - if (tmo) - tick->list = tmo->next; - - odp_spinlock_unlock(&tick->lock); + assert(this->tmo_buf == ODP_BUFFER_INVALID); + assert(this->queue == ODP_QUEUE_INVALID); + pq_element_des(&this->pqelem); +} - if (tmo) - tmo->next = NULL; +/* Setup when timer is allocated */ +static void setup(odp_timer *this, + odp_queue_t _q, + void *_up, + odp_buffer_t _tmo) +{ + this->req_tmo = INVALID_PRIORITY; + this->tmo_buf = _tmo; + this->queue = _q; + this->tag = 0; + this->user_buf = false; + /* Initialise constant fields of timeout event */ + odp_timeout_hdr_t *tmo_hdr = + (odp_timeout_hdr_t *)odp_buf_to_hdr(this->tmo_buf); + tmo_hdr->gc = this->gc; + tmo_hdr->timer = this; + tmo_hdr->user_ptr = _up; + /* tmo_hdr->tag set at expiration time */ + /* tmo_hdr->expiration set at expiration time */ + assert(this->queue != ODP_QUEUE_INVALID); +} - return tmo; +/* Teardown when timer is freed */ +static odp_buffer_t teardown(odp_timer *this) +{ + /* Increase generation count to make pending timeout orphaned */ + ++this->gc; + odp_buffer_t buf = this->tmo_buf; + this->tmo_buf = ODP_BUFFER_INVALID; + this->queue = ODP_QUEUE_INVALID; + return buf; } -/** - * Search and delete tmo entry from timeout list - * return -1 : on error.. handle not in list - * 0 : success - */ -static int find_and_del_tmo(timeout_t **tmo, odp_timer_tmo_t handle) +static inline uint32_t get_next_free(odp_timer *this) { - timeout_t *cur, *prev; - prev = NULL; + assert(this->queue == ODP_QUEUE_INVALID); + return this->tag; +} - for (cur = *tmo; cur != NULL; prev = cur, cur = cur->next) { - if (cur->tmo_buf == handle) { - if (prev == NULL) - *tmo = cur->next; - else - prev->next = cur->next; +static inline void set_next_free(odp_timer *this, uint32_t nf) +{ + assert(this->queue == ODP_QUEUE_INVALID); + this->tag = nf; +} - break; +static inline void expire(odp_timer *this, odp_timer_tick_t tick) +{ + /* Timer expired, is there actually any timeout event */ + /* we can enqueue? */ + if (odp_likely(this->tmo_buf != ODP_BUFFER_INVALID)) { + /* Swap out timeout buffer */ + odp_buffer_t buf = this->tmo_buf; + this->tmo_buf = ODP_BUFFER_INVALID; + if (odp_likely(!this->user_buf)) { + odp_timeout_hdr_t *tmo_hdr = + (odp_timeout_hdr_t *)odp_buf_to_hdr(buf); + /* Copy tag from timer */ + /* and actual expiration tick from timer pool */ + tmo_hdr->tag = this->tag; + tmo_hdr->expiration = tick; } + /* Else don't touch user-defined buffer */ + int rc = odp_queue_enq(this->queue, buf); + if (rc != 0) + abort(); } - - if (!cur) - /* couldn't find tmo in list */ - return -1; - - /* application to free tmo_buf provided by absolute_tmo call */ - return 0; + /* No, timeout event already enqueued */ } -int odp_timer_cancel_tmo(odp_timer_t timer_hdl, odp_timer_tmo_t tmo) +typedef struct odp_timer_pool { + priority_queue pq; + uint64_t tick; + bool shared; + odp_spinlock_t lock; + const char *name; + odp_buffer_pool_t buf_pool; + uint64_t resolution_ns; + uint64_t max_timeout; + odp_timer *timers; + uint32_t num_alloc;/* Current number of allocated timers */ + uint32_t max_timers;/* Max number of timers */ + uint32_t first_free;/* 0..max_timers-1 => free timer */ + timer_t timerid; + odp_timer_pool_clock_source_t clk_src; +} odp_timer_pool; + +/* Forward declarations */ +static void timer_init(odp_timer_pool *tp); +static void timer_exit(odp_timer_pool *tp); + +static void odp_timer_pool_con(odp_timer_pool *this, + const char *_n, + odp_buffer_pool_t _bp, + uint64_t _r, + uint64_t _m, + uint32_t _mt, + bool _s, + odp_timer_pool_clock_source_t _cs) { - int id; - uint64_t tick_idx; - timeout_t *cancel_tmo; - odp_timeout_hdr_t *tmo_hdr; - tick_t *tick; - - /* get id */ - id = timer_hdl - 1; - - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo); - /* get tmo_buf to cancel */ - cancel_tmo = &tmo_hdr->meta; - - tick_idx = cancel_tmo->tick; - tick = &odp_timer.timer[id].tick[tick_idx]; - - odp_spinlock_lock(&tick->lock); - /* search and delete tmo from tick list */ - if (find_and_del_tmo(&tick->list, tmo) != 0) { - odp_spinlock_unlock(&tick->lock); - ODP_DBG("Couldn't find the tmo (%d) in tick list\n", (int)tmo); - return -1; + priority_queue_con(&this->pq, _mt); + this->tick = 0; + this->shared = _s; + this->name = strdup(_n); + this->buf_pool = _bp; + this->resolution_ns = _r; + this->max_timeout = _m; + this->num_alloc = 0; + this->max_timers = _mt; + this->first_free = 0; + this->clk_src = _cs; + this->timers = malloc(sizeof(odp_timer) * this->max_timers); + if (this->timers == NULL) { + ODP_ERR("%s: malloc failed\n", _n); + abort(); } - odp_spinlock_unlock(&tick->lock); - - return 0; + uint32_t i; + for (i = 0; i < this->max_timers; i++) + odp_timer_con(&this->timers[i]); + for (i = 0; i < this->max_timers; i++) + set_next_free(&this->timers[i], i + 1); + odp_spinlock_init(&this->lock); + if (this->clk_src == ODP_CLOCK_DEFAULT) + timer_init(this); + /* Make sure timer pool initialisation is globally observable */ + /* before we return a pointer to it */ + odp_sync_stores(); } -static void notify_function(union sigval sigval) +static odp_timer_pool *odp_timer_pool_new( + const char *_n, + odp_buffer_pool_t _bp, + uint64_t _r, + uint64_t _m, + uint32_t _mt, + bool _s, + odp_timer_pool_clock_source_t _cs) { - uint64_t cur_tick; - timeout_t *tmo; - tick_t *tick; - timer_ring_t *timer; - - timer = sigval.sival_ptr; - - if (timer->active == 0) { - ODP_DBG("Timer (%u) not active\n", timer->timer_hdl); - return; + odp_timer_pool *this = malloc(sizeof(odp_timer_pool)); + if (odp_unlikely(this == NULL)) { + ODP_ERR("%s: malloc failed\n", _n); + abort(); } + odp_timer_pool_con(this, _n, _bp, _r, _m, _mt, _s, _cs); + return this; +} - /* ODP_DBG("Tick\n"); */ +static void odp_timer_pool_des(odp_timer_pool *this) +{ + if (this->shared) + odp_spinlock_lock(&this->lock); + if (this->num_alloc != 0) { + /* It's a programming error to attempt to destroy a */ + /* timer pool which is still in use */ + ODP_ERR("%s: timers in use\n", this->name); + abort(); + } + if (this->clk_src == ODP_CLOCK_DEFAULT) + timer_exit(this); + uint32_t i; + for (i = 0; i < this->max_timers; i++) + odp_timer_des(&this->timers[i]); + free(this->timers); + priority_queue_des(&this->pq); + odp_sync_stores(); +} - cur_tick = timer->cur_tick++; +static void odp_timer_pool_del(odp_timer_pool *this) +{ + odp_timer_pool_des(this); + free(this); +} - odp_sync_stores(); +static inline odp_timer *timer_alloc(odp_timer_pool *this, + odp_queue_t queue, + void *user_ptr, + odp_buffer_t tmo_buf) +{ + odp_timer *tim = ODP_TIMER_INVALID; + if (odp_likely(this->shared)) + odp_spinlock_lock(&this->lock); + if (odp_likely(this->num_alloc < this->max_timers)) { + this->num_alloc++; + /* Remove first unused timer from free list */ + assert(this->first_free != this->max_timers); + tim = &this->timers[this->first_free]; + this->first_free = get_next_free(tim); + /* Insert timer into priority queue */ + if (odp_unlikely(!pq_register_element(&this->pq, + &tim->pqelem))) { + /* Unexpected internal error */ + abort(); + } + /* Create timer */ + setup(tim, queue, user_ptr, tmo_buf); + } else { + errno = ENFILE; /* Reusing file table overvlow */ + } + if (odp_likely(this->shared)) + odp_spinlock_unlock(&this->lock); + return tim; +} - tick = &timer->tick[cur_tick % MAX_TICKS]; +static inline void timer_free(odp_timer_pool *this, odp_timer *tim) +{ + if (odp_likely(this->shared)) + odp_spinlock_lock(&this->lock); + /* Destroy timer */ + odp_buffer_t buf = teardown(tim); + /* Remove timer from priority queue */ + pq_unregister_element(&this->pq, &tim->pqelem); + /* Insert timer into free list */ + set_next_free(tim, this->first_free); + this->first_free = (tim - &this->timers[0]) / sizeof(this->timers[0]); + assert(this->num_alloc != 0); + this->num_alloc--; + if (odp_likely(this->shared)) + odp_spinlock_unlock(&this->lock); + if (buf != ODP_BUFFER_INVALID) + odp_buffer_free(buf); +} - while ((tmo = rem_tmo(tick)) != NULL) { - odp_queue_t queue; - odp_buffer_t buf; +static inline void timer_reset(odp_timer_pool *this, + odp_timer *tim, + odp_timer_tick_t abs_tck) +{ + if (odp_likely(this->shared)) + odp_spinlock_lock(&this->lock); + /* Increase timer tag to make any pending timeout stale */ + tim->tag++; + /* Save requested timeout */ + tim->req_tmo = abs_tck; + /* Update timer position in priority queue */ + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); + if (odp_likely(this->shared)) + odp_spinlock_unlock(&this->lock); +} - queue = tmo->queue; - buf = tmo->buf; +static inline void timer_reset_w_buf(odp_timer_pool *this, + odp_timer *tim, + odp_timer_tick_t abs_tck, + odp_buffer_t user_buf) +{ + if (odp_likely(this->shared)) + odp_spinlock_lock(&this->lock); + /* Increase timer tag to make any pending timeout stale */ + tim->tag++; + /* Save requested timeout */ + tim->req_tmo = abs_tck; + /* Set flag indicating presence of user defined buffer */ + tim->user_buf = true; + /* Swap in new buffer, get any old buffer pointer */ + odp_buffer_t old_buf = tim->tmo_buf; + tim->tmo_buf = user_buf; + /* Update timer position in priority queue */ + pq_reset_element(&this->pq, &tim->pqelem, abs_tck); + if (odp_likely(this->shared)) + odp_spinlock_unlock(&this->lock); + /* Free old buffer if present */ + if (odp_unlikely(old_buf != ODP_BUFFER_INVALID)) + odp_buffer_free(old_buf); +} - if (buf != tmo->tmo_buf) - odp_buffer_free(tmo->tmo_buf); +static inline void timer_cancel(odp_timer_pool *this, + odp_timer *tim) +{ + odp_buffer_t tmo_buf = ODP_BUFFER_INVALID; + if (odp_likely(this->shared)) + odp_spinlock_lock(&this->lock); + if (odp_unlikely(tim->user_buf)) { + /* Swap out old user buffer */ + tmo_buf = tim->tmo_buf; + tim->tmo_buf = ODP_BUFFER_INVALID; + tim->user_buf = false; + } + /* Else a normal timer (no user-defined buffer) */ + /* Increase timer tag to make any pending timeout stale */ + tim->tag++; + /* Clear requested timeout */ + tim->req_tmo = INVALID_PRIORITY; + /* Remove timer from the priority queue */ + pq_deactivate_element(&this->pq, &tim->pqelem); + if (odp_likely(this->shared)) + odp_spinlock_unlock(&this->lock); + /* Free user-defined buffer if present */ + if (odp_unlikely(tmo_buf != ODP_BUFFER_INVALID)) + odp_buffer_free(tmo_buf); +} - odp_queue_enq(queue, buf); +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, odp_timer_tick_t tick) +{ + odp_spinlock_lock(&tpid->lock); + unsigned nexp = 0; + odp_timer_t tim; + tpid->tick = tick; + while ((tim = (odp_timer_t)pq_release_element(&tpid->pq, tick)) != + ODP_TIMER_INVALID) { + assert(get_prio(&tim->pqelem) <= tick); + expire(tim, tick); + nexp++; } + odp_spinlock_unlock(&tpid->lock); + return nexp; } -static void timer_start(timer_ring_t *timer) +/* Functions that use Linux/POSIX per-process timers and related facilities */ +static void timer_notify(union sigval sigval) +{ + odp_timer_pool *tp = (odp_timer_pool *)sigval.sival_ptr; + uint64_t new_tick = tp->tick + 1; + (void)odp_timer_pool_expire(tp, new_tick); +} + +static void timer_init(odp_timer_pool *tp) { struct sigevent sigev; struct itimerspec ispec; - ODP_DBG("\nTimer (%u) starts\n", timer->timer_hdl); + ODP_DBG("Creating POSIX timer for timer pool %s, period %" + PRIu64" ns\n", tp->name, tp->resolution_ns); memset(&sigev, 0, sizeof(sigev)); memset(&ispec, 0, sizeof(ispec)); sigev.sigev_notify = SIGEV_THREAD; - sigev.sigev_notify_function = notify_function; - sigev.sigev_value.sival_ptr = timer; + sigev.sigev_notify_function = timer_notify; + sigev.sigev_value.sival_ptr = tp; - if (timer_create(CLOCK_MONOTONIC, &sigev, &timer->timerid)) { - ODP_DBG("Timer create failed\n"); - return; + if (timer_create(CLOCK_MONOTONIC, &sigev, &tp->timerid)) { + perror("timer_create"); + abort(); } ispec.it_interval.tv_sec = 0; - ispec.it_interval.tv_nsec = RESOLUTION_NS; + ispec.it_interval.tv_nsec = tp->resolution_ns; ispec.it_value.tv_sec = 0; - ispec.it_value.tv_nsec = RESOLUTION_NS; + ispec.it_value.tv_nsec = tp->resolution_ns; - if (timer_settime(timer->timerid, 0, &ispec, NULL)) { - ODP_DBG("Timer set failed\n"); - return; + if (timer_settime(&tp->timerid, 0, &ispec, NULL)) { + perror("timer_settime"); + abort(); } - - return; } -int odp_timer_init_global(void) +static void timer_exit(odp_timer_pool *tp) { - ODP_DBG("Timer init ..."); - - memset(&odp_timer, 0, sizeof(timer_global_t)); - - odp_spinlock_init(&odp_timer.lock); - - ODP_DBG("done\n"); - - return 0; + if (timer_delete(tp->timerid) != 0) { + perror("timer_delete"); + abort(); + } } -int odp_timer_disarm_all(void) +odp_timer_pool_t +odp_timer_pool_create(const char *name, + odp_buffer_pool_t buf_pool, + uint64_t resolution_ns, + uint64_t max_timeout, + uint32_t num_timers, + bool shared, + odp_timer_pool_clock_source_t clk_src) { - int timers; - struct itimerspec ispec; - - odp_spinlock_lock(&odp_timer.lock); - - timers = odp_timer.num_timers; - - ispec.it_interval.tv_sec = 0; - ispec.it_interval.tv_nsec = 0; - ispec.it_value.tv_sec = 0; - ispec.it_value.tv_nsec = 0; - - for (; timers >= 0; timers--) { - if (timer_settime(odp_timer.timer[timers].timerid, - 0, &ispec, NULL)) { - ODP_DBG("Timer reset failed\n"); - odp_spinlock_unlock(&odp_timer.lock); - return -1; - } - odp_timer.num_timers--; + /* Verify that buffer pool can be used for timeouts */ + odp_buffer_t buf = odp_buffer_alloc(buf_pool); + if (buf == ODP_BUFFER_INVALID) { + ODP_ERR("%s: Failed to allocate buffer\n", name); + abort(); } + if (odp_buffer_type(buf) != ODP_BUFFER_TYPE_TIMEOUT) { + ODP_ERR("%s: Buffer pool wrong type\n", name); + abort(); + } + odp_buffer_free(buf); + odp_timer_pool_t tp = odp_timer_pool_new(name, buf_pool, resolution_ns, + max_timeout, num_timers, + shared, clk_src); + return tp; +} - odp_spinlock_unlock(&odp_timer.lock); +void odp_timer_pool_start(void) +{ + /* Nothing to do here */ +} - return 0; +void odp_timer_pool_destroy(odp_timer_pool_t tpid) +{ + odp_timer_pool_del(tpid); } -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) +uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, odp_timer_tick_t ticks) { - uint32_t id; - timer_ring_t *timer; - odp_timer_t timer_hdl; - int i; - (void) name; (void) resolution; (void) min_tmo; (void) max_tmo; + return ticks * tpid->resolution_ns; +} - odp_spinlock_lock(&odp_timer.lock); +odp_timer_tick_t odp_timer_ns_to_tick(odp_timer_pool_t tpid, uint64_t ns) +{ + return (odp_timer_tick_t)(ns / tpid->resolution_ns); +} - if (odp_timer.num_timers >= NUM_TIMERS) { - odp_spinlock_unlock(&odp_timer.lock); - return ODP_TIMER_INVALID; - } +odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid) +{ + return tpid->tick; +} - for (id = 0; id < NUM_TIMERS; id++) { - if (odp_timer.timer[id].allocated == 0) - break; +uint64_t odp_timer_pool_query_conf(odp_timer_pool_t tpid, + odp_timer_pool_conf_t item) +{ + switch (item) { + case ODP_TIMER_NAME: + return (uint64_t)(tpid->name); + case ODP_TIMER_RESOLUTION: + return tpid->resolution_ns; + case ODP_TIMER_MAX_TMO: + return tpid->max_timeout; + case ODP_TIMER_NUM_TIMERS: + return tpid->max_timers; + case ODP_TIMER_SHARED: + return tpid->shared; + default: + return 0; } +} - timer = &odp_timer.timer[id]; - timer->allocated = 1; - odp_timer.num_timers++; - - odp_spinlock_unlock(&odp_timer.lock); - - timer_hdl = id + 1; - - timer->timer_hdl = timer_hdl; - timer->pool = pool; - timer->resolution_ns = RESOLUTION_NS; - timer->max_ticks = MAX_TICKS; - - for (i = 0; i < MAX_TICKS; i++) { - odp_spinlock_init(&timer->tick[i].lock); - timer->tick[i].list = NULL; +odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, + odp_queue_t queue, + void *user_ptr) +{ + /* We check this because ODP_QUEUE_INVALID is used */ + /* to indicate a free timer */ + if (odp_unlikely(queue == ODP_QUEUE_INVALID)) { + ODP_ERR("%s: Invalid queue identifier\n", tpid->name); + abort(); } - - timer->active = 1; - odp_sync_stores(); - - timer_start(timer); - - return timer_hdl; + odp_buffer_t tmo_buf = odp_buffer_alloc(tpid->buf_pool); + if (odp_likely(tmo_buf != ODP_BUFFER_INVALID)) { + odp_timer *tim = timer_alloc(tpid, queue, user_ptr, tmo_buf); + if (tim != ODP_TIMER_INVALID) { + /* Success */ + assert(tim->queue != ODP_QUEUE_INVALID); + return tim; + } + odp_buffer_free(tmo_buf); + } + /* Else failed to allocate timeout event */ + /* errno set by odp_buffer_alloc() or timer_alloc () */ + return ODP_TIMER_INVALID; } -odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer_hdl, uint64_t tmo_tick, - odp_queue_t queue, odp_buffer_t buf) +void odp_timer_free(odp_timer_t tim) { - int id; - uint64_t tick; - uint64_t cur_tick; - timeout_t *new_tmo; - odp_buffer_t tmo_buf; - odp_timeout_hdr_t *tmo_hdr; - timer_ring_t *timer; - - id = timer_hdl - 1; - timer = &odp_timer.timer[id]; - - cur_tick = timer->cur_tick; - if (tmo_tick <= cur_tick) { - ODP_DBG("timeout too close\n"); - return ODP_TIMER_TMO_INVALID; + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { + ODP_ERR("Invalid timer %p\n", tim); + abort(); } + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); + timer_free(tp, tim); +} - tick = tmo_tick - cur_tick; - if (tick > MAX_TICKS) { - ODP_DBG("timeout too far\n"); - return ODP_TIMER_TMO_INVALID; +void odp_timer_set_abs_w_buf(odp_timer_t tim, + odp_timer_tick_t abs_tck, + odp_buffer_t user_buf) +{ + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { + ODP_ERR("Invalid timer %p\n", tim); + abort(); } + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); + timer_reset_w_buf(tp, tim, abs_tck, user_buf); +} - tick = (cur_tick + tick) % MAX_TICKS; - - tmo_buf = odp_buffer_alloc(timer->pool); - if (tmo_buf == ODP_BUFFER_INVALID) { - ODP_DBG("alloc failed\n"); - return ODP_TIMER_TMO_INVALID; +void odp_timer_set_abs(odp_timer_t tim, odp_timer_tick_t abs_tck) +{ + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { + ODP_ERR("Invalid timer %p\n", tim); + abort(); } - - tmo_hdr = odp_timeout_hdr((odp_timeout_t) tmo_buf); - new_tmo = &tmo_hdr->meta; - - new_tmo->timer_id = id; - new_tmo->tick = (int)tick; - new_tmo->tmo_tick = tmo_tick; - new_tmo->queue = queue; - new_tmo->tmo_buf = tmo_buf; - - if (buf != ODP_BUFFER_INVALID) - new_tmo->buf = buf; - else - new_tmo->buf = tmo_buf; - - add_tmo(&timer->tick[tick], new_tmo); - - return tmo_buf; + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); + timer_reset(tp, tim, abs_tck); } -uint64_t odp_timer_tick_to_ns(odp_timer_t timer_hdl, uint64_t ticks) +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck) { - uint32_t id; - - id = timer_hdl - 1; - return ticks * odp_timer.timer[id].resolution_ns; + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { + ODP_ERR("Invalid timer %p\n", tim); + abort(); + } + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); + timer_reset(tp, tim, tp->tick + rel_tck); } -uint64_t odp_timer_ns_to_tick(odp_timer_t timer_hdl, uint64_t ns) +void odp_timer_cancel(odp_timer_t tim) { - uint32_t id; - - id = timer_hdl - 1; - return ns / odp_timer.timer[id].resolution_ns; + if (odp_unlikely(tim->queue == ODP_QUEUE_INVALID)) { + ODP_ERR("Invalid timer %p\n", tim); + abort(); + } + odp_timer_pool *tp = (odp_timer_pool *)get_pq(&tim->pqelem); + timer_cancel(tp, tim); } -uint64_t odp_timer_resolution(odp_timer_t timer_hdl) +odp_timer_tmo_status_t odp_timer_tmo_status(odp_timer_tmo_t tmo_buf) { - uint32_t id; + odp_timeout_hdr_t *tmo_hdr = + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); + odp_timer *tim = tmo_hdr->timer; + + /* Make sure stores to '*tim' are visible */ + odp_sync_stores(); - id = timer_hdl - 1; - return odp_timer.timer[id].resolution_ns; + /* Compare generation count (gc) of timeout and parent timer (if any)*/ + if (odp_unlikely(tim == ODP_TIMER_INVALID || + tmo_hdr->gc != tim->gc)) { + /* Generation counters differ => timeout is orphaned */ + return ODP_TMO_ORPHAN; + } + /* Else gen-cnts match => parent timer exists */ + + /* Return timeout to timer so that it can be delivered again */ + tim->tmo_buf = tmo_buf; + /* FIXME do we need some kind of synchronisation or locking here? */ + + /* Compare tags of timeout and parent timer */ + /* Compare requested and actual timeout time */ + if (odp_likely(tim->tag == tmo_hdr->tag && + tim->req_tmo <= tmo_hdr->expiration)) { + /* Tags match, actual timeout is after requested => good! */ + return ODP_TMO_FRESH; + } else { + /* Tags don't match or actual timeout time is before */ + /* requested */ + /* Timer has been reset or cancelled and timeout is stale */ + /* or timeout expired too early */ + if (tim->req_tmo != INVALID_PRIORITY) { + /* Reset the timer for requested timeout */ + odp_timer_set_abs(tim, tim->req_tmo); + } + /* Else timer was cancelled, do nothing */ + return ODP_TMO_STALE; + } } -uint64_t odp_timer_maximum_tmo(odp_timer_t timer_hdl) +odp_timer_t odp_timer_get_handle(odp_timer_tmo_t tmo_buf) { - uint32_t id; - - id = timer_hdl - 1; - return odp_timer.timer[id].max_ticks; + odp_timeout_hdr_t *tmo_hdr = + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); + odp_timer_t tim = tmo_hdr->timer; + if (odp_likely(tim != ODP_TIMER_INVALID && tmo_hdr->gc == tim->gc)) + return tim; + else + return ODP_TIMER_INVALID; } -uint64_t odp_timer_current_tick(odp_timer_t timer_hdl) +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo_buf) { - uint32_t id; - - id = timer_hdl - 1; - return odp_timer.timer[id].cur_tick; + odp_timeout_hdr_t *tmo_hdr = + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); + return tmo_hdr->expiration; } -odp_timeout_t odp_timeout_from_buffer(odp_buffer_t buf) +void *odp_timer_get_userptr(odp_timer_tmo_t tmo_buf) { - return (odp_timeout_t) buf; + odp_timeout_hdr_t *tmo_hdr = + (odp_timeout_hdr_t *)odp_buf_to_hdr(tmo_buf); + return tmo_hdr->user_ptr; } -uint64_t odp_timeout_tick(odp_timeout_t tmo) +int odp_timer_init_global(void) { - odp_timeout_hdr_t *tmo_hdr = odp_timeout_hdr(tmo); - return tmo_hdr->meta.tmo_tick; + return 0; } diff --git a/platform/linux-generic/priority_queue.c b/platform/linux-generic/priority_queue.c new file mode 100644 index 0000000..ba4ba0e --- /dev/null +++ b/platform/linux-generic/priority_queue.c @@ -0,0 +1,289 @@ +#define NDEBUG /* Enabled by default by ODP build system */ +#include <assert.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <odp_hints.h> +#include <odp_align.h> +#include <odp_debug.h> + +#include "priority_queue.h" + + +#define NUM_CHILDREN 4 +#define CHILD(n) (NUM_CHILDREN * (n) + 1) +#define PARENT(n) (((n) - 1) / NUM_CHILDREN) + +/* Internal nodes in the array */ +typedef struct heap_node { + pq_element *elem; + /* Copy of elem->prio so we avoid unnecessary dereferencing */ + pq_priority_t prio; +} heap_node; + +static void pq_assert_heap(priority_queue *this); + +#define ALIGNMENT(p) (1U << ((unsigned)ffs((int)p) - 1U)) + +void priority_queue_con(priority_queue *this, uint32_t _max_elems) +{ + this->max_elems = _max_elems; + this->reg_elems = 0; + this->num_elems = 0; + this->org_ptr = malloc((_max_elems + 64 / sizeof(heap_node)) * + sizeof(heap_node)); + if (odp_unlikely(this->org_ptr == NULL)) { + ODP_ERR("malloc failed\n"); + abort(); + } + this->heap = this->org_ptr; + assert((size_t)&this->heap[1] % 8 == 0); + /* Increment base address until first child (index 1) is cache line */ + /* aligned and thus all children (e.g. index 1-4) stored in the */ + /* same cache line. We are not interested in the alignment of */ + /* heap[0] as this is a lone node */ + while ((size_t)&this->heap[1] % ODP_CACHE_LINE_SIZE != 0) { + /* Cast to ptr to struct member with the greatest alignment */ + /* requirement */ + this->heap = (heap_node *)((pq_priority_t *)this->heap + 1); + } +#if 0 + printf("Alignment of heap[1]=%u\n", ALIGNMENT((size_t)&heap[1])); + printf("Alignment of heap[CHILD(1)]=%u\n", + ALIGNMENT((size_t)&heap[CHILD(1)])); +#endif + pq_assert_heap(this); +} + +void priority_queue_des(priority_queue *this) +{ + pq_assert_heap(this); + free(this->org_ptr); +} + +#ifndef NDEBUG +static uint32_t +pq_assert_elem(priority_queue *this, uint32_t index, bool recurse) +{ + uint32_t num = 1; + const pq_element *elem = this->heap[index].elem; + assert(elem->index == index); + assert(elem->prio == this->heap[index].prio); + uint32_t child = CHILD(index); + uint32_t i; + for (i = 0; i < NUM_CHILDREN; i++, child++) { + if (valid_index(this, child)) { + assert(this->heap[child].elem != NULL); + assert(this->heap[child].prio >= elem->prio); + if (recurse) + num += pq_assert_elem(this, child, recurse); + } + } + return num; +} +#endif + +static void +pq_assert_heap(priority_queue *this) +{ + (void)this; +#ifndef NDEBUG + uint32_t num = 0; + if (odp_likely(this->num_elems != 0)) { + assert(this->heap[0].elem != NULL); + num += pq_assert_elem(this, 0, true); + } + assert(num == this->num_elems); + unsigned i; + for (i = 0; i < this->num_elems; i++) { + assert(this->heap[i].elem != NULL); + assert(this->heap[i].prio != INVALID_PRIORITY); + } +#endif +} + +/* Bubble up to proper position */ +void +pq_bubble_up(priority_queue *this, pq_element *elem) +{ + assert(this->heap[elem->index].elem == elem); + assert(this->heap[elem->index].prio == elem->prio); + uint32_t current = elem->index; + pq_priority_t prio = elem->prio; + assert(current == 0 || this->heap[PARENT(current)].elem != NULL); + /* Move up into proper position */ + while (current != 0 && this->heap[PARENT(current)].prio > prio) { + uint32_t parent = PARENT(current); + assert(this->heap[parent].elem != NULL); + /* Swap current with parent */ + /* 1) Move parent down */ + this->heap[current].elem = this->heap[parent].elem; + this->heap[current].prio = this->heap[parent].prio; + this->heap[current].elem->index = current; + /* 2) Move current up to parent */ + this->heap[parent].elem = elem; + this->heap[parent].prio = prio; + this->heap[parent].elem->index = parent; + /* Continue moving elem until it is in the right place */ + current = parent; + } + pq_assert_heap(this); +} + +/* Find the smallest child that is smaller than the specified priority */ +/* TODO very hot function! */ +uint32_t pq_smallest_child(priority_queue *this, + uint32_t index, + pq_priority_t val) +{ + uint32_t smallest = index; + uint32_t child = CHILD(index); +#if 1 + /* Unroll loop when all children exist */ + if (odp_likely(valid_index(this, child + 3))) { + if (this->heap[child + 0].prio < val) + val = this->heap[smallest = child + 0].prio; + if (this->heap[child + 1].prio < val) + val = this->heap[smallest = child + 1].prio; + if (this->heap[child + 2].prio < val) + val = this->heap[smallest = child + 2].prio; + if (this->heap[child + 3].prio < val) + val = this->heap[smallest = child + 3].prio; + return smallest; + } +#endif + uint32_t i; + for (i = 0; i < NUM_CHILDREN; i++) { + if (odp_unlikely(!valid_index(this, child + i))) + break; + if (this->heap[child + i].prio < val) { + smallest = child + i; + val = this->heap[smallest].prio; + } + } + return smallest; +} + +/* TODO very hot function, can it be optimised? */ +void +pq_bubble_down(priority_queue *this, pq_element *elem) +{ + assert(this->heap[elem->index].elem == elem); + assert(this->heap[elem->index].prio == elem->prio); + uint32_t current = elem->index; + pq_priority_t prio = elem->prio; + for (;;) { + uint32_t child = pq_smallest_child(this, current, prio); + if (current == child) { + /* No smaller child, we are done */ + pq_assert_heap(this); + return; + } + /* Element larger than smaller child, must move down */ + assert(this->heap[child].elem != NULL); + /* 1) Move child up to current */ + this->heap[current].elem = this->heap[child].elem; + this->heap[current].prio = this->heap[child].prio; + /* 2) Move current down to child */ + this->heap[child].elem = elem; + this->heap[child].prio = prio; + this->heap[child].elem->index = child; + + this->heap[current].elem->index = current; /* cache misses! */ + /* Continue moving element until it is in the right place */ + current = child; + } +} + +bool +pq_register_element(priority_queue *this, pq_element *elem) +{ + if (odp_likely(this->reg_elems < this->max_elems)) { + elem->pq = this; + this->reg_elems++; + return true; + } + return false; +} + +void +pq_unregister_element(priority_queue *this, pq_element *elem) +{ + assert(elem->pq == this); + if (is_active(elem)) + pq_deactivate_element(this, elem); + elem->pq = NULL; + this->reg_elems--; +} + +void +pq_activate_element(priority_queue *this, pq_element *elem, pq_priority_t prio) +{ + assert(elem->pq == this); + /* Insert element at end */ + uint32_t index = this->num_elems++; + this->heap[index].elem = elem; + this->heap[index].prio = prio; + elem->index = index; + elem->prio = prio; + pq_bubble_up(this, elem); +} + +void +pq_deactivate_element(priority_queue *this, pq_element *elem) +{ + assert(elem->pq == this); + if (odp_likely(is_active(elem))) { + /* Swap element with last element */ + uint32_t current = elem->index; + uint32_t last = --this->num_elems; + if (odp_likely(last != current)) { + /* Move last element to current */ + this->heap[current].elem = this->heap[last].elem; + this->heap[current].prio = this->heap[last].prio; + this->heap[current].elem->index = current; + /* Bubble down old 'last' element to its proper place*/ + if (this->heap[current].prio < elem->prio) + pq_bubble_up(this, this->heap[current].elem); + else + pq_bubble_down(this, this->heap[current].elem); + } + elem->index = INVALID_INDEX; + pq_assert_heap(this); + } +} + +void +pq_reset_element(priority_queue *this, pq_element *elem, pq_priority_t prio) +{ + assert(prio != INVALID_PRIORITY); + if (odp_likely(is_active(elem))) { + assert(prio >= elem->prio); + elem->prio = prio; + this->heap[elem->index].prio = prio;/* cache misses here! */ + pq_bubble_down(this, elem); + pq_assert_heap(this); + } else { + pq_activate_element(this, elem, prio); + } +} + +pq_priority_t pq_first_priority(const priority_queue *this) +{ + return this->num_elems != 0 ? this->heap[0].prio : INVALID_PRIORITY; +} + +pq_element * +pq_release_element(priority_queue *this, pq_priority_t threshold) +{ + if (odp_likely(this->num_elems != 0 && + this->heap[0].prio <= threshold)) { + pq_element *elem = this->heap[0].elem; + /* Remove element from heap */ + pq_deactivate_element(this, elem); + assert(elem->prio <= threshold); + return elem; + } + return NULL; +} diff --git a/platform/linux-generic/priority_queue.h b/platform/linux-generic/priority_queue.h new file mode 100644 index 0000000..c461590 --- /dev/null +++ b/platform/linux-generic/priority_queue.h @@ -0,0 +1,107 @@ +#ifndef _PRIORITY_QUEUE_H +#define _PRIORITY_QUEUE_H + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +#define INVALID_INDEX ~0U +#define INVALID_PRIORITY ((pq_priority_t)~0ULL) + +typedef uint64_t pq_priority_t; + +struct heap_node; + +typedef struct priority_queue { + uint32_t max_elems;/* Number of elements in heap */ + /* Number of registered elements (active + inactive) */ + uint32_t reg_elems; + uint32_t num_elems;/* Number of active elements */ + struct heap_node *heap; + struct heap_node *org_ptr; +} priority_queue; + +/* The user gets a pointer to this structure */ +typedef struct { + /* Set when pq_element registered with priority queue */ + priority_queue *pq; + uint32_t index;/* Index into heap array */ + pq_priority_t prio; +} pq_element; + +/*** Operations on pq_element ***/ + +static inline void pq_element_con(pq_element *this) +{ + this->pq = NULL; + this->index = INVALID_INDEX; + this->prio = 0U; +} + +static inline void pq_element_des(pq_element *this) +{ + (void)this; + assert(this->index == INVALID_INDEX); +} + +static inline priority_queue *get_pq(const pq_element *this) +{ + return this->pq; +} + +static inline pq_priority_t get_prio(const pq_element *this) +{ + return this->prio; +} + +static inline uint32_t get_index(const pq_element *this) +{ + return this->index; +} + +static inline bool is_active(const pq_element *this) +{ + return this->index != INVALID_INDEX; +} + +/*** Operations on priority_queue ***/ + +extern uint32_t pq_smallest_child(priority_queue *, uint32_t, pq_priority_t); +extern void pq_bubble_down(priority_queue *, pq_element *); +extern void pq_bubble_up(priority_queue *, pq_element *); + +static inline bool valid_index(priority_queue *this, uint32_t idx) +{ + return idx < this->num_elems; +} + +extern void priority_queue_con(priority_queue *, uint32_t _max_elems); +extern void priority_queue_des(priority_queue *); + +/* Register pq_element with priority queue */ +/* Return false if priority queue full */ +extern bool pq_register_element(priority_queue *, pq_element *); + +/* Activate and add pq_element to priority queue */ +/* Element must be disarmed */ +extern void pq_activate_element(priority_queue *, pq_element *, pq_priority_t); + +/* Reset (increase) priority for pq_element */ +/* Element may be active or inactive (released) */ +extern void pq_reset_element(priority_queue *, pq_element *, pq_priority_t); + +/* Deactivate and remove element from priority queue */ +/* Element may be active or inactive (released) */ +extern void pq_deactivate_element(priority_queue *, pq_element *); + +/* Unregister pq_element */ +extern void pq_unregister_element(priority_queue *, pq_element *); + +/* Return priority of first element (lowest numerical value) */ +extern pq_priority_t pq_first_priority(const priority_queue *); + +/* Deactivate and return first element if it's prio is <= threshold */ +extern pq_element *pq_release_element(priority_queue *, pq_priority_t thresh); + +#endif /* _PRIORITY_QUEUE_H */ diff --git a/test/api_test/odp_timer_ping.c b/test/api_test/odp_timer_ping.c index c1cc255..c4332e3 100644 --- a/test/api_test/odp_timer_ping.c +++ b/test/api_test/odp_timer_ping.c @@ -20,6 +20,7 @@ * Otherwise timeout may happen bcz of slow nw speed */ +#include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> @@ -43,7 +44,8 @@ #define PING_CNT 10 #define PING_THRD 2 /* Send and Rx Ping thread */ -static odp_timer_t test_timer_ping; +static odp_timer_pool_t tp; +static odp_timer_t test_timer_ping = ODP_TIMER_INVALID; static odp_timer_tmo_t test_ping_tmo; #define PKTSIZE 64 @@ -123,15 +125,7 @@ static int listen_to_pingack(void) (socklen_t *)&len); if (bytes > 0) { /* pkt rxvd therefore cancel the timeout */ - if (odp_timer_cancel_tmo(test_timer_ping, - test_ping_tmo) != 0) { - ODP_ERR("cancel_tmo failed ..exiting listner thread\n"); - /* avoid exiting from here even if tmo - * failed for current ping, - * allow subsequent ping_rx request */ - err = -1; - - } + odp_timer_cancel(test_timer_ping); /* cruel bad hack used for sender, listner ipc.. * euwww.. FIXME .. */ @@ -153,7 +147,7 @@ static int send_ping_request(struct sockaddr_in *addr) int sd, cnt = 1; struct packet pckt; - uint64_t tick; + odp_timer_tick_t tick; odp_queue_t queue; odp_buffer_t buf; @@ -179,6 +173,12 @@ static int send_ping_request(struct sockaddr_in *addr) /* get the ping queue */ queue = odp_queue_lookup("ping_timer_queue"); + test_timer_ping = odp_timer_alloc(tp, queue, NULL); + if (test_timer_ping == ODP_TIMER_INVALID) { + ODP_ERR("Failed to allocate timer.\n"); + err = -1; + goto err; + } for (i = 0; i < PING_CNT; i++) { /* prepare icmp pkt */ @@ -204,12 +204,10 @@ static int send_ping_request(struct sockaddr_in *addr) printf(" icmp_sent msg_cnt %d\n", i); /* arm the timer */ - tick = odp_timer_current_tick(test_timer_ping); + tick = odp_timer_current_tick(tp); tick += 1000; - test_ping_tmo = odp_timer_absolute_tmo(test_timer_ping, tick, - queue, - ODP_BUFFER_INVALID); + odp_timer_set_abs(test_timer_ping, tick); /* wait for timeout event */ while ((buf = odp_queue_deq(queue)) == ODP_BUFFER_INVALID) { /* flag true means ack rxvd.. a cruel hack as I @@ -225,16 +223,24 @@ static int send_ping_request(struct sockaddr_in *addr) } } - /* free tmo_buf for timeout case */ - if (buf != ODP_BUFFER_INVALID) { + switch (odp_timer_tmo_status(buf)) { + case ODP_TMO_FRESH: ODP_DBG(" timeout msg_cnt [%i] \n", i); /* so to avoid seg fault commented */ - odp_buffer_free(buf); err = -1; + break; + case ODP_TMO_STALE: + /* Ignore stale timeouts */ + break; + case ODP_TMO_ORPHAN: + ODP_ERR("Received orphaned timeout!\n"); + abort(); } } err: + if (test_timer_ping != ODP_TIMER_INVALID) + odp_timer_free(test_timer_ping); return err; } @@ -335,7 +341,7 @@ int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED) ODP_CACHE_LINE_SIZE, ODP_BUFFER_TYPE_RAW); if (pool == ODP_BUFFER_POOL_INVALID) { - ODP_ERR("Pool create failed.\n"); + ODP_ERR("Buffer pool create failed.\n"); return -1; } @@ -350,8 +356,18 @@ int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED) return -1; } - test_timer_ping = odp_timer_create("ping_timer", pool, - 1000000, 1000000, 1000000000000UL); + /* + * Create timer pool + */ + tp = odp_timer_pool_create("timer_pool", pool, + 1000000U, /* 1 millisecond */ + 10000000000U, /* 10 seconds */ + 1, false, ODP_CLOCK_DEFAULT); + if (tp == ODP_TIMER_POOL_INVALID) { + ODP_ERR("Timer pool create failed.\n"); + return -1; + } + odp_shm_print_all(); pingarg.thrdarg.testcase = ODP_TIMER_PING_TEST;
Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org> --- (This document/code contribution attached is provided under the terms of agreement LES-LTM-21309) New timer API and corresponding SW implementation for linux-generic. Read more about use cases and usage here: https://docs.google.com/a/linaro.org/document/d/1bfY_J8ecLJPsFTmYftb0NVmGnB9qkEc_NpcJ87yfaD8/edit# example/timer/odp_timer_test.c | 93 ++- platform/linux-generic/Makefile.am | 1 + platform/linux-generic/include/api/odp_timer.h | 478 ++++++++++-- .../linux-generic/include/odp_timer_internal.h | 71 +- platform/linux-generic/odp_timer.c | 823 +++++++++++++-------- platform/linux-generic/priority_queue.c | 289 ++++++++ platform/linux-generic/priority_queue.h | 107 +++ test/api_test/odp_timer_ping.c | 58 +- 8 files changed, 1455 insertions(+), 465 deletions(-) create mode 100644 platform/linux-generic/priority_queue.c create mode 100644 platform/linux-generic/priority_queue.h