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 <[email protected] <mailto:[email protected]>> wrote:

    Signed-off-by: Ola Liljedahl <[email protected]
    <mailto:[email protected]>>
    ---
    (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
[email protected]
http://lists.linaro.org/mailman/listinfo/lng-odp


_______________________________________________
lng-odp mailing list
[email protected]
http://lists.linaro.org/mailman/listinfo/lng-odp

Reply via email to