diff mbox

[PATCHv2] Timer API and and priority queue-based implementation

Message ID 1410439059-30549-1-git-send-email-ola.liljedahl@linaro.org
State New
Headers show

Commit Message

Ola Liljedahl Sept. 11, 2014, 12:37 p.m. UTC
Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org>
---
(This document/code contribution attached is provided under the terms of agreement LES-LTM-21309)
New timer API and corresponding SW implementation for linux-generic.
Read more about use cases and usage here:
https://docs.google.com/a/linaro.org/document/d/1bfY_J8ecLJPsFTmYftb0NVmGnB9qkEc_NpcJ87yfaD8
Updated after review:
Renamed and moved files (see git summary below).
Removed/rephrased some TODO texts.
Removed unused assignment of "val".
Modified "#if 1" expression.
Enhanced a comment in the odp_timer_ping test program.
Incorporated some later updates to odp_timer.c.

 example/timer/odp_timer_test.c                     |  99 +--
 platform/linux-generic/Makefile.am                 |   1 +
 platform/linux-generic/include/api/odp_timer.h     | 478 ++++++++++--
 .../include/odp_priority_queue_internal.h          | 108 +++
 .../linux-generic/include/odp_timer_internal.h     |  71 +-
 platform/linux-generic/odp_priority_queue.c        | 284 +++++++
 platform/linux-generic/odp_timer.c                 | 847 +++++++++++++--------
 test/api_test/odp_timer_ping.c                     |  60 +-
 8 files changed, 1452 insertions(+), 496 deletions(-)
 create mode 100644 platform/linux-generic/include/odp_priority_queue_internal.h
 create mode 100644 platform/linux-generic/odp_priority_queue.c

Comments

Savolainen, Petri (NSN - FI/Espoo) Sept. 12, 2014, 9:30 a.m. UTC | #1
Commenting mainly on the API...

> -----Original Message-----
> From: lng-odp-bounces@lists.linaro.org [mailto:lng-odp-
> bounces@lists.linaro.org] On Behalf Of ext Ola Liljedahl
> Sent: Thursday, September 11, 2014 3:38 PM
> To: lng-odp@lists.linaro.org
> Subject: [lng-odp] [PATCHv2] Timer API and and priority queue-based
> implementation
> 
> Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org>
> ---
> (This document/code contribution attached is provided under the terms of
> agreement LES-LTM-21309)
> New timer API and corresponding SW implementation for linux-generic.
> Read more about use cases and usage here:
> https://docs.google.com/a/linaro.org/document/d/1bfY_J8ecLJPsFTmYftb0NVmGn
> B9qkEc_NpcJ87yfaD8
> Updated after review:
> Renamed and moved files (see git summary below).
> Removed/rephrased some TODO texts.
> Removed unused assignment of "val".
> Modified "#if 1" expression.
> Enhanced a comment in the odp_timer_ping test program.
> Incorporated some later updates to odp_timer.c.
> 
>  example/timer/odp_timer_test.c                     |  99 +--
>  platform/linux-generic/Makefile.am                 |   1 +
>  platform/linux-generic/include/api/odp_timer.h     | 478 ++++++++++--
>  .../include/odp_priority_queue_internal.h          | 108 +++
>  .../linux-generic/include/odp_timer_internal.h     |  71 +-
>  platform/linux-generic/odp_priority_queue.c        | 284 +++++++
>  platform/linux-generic/odp_timer.c                 | 847 +++++++++++++---
> -----
>  test/api_test/odp_timer_ping.c                     |  60 +-
>  8 files changed, 1452 insertions(+), 496 deletions(-)
>  create mode 100644 platform/linux-
> generic/include/odp_priority_queue_internal.h
>  create mode 100644 platform/linux-generic/odp_priority_queue.c
> 
> diff --git a/example/timer/odp_timer_test.c
> b/example/timer/odp_timer_test.c
> index 1061190..87112fb 100644
> --- a/example/timer/odp_timer_test.c
> +++ b/example/timer/odp_timer_test.c
> @@ -31,7 +31,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*/
> @@ -41,18 +40,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);
> @@ -60,37 +57,51 @@ static void test_abs_timeouts(int thr, test_args_t
> *args)
>  	queue = odp_queue_lookup("timer_queue");
> 
>  	period_ns = args->period_us*ODP_TIME_USEC;
> -	period    = odp_timer_ns_to_tick(test_timer, period_ns);
> +	period    = odp_timer_ns_to_tick(tp, period_ns);
> 
>  	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);

