diff mbox series

[API-NEXT,PATCHv4,2/4] linux-generic: timer: implement odp_timer_capability()

Message ID 1486521424-11172-3-git-send-email-kevin.wang@linaro.org
State New
Headers show
Series a new API to support timer capability | expand

Commit Message

Kevin Wang Feb. 8, 2017, 2:37 a.m. UTC
Implement a new internal function timer_res_init() to detect the max
timer resolution without overrun at the ODP init stage. It will check
timer resolution from 1ms to 100us, 10us...1ns until the timer is
overrun.

Signed-off-by: Kevin Wang <kevin.wang@linaro.org>

---
 platform/linux-generic/odp_timer.c | 95 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

-- 
1.9.1

Comments

Maxim Uvarov Feb. 16, 2017, 7 p.m. UTC | #1
why not clock_getres() ?

Maxim.

On 02/08/17 05:37, Kevin Wang wrote:
> Implement a new internal function timer_res_init() to detect the max

> timer resolution without overrun at the ODP init stage. It will check

> timer resolution from 1ms to 100us, 10us...1ns until the timer is

> overrun.

> 

> Signed-off-by: Kevin Wang <kevin.wang@linaro.org>

> ---

>  platform/linux-generic/odp_timer.c | 95 ++++++++++++++++++++++++++++++++++++++

>  1 file changed, 95 insertions(+)

> 

> diff --git a/platform/linux-generic/odp_timer.c b/platform/linux-generic/odp_timer.c

> index 53fec08..9b884ea 100644

> --- a/platform/linux-generic/odp_timer.c

> +++ b/platform/linux-generic/odp_timer.c

> @@ -70,6 +70,9 @@ static _odp_atomic_flag_t locks[NUM_LOCKS]; /* Multiple locks per cache line! */

>  #define IDX2LOCK(idx) (&locks[(idx) % NUM_LOCKS])

>  #endif

>  

> +/* Timer resolution in nanoseconds */

> +static uint64_t timer_res;

> +

>  /******************************************************************************

>   * Translation between timeout buffer and timeout header

>   *****************************************************************************/

> @@ -188,6 +191,7 @@ typedef struct odp_timer_pool_s {

>  

>  #define MAX_TIMER_POOLS 255 /* Leave one for ODP_TIMER_INVALID */

>  #define INDEX_BITS 24

> +#define TIMER_RES_TEST_LOOP_COUNT 10

>  static odp_atomic_u32_t num_timer_pools;

>  static odp_timer_pool *timer_pool[MAX_TIMER_POOLS];

>  

> @@ -738,6 +742,81 @@ static void *timer_thread(void *arg)

>  	return NULL;

>  }

>  

> +/* Get the max timer resolution without overrun and fill in timer_res variable.

> + *

> + * Set timer's interval with candidate resolutions to get the max resolution

> + * that the timer would not be overrun.

> + * The candidate resolution value is from 1ms to 100us, 10us...1ns etc.

> + */

> +static int timer_res_init(void)

> +{

> +	struct sigevent sigev;

> +	timer_t timerid;

> +	uint64_t res, sec, nsec;

> +	struct itimerspec ispec;

> +	sigset_t sigset;

> +	siginfo_t si;

> +	int loop_cnt;

> +	struct timespec tmo;

> +

> +	sigev.sigev_notify = SIGEV_THREAD_ID;

> +	sigev._sigev_un._tid = (pid_t)syscall(SYS_gettid);

> +	sigev.sigev_signo = SIGUSR1;

> +

> +	/* Create timer */

> +	if (timer_create(CLOCK_MONOTONIC, &sigev, &timerid))

> +		ODP_ABORT("timer_create() returned error %s\n",

> +			  strerror(errno));

> +

> +	/* Timer resolution start from 1ms */

> +	res = ODP_TIME_MSEC_IN_NS;

> +	/* Set initial value of timer_res */

> +	timer_res = res;

> +	sigemptyset(&sigset);

> +	/* Add SIGUSR1 to sigset */

> +	sigaddset(&sigset, SIGUSR1);

> +	sigprocmask(SIG_BLOCK, &sigset, NULL);

> +

> +	while (res > 0) {

> +		/* Loop for 10 times to test the result */

> +		loop_cnt = TIMER_RES_TEST_LOOP_COUNT;

> +		sec  = res / ODP_TIME_SEC_IN_NS;

> +		nsec = res - sec * ODP_TIME_SEC_IN_NS;

> +

> +		memset(&ispec, 0, sizeof(ispec));

> +		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(timerid, 0, &ispec, NULL))

> +			ODP_ABORT("timer_settime() returned error %s\n",

> +				  strerror(errno));

> +		/* Set signal wait timeout to 10*res */

> +		tmo.tv_sec = 0;

> +		tmo.tv_nsec = res * 10;

> +		while (loop_cnt--) {

> +			if (sigtimedwait(&sigset, &si, &tmo) > 0) {

> +				if (timer_getoverrun(timerid))

> +					/* overrun at this resolution */

> +					/* goto the end */

> +					goto timer_res_init_done;

> +			}

> +		}

> +		/* Set timer_res */

> +		timer_res = res;

> +		/* Test the next timer resolution candidate */

> +		res /= 10;

> +	}

> +timer_res_init_done:

> +	if (timer_delete(timerid) != 0)

> +		ODP_ABORT("timer_delete() returned error %s\n",

> +			  strerror(errno));

> +	sigemptyset(&sigset);

> +	sigprocmask(SIG_BLOCK, &sigset, NULL);

> +	return 0;

> +}

> +

>  static void itimer_init(odp_timer_pool *tp)

