2015-03-12 03:40:03 +03:00
/* set_timer latency test
* John Stultz ( john . stultz @ linaro . org )
* ( C ) Copyright Linaro 2014
* Licensed under the GPLv2
*
* This test makes sure the set_timer api is correct
*
* To build :
* $ gcc set - timer - lat . c - o set - timer - lat - lrt
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2017-07-26 00:36:26 +03:00
# include <errno.h>
2015-03-12 03:40:03 +03:00
# include <stdio.h>
# include <unistd.h>
# include <time.h>
# include <string.h>
# include <signal.h>
# include <stdlib.h>
# include <pthread.h>
# include "../kselftest.h"
# define CLOCK_REALTIME 0
# define CLOCK_MONOTONIC 1
# define CLOCK_PROCESS_CPUTIME_ID 2
# define CLOCK_THREAD_CPUTIME_ID 3
# define CLOCK_MONOTONIC_RAW 4
# define CLOCK_REALTIME_COARSE 5
# define CLOCK_MONOTONIC_COARSE 6
# define CLOCK_BOOTTIME 7
# define CLOCK_REALTIME_ALARM 8
# define CLOCK_BOOTTIME_ALARM 9
# define CLOCK_HWSPECIFIC 10
# define CLOCK_TAI 11
# define NR_CLOCKIDS 12
# define NSEC_PER_SEC 1000000000ULL
# define UNRESONABLE_LATENCY 40000000 /* 40ms in nanosecs */
2015-03-26 02:44:33 +03:00
# define TIMER_SECS 1
2015-03-12 03:40:03 +03:00
int alarmcount ;
int clock_id ;
struct timespec start_time ;
long long max_latency_ns ;
2017-07-26 00:36:25 +03:00
int timer_fired_early ;
2015-03-12 03:40:03 +03:00
char * clockstring ( int clockid )
{
switch ( clockid ) {
case CLOCK_REALTIME :
return " CLOCK_REALTIME " ;
case CLOCK_MONOTONIC :
return " CLOCK_MONOTONIC " ;
case CLOCK_PROCESS_CPUTIME_ID :
return " CLOCK_PROCESS_CPUTIME_ID " ;
case CLOCK_THREAD_CPUTIME_ID :
return " CLOCK_THREAD_CPUTIME_ID " ;
case CLOCK_MONOTONIC_RAW :
return " CLOCK_MONOTONIC_RAW " ;
case CLOCK_REALTIME_COARSE :
return " CLOCK_REALTIME_COARSE " ;
case CLOCK_MONOTONIC_COARSE :
return " CLOCK_MONOTONIC_COARSE " ;
case CLOCK_BOOTTIME :
return " CLOCK_BOOTTIME " ;
case CLOCK_REALTIME_ALARM :
return " CLOCK_REALTIME_ALARM " ;
case CLOCK_BOOTTIME_ALARM :
return " CLOCK_BOOTTIME_ALARM " ;
case CLOCK_TAI :
return " CLOCK_TAI " ;
} ;
return " UNKNOWN_CLOCKID " ;
}
long long timespec_sub ( struct timespec a , struct timespec b )
{
long long ret = NSEC_PER_SEC * b . tv_sec + b . tv_nsec ;
ret - = NSEC_PER_SEC * a . tv_sec + a . tv_nsec ;
return ret ;
}
void sigalarm ( int signo )
{
long long delta_ns ;
struct timespec ts ;
clock_gettime ( clock_id , & ts ) ;
alarmcount + + ;
delta_ns = timespec_sub ( start_time , ts ) ;
delta_ns - = NSEC_PER_SEC * TIMER_SECS * alarmcount ;
if ( delta_ns < 0 )
2017-07-26 00:36:25 +03:00
timer_fired_early = 1 ;
2015-03-12 03:40:03 +03:00
if ( delta_ns > max_latency_ns )
max_latency_ns = delta_ns ;
}
2017-07-26 00:36:26 +03:00
void describe_timer ( int flags , int interval )
2017-07-26 00:36:25 +03:00
{
2017-07-26 00:36:26 +03:00
printf ( " %-22s %s %s " ,
2017-07-26 00:36:25 +03:00
clockstring ( clock_id ) ,
2017-07-26 00:36:26 +03:00
flags ? " ABSTIME " : " RELTIME " ,
interval ? " PERIODIC " : " ONE-SHOT " ) ;
2017-07-26 00:36:25 +03:00
}
2017-07-26 00:36:26 +03:00
int setup_timer ( int clock_id , int flags , int interval , timer_t * tm1 )
2015-03-12 03:40:03 +03:00
{
struct sigevent se ;
struct itimerspec its1 , its2 ;
int err ;
/* Set up timer: */
memset ( & se , 0 , sizeof ( se ) ) ;
se . sigev_notify = SIGEV_SIGNAL ;
se . sigev_signo = SIGRTMAX ;
se . sigev_value . sival_int = 0 ;
max_latency_ns = 0 ;
alarmcount = 0 ;
2017-07-26 00:36:25 +03:00
timer_fired_early = 0 ;
2015-03-12 03:40:03 +03:00
2017-07-26 00:36:26 +03:00
err = timer_create ( clock_id , & se , tm1 ) ;
2015-03-12 03:40:03 +03:00
if ( err ) {
2015-04-02 06:43:39 +03:00
if ( ( clock_id = = CLOCK_REALTIME_ALARM ) | |
( clock_id = = CLOCK_BOOTTIME_ALARM ) ) {
printf ( " %-22s %s missing CAP_WAKE_ALARM? : [UNSUPPORTED] \n " ,
clockstring ( clock_id ) ,
flags ? " ABSTIME " : " RELTIME " ) ;
2017-09-21 22:05:18 +03:00
/* Indicate timer isn't set, so caller doesn't wait */
return 1 ;
2015-04-02 06:43:39 +03:00
}
2015-03-12 03:40:03 +03:00
printf ( " %s - timer_create() failed \n " , clockstring ( clock_id ) ) ;
return - 1 ;
}
clock_gettime ( clock_id , & start_time ) ;
if ( flags ) {
its1 . it_value = start_time ;
its1 . it_value . tv_sec + = TIMER_SECS ;
} else {
its1 . it_value . tv_sec = TIMER_SECS ;
its1 . it_value . tv_nsec = 0 ;
}
2017-07-26 00:36:26 +03:00
its1 . it_interval . tv_sec = interval ;
2015-03-12 03:40:03 +03:00
its1 . it_interval . tv_nsec = 0 ;
2017-07-26 00:36:26 +03:00
err = timer_settime ( * tm1 , flags , & its1 , & its2 ) ;
2015-03-12 03:40:03 +03:00
if ( err ) {
printf ( " %s - timer_settime() failed \n " , clockstring ( clock_id ) ) ;
return - 1 ;
}
2017-07-26 00:36:26 +03:00
return 0 ;
}
2015-03-12 03:40:03 +03:00
2017-07-26 00:36:26 +03:00
int check_timer_latency ( int flags , int interval )
{
int err = 0 ;
describe_timer ( flags , interval ) ;
2017-07-26 00:36:25 +03:00
printf ( " timer fired early: %7d : " , timer_fired_early ) ;
if ( ! timer_fired_early ) {
printf ( " [OK] \n " ) ;
} else {
printf ( " [FAILED] \n " ) ;
err = - 1 ;
}
2017-07-26 00:36:26 +03:00
describe_timer ( flags , interval ) ;
2017-07-26 00:36:25 +03:00
printf ( " max latency: %10lld ns : " , max_latency_ns ) ;
2015-03-12 03:40:03 +03:00
if ( max_latency_ns < UNRESONABLE_LATENCY ) {
printf ( " [OK] \n " ) ;
2017-07-26 00:36:25 +03:00
} else {
printf ( " [FAILED] \n " ) ;
err = - 1 ;
2015-03-12 03:40:03 +03:00
}
2017-07-26 00:36:25 +03:00
return err ;
2015-03-12 03:40:03 +03:00
}
2017-07-26 00:36:26 +03:00
int check_alarmcount ( int flags , int interval )
{
describe_timer ( flags , interval ) ;
printf ( " count: %19d : " , alarmcount ) ;
if ( alarmcount = = 1 ) {
printf ( " [OK] \n " ) ;
return 0 ;
}
printf ( " [FAILED] \n " ) ;
return - 1 ;
}
int do_timer ( int clock_id , int flags )
{
timer_t tm1 ;
const int interval = TIMER_SECS ;
int err ;
err = setup_timer ( clock_id , flags , interval , & tm1 ) ;
2017-09-21 22:05:18 +03:00
/* Unsupported case - return 0 to not fail the test */
2017-07-26 00:36:26 +03:00
if ( err )
2017-09-21 22:05:18 +03:00
return err = = 1 ? 0 : err ;
2017-07-26 00:36:26 +03:00
while ( alarmcount < 5 )
sleep ( 1 ) ;
timer_delete ( tm1 ) ;
return check_timer_latency ( flags , interval ) ;
}
int do_timer_oneshot ( int clock_id , int flags )
{
timer_t tm1 ;
const int interval = 0 ;
struct timeval timeout ;
int err ;
err = setup_timer ( clock_id , flags , interval , & tm1 ) ;
2017-09-21 22:05:18 +03:00
/* Unsupported case - return 0 to not fail the test */
2017-07-26 00:36:26 +03:00
if ( err )
2017-09-21 22:05:18 +03:00
return err = = 1 ? 0 : err ;
2017-07-26 00:36:26 +03:00
memset ( & timeout , 0 , sizeof ( timeout ) ) ;
timeout . tv_sec = 5 ;
do {
2017-09-21 22:46:01 +03:00
err = select ( 0 , NULL , NULL , NULL , & timeout ) ;
2017-07-26 00:36:26 +03:00
} while ( err = = - 1 & & errno = = EINTR ) ;
timer_delete ( tm1 ) ;
err = check_timer_latency ( flags , interval ) ;
err | = check_alarmcount ( flags , interval ) ;
return err ;
}
2015-03-12 03:40:03 +03:00
int main ( void )
{
struct sigaction act ;
int signum = SIGRTMAX ;
int ret = 0 ;
/* Set up signal handler: */
sigfillset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
act . sa_handler = sigalarm ;
sigaction ( signum , & act , NULL ) ;
printf ( " Setting timers for every %i seconds \n " , TIMER_SECS ) ;
for ( clock_id = 0 ; clock_id < NR_CLOCKIDS ; clock_id + + ) {
if ( ( clock_id = = CLOCK_PROCESS_CPUTIME_ID ) | |
( clock_id = = CLOCK_THREAD_CPUTIME_ID ) | |
( clock_id = = CLOCK_MONOTONIC_RAW ) | |
( clock_id = = CLOCK_REALTIME_COARSE ) | |
( clock_id = = CLOCK_MONOTONIC_COARSE ) | |
( clock_id = = CLOCK_HWSPECIFIC ) )
continue ;
ret | = do_timer ( clock_id , TIMER_ABSTIME ) ;
ret | = do_timer ( clock_id , 0 ) ;
2017-07-26 00:36:26 +03:00
ret | = do_timer_oneshot ( clock_id , TIMER_ABSTIME ) ;
ret | = do_timer_oneshot ( clock_id , 0 ) ;
2015-03-12 03:40:03 +03:00
}
if ( ret )
return ksft_exit_fail ( ) ;
return ksft_exit_pass ( ) ;
}