This is not portable. Scheduler returns odp_buffer_t. odp_timer_tmo_t type is not always odp_buffer_t.

> +		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);

odp_timer_tmo_t != odp_buffer_t

> +			continue;
> +		}

Could we optimize for the common case. Minimize C lines/cycles for freshness check and ack.

if (odp_unlikely(odp_timer_tmo_check(tmo))) {
	/* handle errors */
	status = odp_timer_tmo_status(tmo);
	...
}


> 
> -		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)
> @@ -98,10 +109,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();
>  }
> @@ -155,7 +169,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");
> @@ -179,7 +192,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'},
> @@ -190,14 +202,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        = 10000000;
>  	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 */
> @@ -209,9 +220,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;
> @@ -295,7 +303,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);
> @@ -319,10 +326,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*ODP_TIME_USEC,
> +				   args.max_us*ODP_TIME_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
>  	 */
> @@ -338,19 +358,6 @@ int main(int argc, char *argv[])
>  		return -1;
>  	}
> 
> -	test_timer = odp_timer_create("test_timer", pool,
> -				      args.resolution_us*ODP_TIME_USEC,
> -				      args.min_us*ODP_TIME_USEC,
> -				      args.max_us*ODP_TIME_USEC);
> -
> -	if (test_timer == ODP_TIMER_INVALID) {
> -		ODP_ERR("Timer create failed.\n");
> -		return -1;
> -	}
> -
> -
> -	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 25c82ea..26964d8 100644
> --- a/platform/linux-generic/Makefile.am
> +++ b/platform/linux-generic/Makefile.am
> @@ -62,6 +62,7 @@ __LIB__libodp_la_SOURCES = \
>  			   odp_packet_flags.c \
>  			   odp_packet_io.c \
>  			   odp_packet_socket.c \
> +			   odp_priority_queue.c \
>  			   odp_queue.c \
>  			   odp_ring.c \
>  			   odp_rwlock.c \
> diff --git a/platform/linux-generic/include/api/odp_timer.h
> b/platform/linux-generic/include/api/odp_timer.h
> index 01db839..b05283a 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;


struct odp_timer_pool_t (or struct odp_timer_pool_s or 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 */


Maybe this comment can be removed. We may be able standardize some. E.g. _CPU_CLOCK (chip internal) and _TIMER_INPUT (chip external, connected to timer input pin)

> +	ODP_CLOCK_NONE = 1

When none is used?

> +} odp_timer_pool_clock_source_t;

Pretty long type name, could we use odp_timer_clock_source_t instead?

> 
> +/**
> +* ODP timer handle (platform dependent)
> +*/
> +struct odp_timer;
> +typedef struct odp_timer *odp_timer_t;


struct 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 */


Do nothing? Did the status function free the buffer? The buffer has to be freed or reused, otherwise it's a memory leak. 

> +	ODP_TMO_ORPHAN,/* Timer deleted, free timeout */
> +} odp_timer_tmo_status_t;
> +
> +/**
> +* ODP tick value
> +*/
> +typedef uint64_t odp_timer_tick_t;


Why tick could not be fixed to uint64_t? It's simpler for the user to perform (portable) tick calculations if the type is fixed. Now it could be signed vs. unsigned, 16/32/64 bits...