>  {

>  	struct sigevent   sigev;

> @@ -795,6 +874,20 @@ static void itimer_fini(odp_timer_pool *tp)

>   * Some parameter checks and error messages

>   * No modificatios of internal state

>   *****************************************************************************/

> +int odp_timer_capability(odp_timer_clk_src_t clk_src,

> +			 odp_timer_capability_t *capa)

> +{

> +	int ret = 0;

> +

> +	if (clk_src == ODP_CLOCK_CPU) {

> +		capa->res_ns = timer_res;

> +	} else {

> +		ODP_ERR("ODP timer system doesn't support external clock source currently\n");

> +		ret = -1;

> +	}

> +	return ret;

> +}

> +

>  odp_timer_pool_t

>  odp_timer_pool_create(const char *name,

>  		      const odp_timer_pool_param_t *param)

> @@ -1003,6 +1096,8 @@ int odp_timer_init_global(void)

>  #endif

>  	odp_atomic_init_u32(&num_timer_pools, 0);

>  

> +	timer_res_init();

> +

>  	block_sigalarm();

>  

>  	return 0;

>
Kevin Wang Feb. 17, 2017, 8:43 a.m. UTC | #2
clock_getres() is to find the hardware resolution of specific clock source.
It shows 1ns in most of modern system.
What this API trys to do is to find out the resolution without overrun of
this timer. Applications are able to deal with each timer event with that
timer resolution.

2017-02-17 3:00 GMT+08:00 Maxim Uvarov <maxim.uvarov@linaro.org>:

> why not clock_getres() ?

>

> Maxim.

>

> On 02/08/17 05:37, Kevin Wang wrote:

> > Implement a new internal function timer_res_init() to detect the max

> > timer resolution without overrun at the ODP init stage. It will check

> > timer resolution from 1ms to 100us, 10us...1ns until the timer is

> > overrun.

> >

> > Signed-off-by: Kevin Wang <kevin.wang@linaro.org>

> > ---

> >  platform/linux-generic/odp_timer.c | 95 ++++++++++++++++++++++++++++++

> ++++++++

> >  1 file changed, 95 insertions(+)

> >

> > diff --git a/platform/linux-generic/odp_timer.c

> b/platform/linux-generic/odp_timer.c

> > index 53fec08..9b884ea 100644

> > --- a/platform/linux-generic/odp_timer.c

> > +++ b/platform/linux-generic/odp_timer.c

> > @@ -70,6 +70,9 @@ static _odp_atomic_flag_t locks[NUM_LOCKS]; /*

> Multiple locks per cache line! */

> >  #define IDX2LOCK(idx) (&locks[(idx) % NUM_LOCKS])

> >  #endif

> >

> > +/* Timer resolution in nanoseconds */

> > +static uint64_t timer_res;

> > +

> >  /***********************************************************

> *******************

> >   * Translation between timeout buffer and timeout header

> >   ************************************************************

> *****************/

> > @@ -188,6 +191,7 @@ typedef struct odp_timer_pool_s {

> >

> >  #define MAX_TIMER_POOLS 255 /* Leave one for ODP_TIMER_INVALID */

> >  #define INDEX_BITS 24

> > +#define TIMER_RES_TEST_LOOP_COUNT 10

> >  static odp_atomic_u32_t num_timer_pools;

> >  static odp_timer_pool *timer_pool[MAX_TIMER_POOLS];

> >

> > @@ -738,6 +742,81 @@ static void *timer_thread(void *arg)

> >       return NULL;

> >  }

> >

> > +/* Get the max timer resolution without overrun and fill in timer_res

> variable.

> > + *

> > + * Set timer's interval with candidate resolutions to get the max

> resolution

> > + * that the timer would not be overrun.

> > + * The candidate resolution value is from 1ms to 100us, 10us...1ns etc.

> > + */

> > +static int timer_res_init(void)

> > +{

> > +     struct sigevent sigev;

> > +     timer_t timerid;

> > +     uint64_t res, sec, nsec;

> > +     struct itimerspec ispec;

> > +     sigset_t sigset;

> > +     siginfo_t si;

> > +     int loop_cnt;

> > +     struct timespec tmo;

> > +

> > +     sigev.sigev_notify = SIGEV_THREAD_ID;

> > +     sigev._sigev_un._tid = (pid_t)syscall(SYS_gettid);

> > +     sigev.sigev_signo = SIGUSR1;

> > +

> > +     /* Create timer */

> > +     if (timer_create(CLOCK_MONOTONIC, &sigev, &timerid))

> > +             ODP_ABORT("timer_create() returned error %s\n",

> > +                       strerror(errno));

> > +

> > +     /* Timer resolution start from 1ms */

> > +     res = ODP_TIME_MSEC_IN_NS;

> > +     /* Set initial value of timer_res */

> > +     timer_res = res;

> > +     sigemptyset(&sigset);

> > +     /* Add SIGUSR1 to sigset */

> > +     sigaddset(&sigset, SIGUSR1);

> > +     sigprocmask(SIG_BLOCK, &sigset, NULL);

> > +

> > +     while (res > 0) {

> > +             /* Loop for 10 times to test the result */

> > +             loop_cnt = TIMER_RES_TEST_LOOP_COUNT;

> > +             sec  = res / ODP_TIME_SEC_IN_NS;

> > +             nsec = res - sec * ODP_TIME_SEC_IN_NS;

> > +

> > +             memset(&ispec, 0, sizeof(ispec));

> > +             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(timerid, 0, &ispec, NULL))

> > +                     ODP_ABORT("timer_settime() returned error %s\n",

> > +                               strerror(errno));

> > +             /* Set signal wait timeout to 10*res */

> > +             tmo.tv_sec = 0;

> > +             tmo.tv_nsec = res * 10;

> > +             while (loop_cnt--) {

> > +                     if (sigtimedwait(&sigset, &si, &tmo) > 0) {

> > +                             if (timer_getoverrun(timerid))

> > +                                     /* overrun at this resolution */

> > +                                     /* goto the end */

> > +                                     goto timer_res_init_done;

> > +                     }

> > +             }

> > +             /* Set timer_res */

> > +             timer_res = res;

> > +             /* Test the next timer resolution candidate */

> > +             res /= 10;

> > +     }

> > +timer_res_init_done:

> > +     if (timer_delete(timerid) != 0)

> > +             ODP_ABORT("timer_delete() returned error %s\n",

> > +                       strerror(errno));

> > +     sigemptyset(&sigset);

> > +     sigprocmask(SIG_BLOCK, &sigset, NULL);

> > +     return 0;

> > +}

