1 /* set_timer latency test 2 * John Stultz (john.stultz@linaro.org) 3 * (C) Copyright Linaro 2014 4 * Licensed under the GPLv2 5 * 6 * This test makes sure the set_timer api is correct 7 * 8 * To build: 9 * $ gcc set-timer-lat.c -o set-timer-lat -lrt 10 * 11 * This program is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation, either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 */ 21 22 23 #include <errno.h> 24 #include <stdio.h> 25 #include <unistd.h> 26 #include <time.h> 27 #include <string.h> 28 #include <signal.h> 29 #include <stdlib.h> 30 #include <pthread.h> 31 #ifdef KTEST 32 #include "../kselftest.h" 33 #else 34 static inline int ksft_exit_pass(void) 35 { 36 exit(0); 37 } 38 static inline int ksft_exit_fail(void) 39 { 40 exit(1); 41 } 42 #endif 43 44 #define CLOCK_REALTIME 0 45 #define CLOCK_MONOTONIC 1 46 #define CLOCK_PROCESS_CPUTIME_ID 2 47 #define CLOCK_THREAD_CPUTIME_ID 3 48 #define CLOCK_MONOTONIC_RAW 4 49 #define CLOCK_REALTIME_COARSE 5 50 #define CLOCK_MONOTONIC_COARSE 6 51 #define CLOCK_BOOTTIME 7 52 #define CLOCK_REALTIME_ALARM 8 53 #define CLOCK_BOOTTIME_ALARM 9 54 #define CLOCK_HWSPECIFIC 10 55 #define CLOCK_TAI 11 56 #define NR_CLOCKIDS 12 57 58 59 #define NSEC_PER_SEC 1000000000ULL 60 #define UNRESONABLE_LATENCY 40000000 /* 40ms in nanosecs */ 61 62 #define TIMER_SECS 1 63 int alarmcount; 64 int clock_id; 65 struct timespec start_time; 66 long long max_latency_ns; 67 int timer_fired_early; 68 69 char *clockstring(int clockid) 70 { 71 switch (clockid) { 72 case CLOCK_REALTIME: 73 return "CLOCK_REALTIME"; 74 case CLOCK_MONOTONIC: 75 return "CLOCK_MONOTONIC"; 76 case CLOCK_PROCESS_CPUTIME_ID: 77 return "CLOCK_PROCESS_CPUTIME_ID"; 78 case CLOCK_THREAD_CPUTIME_ID: 79 return "CLOCK_THREAD_CPUTIME_ID"; 80 case CLOCK_MONOTONIC_RAW: 81 return "CLOCK_MONOTONIC_RAW"; 82 case CLOCK_REALTIME_COARSE: 83 return "CLOCK_REALTIME_COARSE"; 84 case CLOCK_MONOTONIC_COARSE: 85 return "CLOCK_MONOTONIC_COARSE"; 86 case CLOCK_BOOTTIME: 87 return "CLOCK_BOOTTIME"; 88 case CLOCK_REALTIME_ALARM: 89 return "CLOCK_REALTIME_ALARM"; 90 case CLOCK_BOOTTIME_ALARM: 91 return "CLOCK_BOOTTIME_ALARM"; 92 case CLOCK_TAI: 93 return "CLOCK_TAI"; 94 }; 95 return "UNKNOWN_CLOCKID"; 96 } 97 98 99 long long timespec_sub(struct timespec a, struct timespec b) 100 { 101 long long ret = NSEC_PER_SEC * b.tv_sec + b.tv_nsec; 102 103 ret -= NSEC_PER_SEC * a.tv_sec + a.tv_nsec; 104 return ret; 105 } 106 107 108 void sigalarm(int signo) 109 { 110 long long delta_ns; 111 struct timespec ts; 112 113 clock_gettime(clock_id, &ts); 114 alarmcount++; 115 116 delta_ns = timespec_sub(start_time, ts); 117 delta_ns -= NSEC_PER_SEC * TIMER_SECS * alarmcount; 118 119 if (delta_ns < 0) 120 timer_fired_early = 1; 121 122 if (delta_ns > max_latency_ns) 123 max_latency_ns = delta_ns; 124 } 125 126 void describe_timer(int flags, int interval) 127 { 128 printf("%-22s %s %s ", 129 clockstring(clock_id), 130 flags ? "ABSTIME":"RELTIME", 131 interval ? "PERIODIC":"ONE-SHOT"); 132 } 133 134 int setup_timer(int clock_id, int flags, int interval, timer_t *tm1) 135 { 136 struct sigevent se; 137 struct itimerspec its1, its2; 138 int err; 139 140 /* Set up timer: */ 141 memset(&se, 0, sizeof(se)); 142 se.sigev_notify = SIGEV_SIGNAL; 143 se.sigev_signo = SIGRTMAX; 144 se.sigev_value.sival_int = 0; 145 146 max_latency_ns = 0; 147 alarmcount = 0; 148 timer_fired_early = 0; 149 150 err = timer_create(clock_id, &se, tm1); 151 if (err) { 152 if ((clock_id == CLOCK_REALTIME_ALARM) || 153 (clock_id == CLOCK_BOOTTIME_ALARM)) { 154 printf("%-22s %s missing CAP_WAKE_ALARM? : [UNSUPPORTED]\n", 155 clockstring(clock_id), 156 flags ? "ABSTIME":"RELTIME"); 157 return 0; 158 } 159 printf("%s - timer_create() failed\n", clockstring(clock_id)); 160 return -1; 161 } 162 163 clock_gettime(clock_id, &start_time); 164 if (flags) { 165 its1.it_value = start_time; 166 its1.it_value.tv_sec += TIMER_SECS; 167 } else { 168 its1.it_value.tv_sec = TIMER_SECS; 169 its1.it_value.tv_nsec = 0; 170 } 171 its1.it_interval.tv_sec = interval; 172 its1.it_interval.tv_nsec = 0; 173 174 err = timer_settime(*tm1, flags, &its1, &its2); 175 if (err) { 176 printf("%s - timer_settime() failed\n", clockstring(clock_id)); 177 return -1; 178 } 179 180 return 0; 181 } 182 183 int check_timer_latency(int flags, int interval) 184 { 185 int err = 0; 186 187 describe_timer(flags, interval); 188 printf("timer fired early: %7d : ", timer_fired_early); 189 if (!timer_fired_early) { 190 printf("[OK]\n"); 191 } else { 192 printf("[FAILED]\n"); 193 err = -1; 194 } 195 196 describe_timer(flags, interval); 197 printf("max latency: %10lld ns : ", max_latency_ns); 198 199 if (max_latency_ns < UNRESONABLE_LATENCY) { 200 printf("[OK]\n"); 201 } else { 202 printf("[FAILED]\n"); 203 err = -1; 204 } 205 return err; 206 } 207 208 int check_alarmcount(int flags, int interval) 209 { 210 describe_timer(flags, interval); 211 printf("count: %19d : ", alarmcount); 212 if (alarmcount == 1) { 213 printf("[OK]\n"); 214 return 0; 215 } 216 printf("[FAILED]\n"); 217 return -1; 218 } 219 220 int do_timer(int clock_id, int flags) 221 { 222 timer_t tm1; 223 const int interval = TIMER_SECS; 224 int err; 225 226 err = setup_timer(clock_id, flags, interval, &tm1); 227 if (err) 228 return err; 229 230 while (alarmcount < 5) 231 sleep(1); 232 233 timer_delete(tm1); 234 return check_timer_latency(flags, interval); 235 } 236 237 int do_timer_oneshot(int clock_id, int flags) 238 { 239 timer_t tm1; 240 const int interval = 0; 241 struct timeval timeout; 242 fd_set fds; 243 int err; 244 245 err = setup_timer(clock_id, flags, interval, &tm1); 246 if (err) 247 return err; 248 249 memset(&timeout, 0, sizeof(timeout)); 250 timeout.tv_sec = 5; 251 FD_ZERO(&fds); 252 do { 253 err = select(FD_SETSIZE, &fds, NULL, NULL, &timeout); 254 } while (err == -1 && errno == EINTR); 255 256 timer_delete(tm1); 257 err = check_timer_latency(flags, interval); 258 err |= check_alarmcount(flags, interval); 259 return err; 260 } 261 262 int main(void) 263 { 264 struct sigaction act; 265 int signum = SIGRTMAX; 266 int ret = 0; 267 268 /* Set up signal handler: */ 269 sigfillset(&act.sa_mask); 270 act.sa_flags = 0; 271 act.sa_handler = sigalarm; 272 sigaction(signum, &act, NULL); 273 274 printf("Setting timers for every %i seconds\n", TIMER_SECS); 275 for (clock_id = 0; clock_id < NR_CLOCKIDS; clock_id++) { 276 277 if ((clock_id == CLOCK_PROCESS_CPUTIME_ID) || 278 (clock_id == CLOCK_THREAD_CPUTIME_ID) || 279 (clock_id == CLOCK_MONOTONIC_RAW) || 280 (clock_id == CLOCK_REALTIME_COARSE) || 281 (clock_id == CLOCK_MONOTONIC_COARSE) || 282 (clock_id == CLOCK_HWSPECIFIC)) 283 continue; 284 285 ret |= do_timer(clock_id, TIMER_ABSTIME); 286 ret |= do_timer(clock_id, 0); 287 ret |= do_timer_oneshot(clock_id, TIMER_ABSTIME); 288 ret |= do_timer_oneshot(clock_id, 0); 289 } 290 if (ret) 291 return ksft_exit_fail(); 292 return ksft_exit_pass(); 293 } 294