> 
> 
>  /**
> - * 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.


I think the speculation above is not needed. There's also timer pool destroy 
function, and user may destroy/create pools also after init.

>   *
>   * @param name       Name
> - * @param pool       Buffer pool for allocating timeout notifications
> + * @param buf_pool   Buffer pool for allocating timers


odp_timer_t or odp_timer_tmo_t or both? I'm thinking timeouts.

>   * @param resolution Timeout resolution in nanoseconds
> - * @param min_tmo    Minimum timeout duration in nanoseconds


Min_tmo could be useful still. Some implementations may appreciate to have 
more information about the intended usage, e.g. what kind of (cascading) 
timer wheels to create, etc. Zero could be the default (== resolution).

> - * @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.


When user have called odp_timer_free() for all timers, there may be still 
timeouts in timer HW and in queues. Is it OK to delete the timer pool 
if all remaining timeouts have not been received yet? E.g. user could not 
delete the "timer buffer pool" after this call returns, since some timeouts 
may be still on the way and need to be freed. How user knows when the buffer 
pool can be reused or 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);
> +uintptr_t odp_timer_pool_query_conf(odp_timer_pool_t tpid,
> +				    odp_timer_pool_conf_t item);


It would be simpler to just query all information at once.

typedef struct odp_timer_pool_info_s {
	const char *name;
	uint64_t resolution;
	uint64_t max_tmo;
	uint32_t num_timers;
	bool     shared
} odp_timer_pool_info_t;

int odp_timer_pool_info(odp_timer_pool_t tpid, odp_timer_pool_info_t *info);


> 
>  /**
> - * Current timer tick
> + * Allocate a timer
>   *
> - * @param timer Timer
> + * Create a timer (allocating all necessary resources e.g. timeout event)
> from
> + * the timer pool.
>   *
> - * @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.


User should see from a return code if free was successful or not. Now user 
does not know if it has to wait for timeout or not.

> + * 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.


In the two cases above, application must be in control what happens next. ODP does not know what 
application was trying to do with the timeout (tmo priority or severity of the failure) or 
what is the right corrective action (if any is needed). E.g. in "too near" case it may be better 
for application to skip that tmo and just continue from the next cycle, etc. Speculation on 
"too far" is also bad. For example, if user was supposed to ask a 1 sec, but miss 
calculated and asked a 500 year timeout, now ODP implementation would silently round 
that to the max e.g. 3 days. How do you debug it after 3 days? With a "too far" return code 
application could log/report error and take the corrective action where the bug 
was noticed the first time. 


>   *
> - * @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);


odp_timer_set_rel_w_buf() missing?

> 
>  /**
> - * 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.


How user knows if it has to wait for a pending timeout still, or can it e.g. 
remove the destination queue? Function should return a success code.

> + * 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).


It's not very well documented how a timer is reset. E.g. another odp_timer_set_abs_w_buf() call on an already active timer?

Can user mix timer set calls when resetting e.g. odp_timer_set_abs_w_buf(timer, tick, buf) -> odp_timer_set_abs(timer, tick)?

What happens if timer is reset too late? Does user receive the original, the new or a stale tmo?


> + * 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);


Should we drop _get_ to be consistent with other ODP APIs.


> +
> +/**
> + * 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);


Should we drop _get_ to be consistent with other ODP APIs.

> +
> +/**
> + * 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);


Should we drop _get_ to be consistent with other ODP APIs.

> +
> +/* Helper functions */
> +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, odp_timer_tick_t
> tick);

Is this function part of the API?


-Petri
Ola Liljedahl Sept. 12, 2014, 12:40 p.m. UTC | #2
On 12 September 2014 11:30, Savolainen, Petri (NSN - FI/Espoo) <
petri.savolainen@nsn.com> wrote:

> > +             /* Get the next ready buffer/timeout */
> > +             tmo = odp_schedule_one(&queue, ODP_SCHED_WAIT);
>
> This is not portable. Scheduler returns odp_buffer_t. odp_timer_tmo_t type
> is not always odp_buffer_t.
>

