Signed-off-by: Ola Liljedahl <[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

Reply via email to