> > +

> >  static void itimer_init(odp_timer_pool *tp)

> >  {

> >       struct sigevent   sigev;

> > @@ -795,6 +874,20 @@ static void itimer_fini(odp_timer_pool *tp)

> >   * Some parameter checks and error messages

> >   * No modificatios of internal state

> >   ************************************************************

> *****************/

> > +int odp_timer_capability(odp_timer_clk_src_t clk_src,

> > +                      odp_timer_capability_t *capa)

> > +{

> > +     int ret = 0;

> > +

> > +     if (clk_src == ODP_CLOCK_CPU) {

> > +             capa->res_ns = timer_res;

> > +     } else {

> > +             ODP_ERR("ODP timer system doesn't support external clock

> source currently\n");

> > +             ret = -1;

> > +     }

> > +     return ret;

> > +}

> > +

> >  odp_timer_pool_t

> >  odp_timer_pool_create(const char *name,

> >                     const odp_timer_pool_param_t *param)

> > @@ -1003,6 +1096,8 @@ int odp_timer_init_global(void)

> >  #endif

> >       odp_atomic_init_u32(&num_timer_pools, 0);

> >

> > +     timer_res_init();

> > +

> >       block_sigalarm();

> >

> >       return 0;

> >

>

>



-- 
Thanks,
Kevin
François Ozog Feb. 17, 2017, 8:51 a.m. UTC | #3
I think this is quite useful for cloud applications that do not know
the underlying hardware specs and need to check what is effectively
possible. "hardware" resolution is nothing without all surrounding
software layers, and virtualization in particular...

On 17 February 2017 at 09:43, Kevin Wang <kevin.wang@linaro.org> wrote:
> clock_getres() is to find the hardware resolution of specific clock source.

> It shows 1ns in most of modern system.

> What this API trys to do is to find out the resolution without overrun of

> this timer. Applications are able to deal with each timer event with that

> timer resolution.

>

> 2017-02-17 3:00 GMT+08:00 Maxim Uvarov <maxim.uvarov@linaro.org>:

>

>> why not clock_getres() ?

>>

>> Maxim.

>>

>> On 02/08/17 05:37, Kevin Wang wrote:

>> > Implement a new internal function timer_res_init() to detect the max

>> > timer resolution without overrun at the ODP init stage. It will check

>> > timer resolution from 1ms to 100us, 10us...1ns until the timer is

>> > overrun.

>> >

>> > Signed-off-by: Kevin Wang <kevin.wang@linaro.org>

>> > ---

>> >  platform/linux-generic/odp_timer.c | 95 ++++++++++++++++++++++++++++++

>> ++++++++

>> >  1 file changed, 95 insertions(+)

>> >

>> > diff --git a/platform/linux-generic/odp_timer.c

>> b/platform/linux-generic/odp_timer.c

>> > index 53fec08..9b884ea 100644

>> > --- a/platform/linux-generic/odp_timer.c

>> > +++ b/platform/linux-generic/odp_timer.c

>> > @@ -70,6 +70,9 @@ static _odp_atomic_flag_t locks[NUM_LOCKS]; /*

>> Multiple locks per cache line! */

>> >  #define IDX2LOCK(idx) (&locks[(idx) % NUM_LOCKS])

>> >  #endif

>> >

>> > +/* Timer resolution in nanoseconds */

>> > +static uint64_t timer_res;

>> > +

>> >  /***********************************************************

>> *******************

>> >   * Translation between timeout buffer and timeout header

>> >   ************************************************************

>> *****************/

>> > @@ -188,6 +191,7 @@ typedef struct odp_timer_pool_s {

>> >

>> >  #define MAX_TIMER_POOLS 255 /* Leave one for ODP_TIMER_INVALID */

>> >  #define INDEX_BITS 24

>> > +#define TIMER_RES_TEST_LOOP_COUNT 10

>> >  static odp_atomic_u32_t num_timer_pools;

>> >  static odp_timer_pool *timer_pool[MAX_TIMER_POOLS];

>> >

>> > @@ -738,6 +742,81 @@ static void *timer_thread(void *arg)

>> >       return NULL;

>> >  }

>> >

>> > +/* Get the max timer resolution without overrun and fill in timer_res

>> variable.

>> > + *

>> > + * Set timer's interval with candidate resolutions to get the max

>> resolution

>> > + * that the timer would not be overrun.

>> > + * The candidate resolution value is from 1ms to 100us, 10us...1ns etc.

>> > + */

>> > +static int timer_res_init(void)

>> > +{

>> > +     struct sigevent sigev;

>> > +     timer_t timerid;

>> > +     uint64_t res, sec, nsec;

>> > +     struct itimerspec ispec;

>> > +     sigset_t sigset;

>> > +     siginfo_t si;

>> > +     int loop_cnt;

>> > +     struct timespec tmo;

>> > +

>> > +     sigev.sigev_notify = SIGEV_THREAD_ID;

>> > +     sigev._sigev_un._tid = (pid_t)syscall(SYS_gettid);

>> > +     sigev.sigev_signo = SIGUSR1;

>> > +

>> > +     /* Create timer */

>> > +     if (timer_create(CLOCK_MONOTONIC, &sigev, &timerid))

>> > +             ODP_ABORT("timer_create() returned error %s\n",

>> > +                       strerror(errno));

>> > +

>> > +     /* Timer resolution start from 1ms */

>> > +     res = ODP_TIME_MSEC_IN_NS;

>> > +     /* Set initial value of timer_res */

>> > +     timer_res = res;

>> > +     sigemptyset(&sigset);

>> > +     /* Add SIGUSR1 to sigset */

>> > +     sigaddset(&sigset, SIGUSR1);

>> > +     sigprocmask(SIG_BLOCK, &sigset, NULL);

>> > +

>> > +     while (res > 0) {

>> > +             /* Loop for 10 times to test the result */

>> > +             loop_cnt = TIMER_RES_TEST_LOOP_COUNT;

>> > +             sec  = res / ODP_TIME_SEC_IN_NS;

>> > +             nsec = res - sec * ODP_TIME_SEC_IN_NS;

>> > +

>> > +             memset(&ispec, 0, sizeof(ispec));

>> > +             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(timerid, 0, &ispec, NULL))

>> > +                     ODP_ABORT("timer_settime() returned error %s\n",

>> > +                               strerror(errno));

>> > +             /* Set signal wait timeout to 10*res */

>> > +             tmo.tv_sec = 0;

>> > +             tmo.tv_nsec = res * 10;

>> > +             while (loop_cnt--) {

>> > +                     if (sigtimedwait(&sigset, &si, &tmo) > 0) {

>> > +                             if (timer_getoverrun(timerid))

>> > +                                     /* overrun at this resolution */

>> > +                                     /* goto the end */

>> > +                                     goto timer_res_init_done;

>> > +                     }

>> > +             }

>> > +             /* Set timer_res */

>> > +             timer_res = res;

>> > +             /* Test the next timer resolution candidate */

>> > +             res /= 10;

>> > +     }

>> > +timer_res_init_done:

>> > +     if (timer_delete(timerid) != 0)

>> > +             ODP_ABORT("timer_delete() returned error %s\n",

>> > +                       strerror(errno));

>> > +     sigemptyset(&sigset);

>> > +     sigprocmask(SIG_BLOCK, &sigset, NULL);

>> > +     return 0;

>> > +}

>> > +

>> >  static void itimer_init(odp_timer_pool *tp)

>> >  {

>> >       struct sigevent   sigev;

>> > @@ -795,6 +874,20 @@ static void itimer_fini(odp_timer_pool *tp)

>> >   * Some parameter checks and error messages

>> >   * No modificatios of internal state

>> >   ************************************************************

>> *****************/

>> > +int odp_timer_capability(odp_timer_clk_src_t clk_src,

>> > +                      odp_timer_capability_t *capa)

>> > +{

>> > +     int ret = 0;

>> > +

>> > +     if (clk_src == ODP_CLOCK_CPU) {

>> > +             capa->res_ns = timer_res;

>> > +     } else {

>> > +             ODP_ERR("ODP timer system doesn't support external clock

>> source currently\n");

>> > +             ret = -1;

>> > +     }

>> > +     return ret;

>> > +}

>> > +

>> >  odp_timer_pool_t

>> >  odp_timer_pool_create(const char *name,

>> >                     const odp_timer_pool_param_t *param)

>> > @@ -1003,6 +1096,8 @@ int odp_timer_init_global(void)

>> >  #endif

>> >       odp_atomic_init_u32(&num_timer_pools, 0);

>> >

>> > +     timer_res_init();

>> > +

>> >       block_sigalarm();

>> >

>> >       return 0;

>> >

>>

>>

>

>

> --

> Thanks,

> Kevin




-- 
François-Frédéric Ozog | Director Linaro Networking Group
T: +33.67221.6485
francois.ozog@linaro.org | Skype: ffozog
Maxim Uvarov Feb. 17, 2017, 9:46 a.m. UTC | #4
On 17 February 2017 at 11:51, Francois Ozog <francois.ozog@linaro.org>
wrote:

> I think this is quite useful for cloud applications that do not know

> the underlying hardware specs and need to check what is effectively

> possible. "hardware" resolution is nothing without all surrounding

> software layers, and virtualization in particular...

>

> On 17 February 2017 at 09:43, Kevin Wang <kevin.wang@linaro.org> wrote:

> > clock_getres() is to find the hardware resolution of specific clock

> source.

> > It shows 1ns in most of modern system.

> > What this API trys to do is to find out the resolution without overrun of

> > this timer. Applications are able to deal with each timer event with that

> > timer resolution.

> >

>



Timer run in some thread. With different load you will have different
latency on timer action.
Plus you can be in some atomic operation preempted by the kernel. Number of
'in fight' timers
also make sense. I'm not sure that we can do right calculation of that
value.

btw, what is bad in overrun? And what is overrun in current context? Is it
hardware time counter overrun
or variable presented as time in odp overrun?

Maxim.


> > 2017-02-17 3:00 GMT+08:00 Maxim Uvarov <maxim.uvarov@linaro.org>:

> >

> >> why not clock_getres() ?

> >>

> >> Maxim.

> >>

> >> On 02/08/17 05:37, Kevin Wang wrote:

> >> > Implement a new internal function timer_res_init() to detect the max

> >> > timer resolution without overrun at the ODP init stage. It will check

> >> > timer resolution from 1ms to 100us, 10us...1ns until the timer is

> >> > overrun.

> >> >

> >> > Signed-off-by: Kevin Wang <kevin.wang@linaro.org>

> >> > ---

> >> >  platform/linux-generic/odp_timer.c | 95

> ++++++++++++++++++++++++++++++

> >> ++++++++

> >> >  1 file changed, 95 insertions(+)

> >> >

> >> > diff --git a/platform/linux-generic/odp_timer.c

> >> b/platform/linux-generic/odp_timer.c

> >> > index 53fec08..9b884ea 100644

> >> > --- a/platform/linux-generic/odp_timer.c

> >> > +++ b/platform/linux-generic/odp_timer.c

> >> > @@ -70,6 +70,9 @@ static _odp_atomic_flag_t locks[NUM_LOCKS]; /*

> >> Multiple locks per cache line! */

> >> >  #define IDX2LOCK(idx) (&locks[(idx) % NUM_LOCKS])

> >> >  #endif

> >> >

> >> > +/* Timer resolution in nanoseconds */

> >> > +static uint64_t timer_res;

> >> > +

> >> >  /***********************************************************

> >> *******************

> >> >   * Translation between timeout buffer and timeout header

> >> >   ************************************************************

> >> *****************/

> >> > @@ -188,6 +191,7 @@ typedef struct odp_timer_pool_s {

> >> >

> >> >  #define MAX_TIMER_POOLS 255 /* Leave one for ODP_TIMER_INVALID */

> >> >  #define INDEX_BITS 24

> >> > +#define TIMER_RES_TEST_LOOP_COUNT 10

> >> >  static odp_atomic_u32_t num_timer_pools;

> >> >  static odp_timer_pool *timer_pool[MAX_TIMER_POOLS];

> >> >

> >> > @@ -738,6 +742,81 @@ static void *timer_thread(void *arg)

> >> >       return NULL;

> >> >  }

> >> >

> >> > +/* Get the max timer resolution without overrun and fill in timer_res

> >> variable.

> >> > + *

> >> > + * Set timer's interval with candidate resolutions to get the max

> >> resolution

> >> > + * that the timer would not be overrun.

> >> > + * The candidate resolution value is from 1ms to 100us, 10us...1ns

> etc.

> >> > + */

> >> > +static int timer_res_init(void)

> >> > +{

> >> > +     struct sigevent sigev;

> >> > +     timer_t timerid;

> >> > +     uint64_t res, sec, nsec;

> >> > +     struct itimerspec ispec;

> >> > +     sigset_t sigset;

> >> > +     siginfo_t si;

> >> > +     int loop_cnt;

> >> > +     struct timespec tmo;

> >> > +

> >> > +     sigev.sigev_notify = SIGEV_THREAD_ID;

> >> > +     sigev._sigev_un._tid = (pid_t)syscall(SYS_gettid);

> >> > +     sigev.sigev_signo = SIGUSR1;

> >> > +

> >> > +     /* Create timer */

> >> > +     if (timer_create(CLOCK_MONOTONIC, &sigev, &timerid))

> >> > +             ODP_ABORT("timer_create() returned error %s\n",

> >> > +                       strerror(errno));

> >> > +

> >> > +     /* Timer resolution start from 1ms */

> >> > +     res = ODP_TIME_MSEC_IN_NS;

> >> > +     /* Set initial value of timer_res */

> >> > +     timer_res = res;

> >> > +     sigemptyset(&sigset);

> >> > +     /* Add SIGUSR1 to sigset */

> >> > +     sigaddset(&sigset, SIGUSR1);

> >> > +     sigprocmask(SIG_BLOCK, &sigset, NULL);

> >> > +

> >> > +     while (res > 0) {

> >> > +             /* Loop for 10 times to test the result */

> >> > +             loop_cnt = TIMER_RES_TEST_LOOP_COUNT;

> >> > +             sec  = res / ODP_TIME_SEC_IN_NS;

> >> > +             nsec = res - sec * ODP_TIME_SEC_IN_NS;

> >> > +

> >> > +             memset(&ispec, 0, sizeof(ispec));

> >> > +             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(timerid, 0, &ispec, NULL))

> >> > +                     ODP_ABORT("timer_settime() returned error %s\n",

> >> > +                               strerror(errno));

> >> > +             /* Set signal wait timeout to 10*res */

> >> > +             tmo.tv_sec = 0;

> >> > +             tmo.tv_nsec = res * 10;

> >> > +             while (loop_cnt--) {

> >> > +                     if (sigtimedwait(&sigset, &si, &tmo) > 0) {

> >> > +                             if (timer_getoverrun(timerid))

> >> > +                                     /* overrun at this resolution */

> >> > +                                     /* goto the end */

> >> > +                                     goto timer_res_init_done;

> >> > +                     }

> >> > +             }

> >> > +             /* Set timer_res */

> >> > +             timer_res = res;

> >> > +             /* Test the next timer resolution candidate */

> >> > +             res /= 10;

> >> > +     }

> >> > +timer_res_init_done:

> >> > +     if (timer_delete(timerid) != 0)

> >> > +             ODP_ABORT("timer_delete() returned error %s\n",

> >> > +                       strerror(errno));

> >> > +     sigemptyset(&sigset);

> >> > +     sigprocmask(SIG_BLOCK, &sigset, NULL);

> >> > +     return 0;

> >> > +}

> >> > +

> >> >  static void itimer_init(odp_timer_pool *tp)

> >> >  {

> >> >       struct sigevent   sigev;

> >> > @@ -795,6 +874,20 @@ static void itimer_fini(odp_timer_pool *tp)

> >> >   * Some parameter checks and error messages

> >> >   * No modificatios of internal state

> >> >   ************************************************************

> >> *****************/

> >> > +int odp_timer_capability(odp_timer_clk_src_t clk_src,

> >> > +                      odp_timer_capability_t *capa)

> >> > +{

> >> > +     int ret = 0;

> >> > +

> >> > +     if (clk_src == ODP_CLOCK_CPU) {

> >> > +             capa->res_ns = timer_res;

> >> > +     } else {

> >> > +             ODP_ERR("ODP timer system doesn't support external clock

> >> source currently\n");

> >> > +             ret = -1;

> >> > +     }

> >> > +     return ret;

> >> > +}

> >> > +

> >> >  odp_timer_pool_t

> >> >  odp_timer_pool_create(const char *name,

> >> >                     const odp_timer_pool_param_t *param)

> >> > @@ -1003,6 +1096,8 @@ int odp_timer_init_global(void)

> >> >  #endif

> >> >       odp_atomic_init_u32(&num_timer_pools, 0);

> >> >

> >> > +     timer_res_init();

> >> > +

> >> >       block_sigalarm();

> >> >

> >> >       return 0;

> >> >

> >>

> >>

> >

> >

> > --

> > Thanks,

> > Kevin

>

>

>

> --

> François-Frédéric Ozog | Director Linaro Networking Group

> T: +33.67221.6485

> francois.ozog@linaro.org | Skype: ffozog

>
Kevin Wang Feb. 23, 2017, 2:18 a.m. UTC | #5
Hi Petri,
Any more comments?
I suggest we should push this patchset firstly. If we have any new
requirements, we can create a new ticket to trace it.
What's your thought?

2017-02-20 10:37 GMT+08:00 Kevin Wang <kevin.wang@linaro.org>:

> We don't know how many threads/locks the application will use. The final

> timer resolution is determined by applications. So what we are trying to do

> here is to find out a MAX resolution in a single thread on this system. So

> no matter in the multi-thread environment or other more workloads system,

> the resolution should be obvious less than this MAX value.

>

> I think overrun means the application is not able to keep up with dealing

> each timer interrupt. It will miss some interrupts.

>

> 2017-02-17 17:46 GMT+08:00 Maxim Uvarov <maxim.uvarov@linaro.org>:

>

>>

>>

>> On 17 February 2017 at 11:51, Francois Ozog <francois.ozog@linaro.org>

>> wrote:

>>

>>> I think this is quite useful for cloud applications that do not know

>>> the underlying hardware specs and need to check what is effectively

>>> possible. "hardware" resolution is nothing without all surrounding

>>> software layers, and virtualization in particular...

>>>

>>> On 17 February 2017 at 09:43, Kevin Wang <kevin.wang@linaro.org> wrote:

>>> > clock_getres() is to find the hardware resolution of specific clock

>>> source.

>>> > It shows 1ns in most of modern system.

>>> > What this API trys to do is to find out the resolution without overrun

>>> of

>>> > this timer. Applications are able to deal with each timer event with

>>> that

>>> > timer resolution.

>>> >

>>>

>>

>>

>> Timer run in some thread. With different load you will have different

>> latency on timer action.

>> Plus you can be in some atomic operation preempted by the kernel. Number

>> of 'in fight' timers

>> also make sense. I'm not sure that we can do right calculation of that

>> value.

>>

>> btw, what is bad in overrun? And what is overrun in current context? Is

>> it hardware time counter overrun

>> or variable presented as time in odp overrun?

>>

>> Maxim.

>>

>>

>>> > 2017-02-17 3:00 GMT+08:00 Maxim Uvarov <maxim.uvarov@linaro.org>:

>>> >

>>> >> why not clock_getres() ?

>>> >>

>>> >> Maxim.

>>> >>

>>> >> On 02/08/17 05:37, Kevin Wang wrote:

>>> >> > Implement a new internal function timer_res_init() to detect the max

>>> >> > timer resolution without overrun at the ODP init stage. It will

>>> check

>>> >> > timer resolution from 1ms to 100us, 10us...1ns until the timer is

>>> >> > overrun.

>>> >> >

>>> >> > Signed-off-by: Kevin Wang <kevin.wang@linaro.org>

>>> >> > ---

>>> >> >  platform/linux-generic/odp_timer.c | 95

>>> ++++++++++++++++++++++++++++++

>>> >> ++++++++

>>> >> >  1 file changed, 95 insertions(+)

>>> >> >

>>> >> > diff --git a/platform/linux-generic/odp_timer.c

>>> >> b/platform/linux-generic/odp_timer.c

>>> >> > index 53fec08..9b884ea 100644

>>> >> > --- a/platform/linux-generic/odp_timer.c

>>> >> > +++ b/platform/linux-generic/odp_timer.c

>>> >> > @@ -70,6 +70,9 @@ static _odp_atomic_flag_t locks[NUM_LOCKS]; /*

>>> >> Multiple locks per cache line! */

>>> >> >  #define IDX2LOCK(idx) (&locks[(idx) % NUM_LOCKS])

>>> >> >  #endif

>>> >> >

>>> >> > +/* Timer resolution in nanoseconds */

>>> >> > +static uint64_t timer_res;

>>> >> > +

>>> >> >  /***********************************************************

>>> >> *******************

>>> >> >   * Translation between timeout buffer and timeout header

>>> >> >   ************************************************************

>>> >> *****************/

>>> >> > @@ -188,6 +191,7 @@ typedef struct odp_timer_pool_s {

>>> >> >

>>> >> >  #define MAX_TIMER_POOLS 255 /* Leave one for ODP_TIMER_INVALID */

>>> >> >  #define INDEX_BITS 24

>>> >> > +#define TIMER_RES_TEST_LOOP_COUNT 10

>>> >> >  static odp_atomic_u32_t num_timer_pools;

>>> >> >  static odp_timer_pool *timer_pool[MAX_TIMER_POOLS];

>>> >> >

>>> >> > @@ -738,6 +742,81 @@ static void *timer_thread(void *arg)

>>> >> >       return NULL;

>>> >> >  }

>>> >> >

>>> >> > +/* Get the max timer resolution without overrun and fill in

>>> timer_res

>>> >> variable.

>>> >> > + *

>>> >> > + * Set timer's interval with candidate resolutions to get the max

>>> >> resolution

>>> >> > + * that the timer would not be overrun.

>>> >> > + * The candidate resolution value is from 1ms to 100us, 10us...1ns

>>> etc.

>>> >> > + */

>>> >> > +static int timer_res_init(void)

>>> >> > +{

>>> >> > +     struct sigevent sigev;

>>> >> > +     timer_t timerid;

>>> >> > +     uint64_t res, sec, nsec;

>>> >> > +     struct itimerspec ispec;

>>> >> > +     sigset_t sigset;

>>> >> > +     siginfo_t si;

>>> >> > +     int loop_cnt;

>>> >> > +     struct timespec tmo;

>>> >> > +

>>> >> > +     sigev.sigev_notify = SIGEV_THREAD_ID;

>>> >> > +     sigev._sigev_un._tid = (pid_t)syscall(SYS_gettid);

>>> >> > +     sigev.sigev_signo = SIGUSR1;

>>> >> > +

>>> >> > +     /* Create timer */

>>> >> > +     if (timer_create(CLOCK_MONOTONIC, &sigev, &timerid))

>>> >> > +             ODP_ABORT("timer_create() returned error %s\n",

>>> >> > +                       strerror(errno));

>>> >> > +

>>> >> > +     /* Timer resolution start from 1ms */

>>> >> > +     res = ODP_TIME_MSEC_IN_NS;

>>> >> > +     /* Set initial value of timer_res */

>>> >> > +     timer_res = res;

>>> >> > +     sigemptyset(&sigset);

>>> >> > +     /* Add SIGUSR1 to sigset */

>>> >> > +     sigaddset(&sigset, SIGUSR1);

>>> >> > +     sigprocmask(SIG_BLOCK, &sigset, NULL);

>>> >> > +

>>> >> > +     while (res > 0) {

>>> >> > +             /* Loop for 10 times to test the result */

>>> >> > +             loop_cnt = TIMER_RES_TEST_LOOP_COUNT;

>>> >> > +             sec  = res / ODP_TIME_SEC_IN_NS;

>>> >> > +             nsec = res - sec * ODP_TIME_SEC_IN_NS;

>>> >> > +

>>> >> > +             memset(&ispec, 0, sizeof(ispec));

>>> >> > +             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(timerid, 0, &ispec, NULL))

>>> >> > +                     ODP_ABORT("timer_settime() returned error

>>> %s\n",

>>> >> > +                               strerror(errno));

>>> >> > +             /* Set signal wait timeout to 10*res */

>>> >> > +             tmo.tv_sec = 0;

>>> >> > +             tmo.tv_nsec = res * 10;

>>> >> > +             while (loop_cnt--) {

>>> >> > +                     if (sigtimedwait(&sigset, &si, &tmo) > 0) {

>>> >> > +                             if (timer_getoverrun(timerid))

>>> >> > +                                     /* overrun at this resolution

>>> */

>>> >> > +                                     /* goto the end */

>>> >> > +                                     goto timer_res_init_done;

>>> >> > +                     }

>>> >> > +             }

>>> >> > +             /* Set timer_res */

>>> >> > +             timer_res = res;

>>> >> > +             /* Test the next timer resolution candidate */

>>> >> > +             res /= 10;

>>> >> > +     }

>>> >> > +timer_res_init_done:

>>> >> > +     if (timer_delete(timerid) != 0)

>>> >> > +             ODP_ABORT("timer_delete() returned error %s\n",

>>> >> > +                       strerror(errno));

>>> >> > +     sigemptyset(&sigset);

>>> >> > +     sigprocmask(SIG_BLOCK, &sigset, NULL);

>>> >> > +     return 0;

>>> >> > +}

>>> >> > +

>>> >> >  static void itimer_init(odp_timer_pool *tp)

>>> >> >  {

>>> >> >       struct sigevent   sigev;

>>> >> > @@ -795,6 +874,20 @@ static void itimer_fini(odp_timer_pool *tp)

>>> >> >   * Some parameter checks and error messages

>>> >> >   * No modificatios of internal state

>>> >> >   ************************************************************

>>> >> *****************/

>>> >> > +int odp_timer_capability(odp_timer_clk_src_t clk_src,

>>> >> > +                      odp_timer_capability_t *capa)

>>> >> > +{

>>> >> > +     int ret = 0;

>>> >> > +

>>> >> > +     if (clk_src == ODP_CLOCK_CPU) {

>>> >> > +             capa->res_ns = timer_res;

>>> >> > +     } else {

>>> >> > +             ODP_ERR("ODP timer system doesn't support external

>>> clock

>>> >> source currently\n");

>>> >> > +             ret = -1;

>>> >> > +     }

>>> >> > +     return ret;

>>> >> > +}

>>> >> > +

>>> >> >  odp_timer_pool_t

>>> >> >  odp_timer_pool_create(const char *name,

>>> >> >                     const odp_timer_pool_param_t *param)

>>> >> > @@ -1003,6 +1096,8 @@ int odp_timer_init_global(void)

>>> >> >  #endif

>>> >> >       odp_atomic_init_u32(&num_timer_pools, 0);

>>> >> >

>>> >> > +     timer_res_init();

>>> >> > +

>>> >> >       block_sigalarm();

>>> >> >

>>> >> >       return 0;

>>> >> >

>>> >>

>>> >>

>>> >

>>> >

>>> > --

>>> > Thanks,

>>> > Kevin

>>>

>>>

>>>

>>> --

>>> François-Frédéric Ozog | Director Linaro Networking Group

>>> T: +33.67221.6485

>>> francois.ozog@linaro.org | Skype: ffozog

>>>

>>

>>

>

>

> --

> Thanks,

> Kevin

>




-- 
Thanks,
Kevin
diff mbox series

Patch

diff --git a/platform/linux-generic/odp_timer.c b/platform/linux-generic/odp_timer.c
index 53fec08..9b884ea 100644
--- a/platform/linux-generic/odp_timer.c
+++ b/platform/linux-generic/odp_timer.c
@@ -70,6 +70,9 @@  static _odp_atomic_flag_t locks[NUM_LOCKS]; /* Multiple locks per cache line! */
 #define IDX2LOCK(idx) (&locks[(idx) % NUM_LOCKS])
 #endif
 
+/* Timer resolution in nanoseconds */
+static uint64_t timer_res;
+
 /******************************************************************************
  * Translation between timeout buffer and timeout header
  *****************************************************************************/
@@ -188,6 +191,7 @@  typedef struct odp_timer_pool_s {
 
 #define MAX_TIMER_POOLS 255 /* Leave one for ODP_TIMER_INVALID */
 #define INDEX_BITS 24
+#define TIMER_RES_TEST_LOOP_COUNT 10
 static odp_atomic_u32_t num_timer_pools;
 static odp_timer_pool *timer_pool[MAX_TIMER_POOLS];
 
@@ -738,6 +742,81 @@  static void *timer_thread(void *arg)
 	return NULL;
 }
 
+/* Get the max timer resolution without overrun and fill in timer_res variable.
+ *
+ * Set timer's interval with candidate resolutions to get the max resolution
+ * that the timer would not be overrun.
+ * The candidate resolution value is from 1ms to 100us, 10us...1ns etc.
+ */
+static int timer_res_init(void)
+{
+	struct sigevent sigev;
+	timer_t timerid;
+	uint64_t res, sec, nsec;
+	struct itimerspec ispec;
+	sigset_t sigset;
+	siginfo_t si;
+	int loop_cnt;
+	struct timespec tmo;
+
+	sigev.sigev_notify = SIGEV_THREAD_ID;
+	sigev._sigev_un._tid = (pid_t)syscall(SYS_gettid);
+	sigev.sigev_signo = SIGUSR1;
+
+	/* Create timer */
+	if (timer_create(CLOCK_MONOTONIC, &sigev, &timerid))
+		ODP_ABORT("timer_create() returned error %s\n",
+			  strerror(errno));
+
+	/* Timer resolution start from 1ms */
+	res = ODP_TIME_MSEC_IN_NS;
+	/* Set initial value of timer_res */
+	timer_res = res;
+	sigemptyset(&sigset);
+	/* Add SIGUSR1 to sigset */
+	sigaddset(&sigset, SIGUSR1);
+	sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+	while (res > 0) {
+		/* Loop for 10 times to test the result */
+		loop_cnt = TIMER_RES_TEST_LOOP_COUNT;
+		sec  = res / ODP_TIME_SEC_IN_NS;
+		nsec = res - sec * ODP_TIME_SEC_IN_NS;
+
+		memset(&ispec, 0, sizeof(ispec));
+		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(timerid, 0, &ispec, NULL))
+			ODP_ABORT("timer_settime() returned error %s\n",
+				  strerror(errno));
+		/* Set signal wait timeout to 10*res */
+		tmo.tv_sec = 0;
+		tmo.tv_nsec = res * 10;
+		while (loop_cnt--) {
+			if (sigtimedwait(&sigset, &si, &tmo) > 0) {
+				if (timer_getoverrun(timerid))
+					/* overrun at this resolution */
+					/* goto the end */
+					goto timer_res_init_done;
+			}
+		}
+		/* Set timer_res */
+		timer_res = res;
+		/* Test the next timer resolution candidate */
+		res /= 10;
+	}
+timer_res_init_done:
+	if (timer_delete(timerid) != 0)
+		ODP_ABORT("timer_delete() returned error %s\n",
+			  strerror(errno));
+	sigemptyset(&sigset);
+	sigprocmask(SIG_BLOCK, &sigset, NULL);
+	return 0;
+}
+
 static void itimer_init(odp_timer_pool *tp)
 {
 	struct sigevent   sigev;
@@ -795,6 +874,20 @@  static void itimer_fini(odp_timer_pool *tp)
  * Some parameter checks and error messages
  * No modificatios of internal state
  *****************************************************************************/
+int odp_timer_capability(odp_timer_clk_src_t clk_src,
+			 odp_timer_capability_t *capa)
+{
+	int ret = 0;
+
+	if (clk_src == ODP_CLOCK_CPU) {
+		capa->res_ns = timer_res;
+	} else {
+		ODP_ERR("ODP timer system doesn't support external clock source currently\n");
+		ret = -1;
+	}
+	return ret;
+}
+
 odp_timer_pool_t
 odp_timer_pool_create(const char *name,
 		      const odp_timer_pool_param_t *param)
@@ -1003,6 +1096,8 @@  int odp_timer_init_global(void)
 #endif
 	odp_atomic_init_u32(&num_timer_pools, 0);
 
+	timer_res_init();
+
 	block_sigalarm();
 
 	return 0;