The implementation has to define odp_timer_tmo_t as a subtype of
odp_buffer_t. This is equivalent to how
odp_packet_t is defined and handled by linux-generic. Why is this portable
for packets but not for timeouts?

The example could be more realistic, using an odp_buffer_t variable for the
return of odp_schedule_one() and
then check if the buffer is actually a timeout and not some other type of
buffer (e.g. packet).



>
> > +             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);
>
> odp_timer_tmo_t != odp_buffer_t
>

odp_timer_tmo_t is a subtype of odp_buffer_t by definition.
odp_buffer_alloc and odp_buffer_free are used,
just like for packets.

From odp_buffer.h:
#define ODP_BUFFER_TYPE_INVALID (-1) /**< Buffer type invalid */
#define ODP_BUFFER_TYPE_ANY       0  /**< Buffer that can hold any other
                                          buffer type */
#define ODP_BUFFER_TYPE_RAW       1  /**< Raw buffer, no additional
metadata */
#define ODP_BUFFER_TYPE_PACKET    2  /**< Packet buffer */
#define ODP_BUFFER_TYPE_TIMEOUT   3  /**< Timeout buffer */


> > +                     continue;
> > +             }
>
> Could we optimize for the common case. Minimize C lines/cycles for
> freshness check and ack.
>
> if (odp_unlikely(odp_timer_tmo_check(tmo))) {
>         /* handle errors */
>         status = odp_timer_tmo_status(tmo);
>         ...
> }
>
> OK. This was just an example.


 > +/**

> > +* ODP timer pool handle (platform dependent)
> > +*/
> > +struct odp_timer_pool;
>
>
> struct odp_timer_pool_t (or struct odp_timer_pool_s or struct
> odp_timer_pool_)
>

OK if this is the coding standard.
Struct names are in their own name space and always preceded by the
"struct" keyword so I cannot
really see how there could be confusion to either the compiler or the user.



> -/** 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 */
>
>
> Maybe this comment can be removed. We may be able standardize some. E.g.
> _CPU_CLOCK (chip internal) and _TIMER_INPUT (chip external, connected to
> timer input pin)
>

I am open to changes here but think the above is good for linux-generic and
I can't see how linux-generic
should handle _CPU_CLOCK and _TIMER_INPUT in any other way than just
ignoring them and do it the
current way anyway. Perhaps this is what we should do?
What do the other platform developers think about Petri's suggestion? Can
we standardize on that (with some
liberal interpretation if needed by different platforms)?


>
> > +     ODP_CLOCK_NONE = 1
>
> When none is used?
>
Sort of a left over from early testing without any HW or OS timer providing
a tick. Instead the
application was providing the tick (a la DPDK) via the
odp_timer_pool_expire() function.
If we don't think this situation makes sense, I can remove the support.



> > +} odp_timer_pool_clock_source_t;
>
> Pretty long type name, could we use odp_timer_clock_source_t instead?
>

OK. We can even abbreviate some of the words, e.g. odp_timer_clk_src_t. OK?




>
> >
> > +/**
> > +* ODP timer handle (platform dependent)
> > +*/
> > +struct odp_timer;
> > +typedef struct odp_timer *odp_timer_t;
>
>
> struct odp_timer_t
>

OK.


> + * 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 */
>
>
> Do nothing? Did the status function free the buffer? The buffer has to be
> freed or reused, otherwise it's a memory leak.
>
If a timer is reset or cancelled, the implementation is reusing the timeout
buffer automatically.



> > +     ODP_TMO_ORPHAN,/* Timer deleted, free timeout */
> > +} odp_timer_tmo_status_t;
> > +
> > +/**
> > +* ODP tick value
> > +*/
> > +typedef uint64_t odp_timer_tick_t;
>
>
> Why tick could not be fixed to uint64_t? It's simpler for the user to
> perform (portable) tick calculations if the type is fixed. Now it could be
> signed vs. unsigned, 16/32/64 bits...
>

