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 #include "../kselftest.h" 32 33 #define CLOCK_REALTIME 0 34 #define CLOCK_MONOTONIC 1 35 #define CLOCK_PROCESS_CPUTIME_ID 2 36 #define CLOCK_THREAD_CPUTIME_ID 3 37 #define CLOCK_MONOTONIC_RAW 4 38 #define CLOCK_REALTIME_COARSE 5 39 #define CLOCK_MONOTONIC_COARSE 6 40 #define CLOCK_BOOTTIME 7 41 #define CLOCK_REALTIME_ALARM 8 42 #define CLOCK_BOOTTIME_ALARM 9 43 #define CLOCK_HWSPECIFIC 10 44 #define CLOCK_TAI 11 45 #define NR_CLOCKIDS 12 46 47 48 #define NSEC_PER_SEC 1000000000ULL 49 #define UNRESONABLE_LATENCY 40000000 /* 40ms in nanosecs */ 50 51 #define TIMER_SECS 1 52 int alarmcount; 53 int clock_id; 54 struct timespec start_time; 55 long long max_latency_ns; 56 int timer_fired_early; 57 58 char *clockstring(int clockid) 59 { 60 switch (clockid) { 61 case CLOCK_REALTIME: 62 return "CLOCK_REALTIME"; 63 case CLOCK_MONOTONIC: 64 return "CLOCK_MONOTONIC"; 65 case CLOCK_PROCESS_CPUTIME_ID: 66 return "CLOCK_PROCESS_CPUTIME_ID"; 67 case CLOCK_THREAD_CPUTIME_ID: 68 return "CLOCK_THREAD_CPUTIME_ID"; 69 case CLOCK_MONOTONIC_RAW: 70 return "CLOCK_MONOTONIC_RAW"; 71 case CLOCK_REALTIME_COARSE: 72 return "CLOCK_REALTIME_COARSE"; 73 case CLOCK_MONOTONIC_COARSE: 74 return "CLOCK_MONOTONIC_COARSE"; 75 case CLOCK_BOOTTIME: 76 return "CLOCK_BOOTTIME"; 77 case CLOCK_REALTIME_ALARM: 78 return "CLOCK_REALTIME_ALARM"; 79 case CLOCK_BOOTTIME_ALARM: 80 return "CLOCK_BOOTTIME_ALARM"; 81 case CLOCK_TAI: 82 return "CLOCK_TAI"; 83 }; 84 return "UNKNOWN_CLOCKID"; 85 } 86 87 88 long long timespec_sub(struct timespec a, struct timespec b) 89 { 90 long long ret = NSEC_PER_SEC * b.tv_sec + b.tv_nsec; 91 92 ret -= NSEC_PER_SEC * a.tv_sec + a.tv_nsec; 93 return ret; 94 } 95 96 97 void sigalarm(int signo) 98 { 99 long long delta_ns; 100 struct timespec ts; 101 102 clock_gettime(clock_id, &ts); 103 alarmcount++; 104 105 delta_ns = timespec_sub(start_time, ts); 106 delta_ns -= NSEC_PER_SEC * TIMER_SECS * alarmcount; 107 108 if (delta_ns < 0) 109 timer_fired_early = 1; 110 111 if (delta_ns > max_latency_ns) 112 max_latency_ns = delta_ns; 113 } 114 115 void describe_timer(int flags, int interval) 116 { 117 printf("%-22s %s %s ", 118 clockstring(clock_id), 119 flags ? "ABSTIME":"RELTIME", 120 interval ? "PERIODIC":"ONE-SHOT"); 121 } 122 123 int setup_timer(int clock_id, int flags, int interval, timer_t *tm1) 124 { 125 struct sigevent se; 126 struct itimerspec its1, its2; 127 int err; 128 129 /* Set up timer: */ 130 memset(&se, 0, sizeof(se)); 131 se.sigev_notify = SIGEV_SIGNAL; 132 se.sigev_signo = SIGRTMAX; 133 se.sigev_value.sival_int = 0; 134 135 max_latency_ns = 0; 136 alarmcount = 0; 137 timer_fired_early = 0; 138 139 err = timer_create(clock_id, &se, tm1); 140 if (err) { 141 if ((clock_id == CLOCK_REALTIME_ALARM) || 142 (clock_id == CLOCK_BOOTTIME_ALARM)) { 143 printf("%-22s %s missing CAP_WAKE_ALARM? : [UNSUPPORTED]\n", 144 clockstring(clock_id), 145 flags ? "ABSTIME":"RELTIME"); 146 /* Indicate timer isn't set, so caller doesn't wait */ 147 return 1; 148 } 149 printf("%s - timer_create() failed\n", clockstring(clock_id)); 150 return -1; 151 } 152 153 clock_gettime(clock_id, &start_time); 154 if (flags) { 155 its1.it_value = start_time; 156 its1.it_value.tv_sec += TIMER_SECS; 157 } else { 158 its1.it_value.tv_sec = TIMER_SECS; 159 its1.it_value.tv_nsec = 0; 160 } 161 its1.it_interval.tv_sec = interval; 162 its1.it_interval.tv_nsec = 0; 163 164 err = timer_settime(*tm1, flags, &its1, &its2); 165 if (err) { 166 printf("%s - timer_settime() failed\n", clockstring(clock_id)); 167 return -1; 168 } 169 170 return 0; 171 } 172 173 int check_timer_latency(int flags, int interval) 174 { 175 int err = 0; 176 177 describe_timer(flags, interval); 178 printf("timer fired early: %7d : ", timer_fired_early); 179 if (!timer_fired_early) { 180 printf("[OK]\n"); 181 } else { 182 printf("[FAILED]\n"); 183 err = -1; 184 } 185 186 describe_timer(flags, interval); 187 printf("max latency: %10lld ns : ", max_latency_ns); 188 189 if (max_latency_ns < UNRESONABLE_LATENCY) { 190 printf("[OK]\n"); 191 } else { 192 printf("[FAILED]\n"); 193 err = -1; 194 } 195 return err; 196 } 197 198 int check_alarmcount(int flags, int interval) 199 { 200 describe_timer(flags, interval); 201 printf("count: %19d : ", alarmcount); 202 if (alarmcount == 1) { 203 printf("[OK]\n"); 204 return 0; 205 } 206 printf("[FAILED]\n"); 207 return -1; 208 } 209 210 int do_timer(int clock_id, int flags) 211 { 212 timer_t tm1; 213 const int interval = TIMER_SECS; 214 int err; 215 216 err = setup_timer(clock_id, flags, interval, &tm1); 217 /* Unsupported case - return 0 to not fail the test */ 218 if (err) 219 return err == 1 ? 0 : err; 220 221 while (alarmcount < 5) 222 sleep(1); 223 224 timer_delete(tm1); 225 return check_timer_latency(flags, interval); 226 } 227 228 int do_timer_oneshot(int clock_id, int flags) 229 { 230 timer_t tm1; 231 const int interval = 0; 232 struct timeval timeout; 233 int err; 234 235 err = setup_timer(clock_id, flags, interval, &tm1); 236 /* Unsupported case - return 0 to not fail the test */ 237 if (err) 238 return err == 1 ? 0 : err; 239 240 memset(&timeout, 0, sizeof(timeout)); 241 timeout.tv_sec = 5; 242 do { 243 err = select(0, NULL, NULL, NULL, &timeout); 244 } while (err == -1 && errno == EINTR); 245 246 timer_delete(tm1); 247 err = check_timer_latency(flags, interval); 248 err |= check_alarmcount(flags, interval); 249 return err; 250 } 251 252 int main(void) 253 { 254 struct sigaction act; 255 int signum = SIGRTMAX; 256 int ret = 0; 257 258 /* Set up signal handler: */ 259 sigfillset(&act.sa_mask); 260 act.sa_flags = 0; 261 act.sa_handler = sigalarm; 262 sigaction(signum, &act, NULL); 263 264 printf("Setting timers for every %i seconds\n", TIMER_SECS); 265 for (clock_id = 0; clock_id < NR_CLOCKIDS; clock_id++) { 266 267 if ((clock_id == CLOCK_PROCESS_CPUTIME_ID) || 268 (clock_id == CLOCK_THREAD_CPUTIME_ID) || 269 (clock_id == CLOCK_MONOTONIC_RAW) || 270 (clock_id == CLOCK_REALTIME_COARSE) || 271 (clock_id == CLOCK_MONOTONIC_COARSE) || 272 (clock_id == CLOCK_HWSPECIFIC)) 273 continue; 274 275 ret |= do_timer(clock_id, TIMER_ABSTIME); 276 ret |= do_timer(clock_id, 0); 277 ret |= do_timer_oneshot(clock_id, TIMER_ABSTIME); 278 ret |= do_timer_oneshot(clock_id, 0); 279 } 280 if (ret) 281 ksft_exit_fail(); 282 ksft_exit_pass(); 283 } 284
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.