I expect timer_tick to be fixed to uint64_t (although one can imagine
architectures where uint32_t would make sense). I just want an explicit
type that denotes timer_ticks. When everything is declared as uint64_t, I
become confused about what those variables and function parameters and
return values actually mean. This is for clarity and much more important
that some "_s" suffix on a struct type.

Perhaps we need a way to limit what ODP platforms redefine types etc? The
ODP validation suite could verify that odp_timer_tick_t is a 64-bit
unsigned scalar type.


>
> >
> >
> >  /**
> > - * 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.
>
>
> I think the speculation above is not needed. There's also timer pool
> destroy
> function, and user may destroy/create pools also after init.
>
OK.



>
> >   *
> >   * @param name       Name
> > - * @param pool       Buffer pool for allocating timeout notifications
> > + * @param buf_pool   Buffer pool for allocating timers
>
>
> odp_timer_t or odp_timer_tmo_t or both? I'm thinking timeouts.
>
You are correct, timeouts it is.




>
> >   * @param resolution Timeout resolution in nanoseconds
> > - * @param min_tmo    Minimum timeout duration in nanoseconds
>
>
> Min_tmo could be useful still. Some implementations may appreciate to have
> more information about the intended usage, e.g. what kind of (cascading)
> timer wheels to create, etc. Zero could be the default (== resolution).
>

If the application specifies a minimum timeout duration, what should happen
if it attempts to request timeouts smaller than that threshold? Should the
implementation silently increase the period to the min duration? Or throw an
error? If we introduce this parameter, we need to define how it affects
user-visible behavior.

What is interesting is whether we should give the implementation some hints
about allowed jitter for a timer. Even with a user-defined resolution, the
application
might be able to tolerate some additional latency which can help the
imlementation
to coalesce timers etc.



>
> > + * 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.
>
>
> When user have called odp_timer_free() for all timers, there may be still
> timeouts in timer HW and in queues. Is it OK to delete the timer pool
> if all remaining timeouts have not been received yet? E.g. user could not
> delete the "timer buffer pool" after this call returns, since some timeouts
> may be still on the way and need to be freed. How user knows when the
> buffer
> pool can be reused or freed?
>

This is a tricky question. And related to buffer pool semantics I would
think.
I assume that when freeing a timer, if the timeout is still in the HW, it
can be
reclaimed as well (but it doesn't have to be done). Timeouts can
definitively
still be on queues and will eventually be received by some agent.

What happens if you attempt to destroy a buffer pool which has buffers
still in
use? (Well currently there is no such call implemented but it is in the
buffer
management design doc). This is what the linux-generic implementation will
boil down to. If the buffer pool API would provide some API to help with
this
situation, it could also be used by the timer API.

Maybe we need to add a sentence that all timeouts also must have been
received and freed. But it is unknown how the timer implementation currently
could check or enforce this condition.

The timer API does handle the situation where a timeout is received after
the
corresponding timer has been freed. Such timeouts will have status orphan
and the application should just free them.

>
>
> > +uintptr_t odp_timer_pool_query_conf(odp_timer_pool_t tpid,
> > +                                 odp_timer_pool_conf_t item);
>
>
> It would be simpler to just query all information at once.
>
> typedef struct odp_timer_pool_info_s {
>         const char *name;
>         uint64_t resolution;
>         uint64_t max_tmo;
>         uint32_t num_timers;
>         bool     shared
> } odp_timer_pool_info_t;
>
> int odp_timer_pool_info(odp_timer_pool_t tpid, odp_timer_pool_info_t
> *info);
>
> Yes but I expect that there could be future additions and thus I wanted a
more
extensible API. If you think the all info in a struct still is better, I
will change to that.



> > +/**
> > + * 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.
>
>
> User should see from a return code if free was successful or not. Now user
> does not know if it has to wait for timeout or not.
>
OK, I think I can add such a return code. It is not an error, the free timer
call won't fail.



> >
> >  /**
> > - * 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.
>
>
> In the two cases above, application must be in control what happens next.
> ODP does not know what
> application was trying to do with the timeout (tmo priority or severity of
> the failure) or
> what is the right corrective action (if any is needed). E.g. in "too near"
> case it may be better
> for application to skip that tmo and just continue from the next cycle,
> etc. Speculation on
> "too far" is also bad. For example, if user was supposed to ask a 1 sec,
> but miss
> calculated and asked a 500 year timeout, now ODP implementation would
> silently round
> that to the max e.g. 3 days. How do you debug it after 3 days? With a "too
> far" return code
> application could log/report error and take the corrective action where
> the bug
> was noticed the first time.
>

Possibly the application would like to handle too near/too far situations
more explicitly, I just
want to avoid the application to have to handle "error" situations when a
flow/connection is
set up. No one else that read and commented on the Timer Use Cases and API
design doc
thought we needed to report such situations here. I think we need a
discussion on this subject
with some more stake holders.



> > +void odp_timer_set_rel(odp_timer_t tim, odp_timer_tick_t rel_tck);
>
>
> odp_timer_set_rel_w_buf() missing?
>

Yes. Do you really want it?


> >
> >  /**
> > - * 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.
>
>
> How user knows if it has to wait for a pending timeout still, or can it
> e.g.
> remove the destination queue? Function should return a success code.
>

The destination queue should be removed after the timer has been freed, not
after a timer/timeout has been cancelled. We agree that odp_timer_free()
should
return a value that indicates whether timeouts are still pending.


> > + * 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).
>
>
> It's not very well documented how a timer is reset. E.g. another
> odp_timer_set_abs_w_buf() call on an already active timer?
>

Yes, just (re-) set or cancel the timer.
I will see how I can improve the documentation to make this clearer.



>
> Can user mix timer set calls when resetting e.g.
> odp_timer_set_abs_w_buf(timer, tick, buf) -> odp_timer_set_abs(timer, tick)?
>
> What happens if timer is reset too late? Does user receive the original,
> the new or a stale tmo?
>
Does this have to be specified?
The linux-generic implementation of course will behave in some consistent
way but I don't think this has to be
normative as I expect timer HW implementations may differ here.

Do applications have any specific interest in one behavior or the other?


I don't think it is a great idea to mix used-defined and system-defined
timeout buffers for the same timer.
The linux-generic implementation handles it but possibly this behavior
could be implementation specific.
Maybe we should add a restriction that once the application has switched to
using user-defined timeout buffers,
it should not change behavior. Maybe this is another parameter to
odp_timer_alloc()?



>
> > + * 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);
>
>
> Should we drop _get_ to be consistent with other ODP APIs.
>


> > +odp_timer_tick_t odp_timer_get_expiry(odp_timer_tmo_t tmo);
>
>
> Should we drop _get_ to be consistent with other ODP APIs.
>


> > +void *odp_timer_get_userptr(odp_timer_tmo_t tmo);
>
>
> Should we drop _get_ to be consistent with other ODP APIs.
>

Three OK's.



>
> > +
> > +/* Helper functions */
> > +unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, odp_timer_tick_t
> > tick);
>
> Is this function part of the API?
>
It depends whether we want to support ODP_CLOCK_NONE.
We could remove it, maybe this is best in order to minimize time spent on
less important issues.


In all a good review, caught some mistakes and suggested some improvements.



>
> -Petri
>
>
>
diff mbox

Patch

diff --git a/example/timer/odp_timer_test.c b/example/timer/odp_timer_test.c
index 1061190..87112fb 100644
--- a/example/timer/odp_timer_test.c
+++ b/example/timer/odp_timer_test.c
@@ -31,7 +31,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*/
@@ -41,18 +40,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);
@@ -60,37 +57,51 @@  static void test_abs_timeouts(int thr, test_args_t *args)
 	queue = odp_queue_lookup("timer_queue");
 
 	period_ns = args->period_us*ODP_TIME_USEC;
-	period    = odp_timer_ns_to_tick(test_timer, period_ns);
+	period    = odp_timer_ns_to_tick(tp, period_ns);
 
 	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)
@@ -98,10 +109,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();
 }
@@ -155,7 +169,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");
@@ -179,7 +192,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'},
@@ -190,14 +202,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        = 10000000;
 	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 */
@@ -209,9 +220,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;
@@ -295,7 +303,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);
@@ -319,10 +326,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*ODP_TIME_USEC,
+				   args.max_us*ODP_TIME_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
 	 */
@@ -338,19 +358,6 @@  int main(int argc, char *argv[])
 		return -1;
 	}
 
-	test_timer = odp_timer_create("test_timer", pool,
-				      args.resolution_us*ODP_TIME_USEC,
-				      args.min_us*ODP_TIME_USEC,
-				      args.max_us*ODP_TIME_USEC);
-
-	if (test_timer == ODP_TIMER_INVALID) {
-		ODP_ERR("Timer create failed.\n");
-		return -1;
-	}
-
-
-	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 25c82ea..26964d8 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -62,6 +62,7 @@  __LIB__libodp_la_SOURCES = \
 			   odp_packet_flags.c \
 			   odp_packet_io.c \
 			   odp_packet_socket.c \
+			   odp_priority_queue.c \
 			   odp_queue.c \
 			   odp_ring.c \
 			   odp_rwlock.c \
diff --git a/platform/linux-generic/include/api/odp_timer.h b/platform/linux-generic/include/api/odp_timer.h
index 01db839..b05283a 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);
+uintptr_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_priority_queue_internal.h b/platform/linux-generic/include/odp_priority_queue_internal.h
new file mode 100644
index 0000000..7d7f3a2
--- /dev/null
+++ b/platform/linux-generic/include/odp_priority_queue_internal.h
@@ -0,0 +1,108 @@ 
+#ifndef _PRIORITY_QUEUE_H
+#define _PRIORITY_QUEUE_H
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <odp_align.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 ODP_ALIGNED(sizeof(uint64_t));
+
+/* 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/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_priority_queue.c b/platform/linux-generic/odp_priority_queue.c
new file mode 100644
index 0000000..1d0a5d3
--- /dev/null
+++ b/platform/linux-generic/odp_priority_queue.c
@@ -0,0 +1,284 @@ 
+#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 "odp_priority_queue_internal.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);
+	}
+	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 */
+/* Very hot function, can we decrease the number of cache misses? */
+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 NUM_CHILDREN == 4
+	/* 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)
+			(void)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;
+}
+
+/* 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/odp_timer.c b/platform/linux-generic/odp_timer.c
index 313c713..cad96f2 100644
--- a/platform/linux-generic/odp_timer.c
+++ b/platform/linux-generic/odp_timer.c
@@ -1,431 +1,636 @@ 
-/* 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_time.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 MAX_RES       ODP_TIME_SEC
-#define MIN_RES       (100*ODP_TIME_USEC)
-
-
-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_time.h"
+#include "odp_timer.h"
+#include "odp_timer_internal.h"
+#include "odp_priority_queue_internal.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;
-	int tick_idx;
-	timeout_t *cancel_tmo;
-	odp_timeout_hdr_t *tmo_hdr;
-	tick_t *tick;
-
-	/* get id */
-	id = (int)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;
 	uint64_t res, sec, nsec;
 
-	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();
 	}
 
-	res  = timer->resolution_ns;
+	res  = tp->resolution_ns;
 	sec  = res / ODP_TIME_SEC;
-	nsec = res - sec*ODP_TIME_SEC;
+	nsec = res - sec * ODP_TIME_SEC;
 
 	ispec.it_interval.tv_sec  = (time_t)sec;
 	ispec.it_interval.tv_nsec = (long)nsec;
 	ispec.it_value.tv_sec     = (time_t)sec;
 	ispec.it_value.tv_nsec    = (long)nsec;
 
-	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();
 	}
-
-	odp_spinlock_unlock(&odp_timer.lock);
-
-	return 0;
+	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_timer_t odp_timer_create(const char *name, odp_buffer_pool_t pool,
-			     uint64_t resolution_ns, uint64_t min_ns,
-			     uint64_t max_ns)
+void odp_timer_pool_start(void)
 {
-	uint32_t id;
-	timer_ring_t *timer;
-	odp_timer_t timer_hdl;
-	int i;
-	uint64_t max_ticks;
-	(void) name;
-
-	if (resolution_ns < MIN_RES)
-		resolution_ns = MIN_RES;
-
-	if (resolution_ns > MAX_RES)
-		resolution_ns = MAX_RES;
+	/* Nothing to do here */
+}
 
-	max_ticks = max_ns / resolution_ns;
+void odp_timer_pool_destroy(odp_timer_pool_t tpid)
+{
+	odp_timer_pool_del(tpid);
+}
 
-	if (max_ticks > MAX_TICKS) {
-		ODP_DBG("Maximum timeout too long: %"PRIu64" ticks\n",
-			max_ticks);
-		return ODP_TIMER_INVALID;
-	}
+uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, odp_timer_tick_t ticks)
+{
+	return ticks * tpid->resolution_ns;
+}
 
-	if (min_ns < resolution_ns) {
-		ODP_DBG("Min timeout %"PRIu64" ns < resolution %"PRIu64" ns\n",
-			min_ns, resolution_ns);
-		return ODP_TIMER_INVALID;
-	}
+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);
+}
 
-	odp_spinlock_lock(&odp_timer.lock);
+odp_timer_tick_t odp_timer_current_tick(odp_timer_pool_t tpid)
+{
+	return tpid->tick;
+}
 
-	if (odp_timer.num_timers >= NUM_TIMERS) {
-		odp_spinlock_unlock(&odp_timer.lock);
-		ODP_DBG("All timers allocated\n");
-		return ODP_TIMER_INVALID;
+uintptr_t odp_timer_pool_query_conf(odp_timer_pool_t tpid,
+				    odp_timer_pool_conf_t item)
+{
+	switch (item) {
+	case ODP_TIMER_NAME:
+		return (uintptr_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;
 	}
+}
 
-	for (id = 0; id < NUM_TIMERS; id++) {
-		if (odp_timer.timer[id].allocated == 0)
-			break;
+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 = &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_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);
 	}
-
-	timer->active = 1;
-	odp_sync_stores();
-
-	timer_start(timer);
-
-	return timer_hdl;
+	/* 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 = (int)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);
+}
 
-	if ((tmo_tick - cur_tick) > MAX_TICKS) {
-		ODP_DBG("timeout too far: cur %"PRIu64" tmo %"PRIu64"\n",
-			cur_tick, tmo_tick);
-		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 = tmo_tick % MAX_TICKS;
-
-	tmo_buf = odp_buffer_alloc(timer->pool);
-	if (tmo_buf == ODP_BUFFER_INVALID) {
-		ODP_DBG("tmo buffer 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/test/api_test/odp_timer_ping.c b/test/api_test/odp_timer_ping.c
index 6ba30d3..1624924 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>
@@ -41,9 +42,10 @@ 
 #define MSG_POOL_SIZE         (4*1024*1024)
 #define BUF_SIZE		8
 #define PING_CNT	10
-#define PING_THRD	2	/* Send and Rx Ping thread */
+#define PING_THRD	2	/* send_ping and rx_ping threads */
 
-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;