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 <stdio.h> 24 #include <unistd.h> 25 #include <time.h> 26 #include <string.h> 27 #include <signal.h> 28 #include <stdlib.h> 29 #include <pthread.h> 30 #ifdef KTEST 31 #include "../kselftest.h" 32 #else 33 static inline int ksft_exit_pass(void) 34 { 35 exit(0); 36 } 37 static inline int ksft_exit_fail(void) 38 { 39 exit(1); 40 } 41 #endif 42 43 #define CLOCK_REALTIME 0 44 #define CLOCK_MONOTONIC 1 45 #define CLOCK_PROCESS_CPUTIME_ID 2 46 #define CLOCK_THREAD_CPUTIME_ID 3 47 #define CLOCK_MONOTONIC_RAW 4 48 #define CLOCK_REALTIME_COARSE 5 49 #define CLOCK_MONOTONIC_COARSE 6 50 #define CLOCK_BOOTTIME 7 51 #define CLOCK_REALTIME_ALARM 8 52 #define CLOCK_BOOTTIME_ALARM 9 53 #define CLOCK_HWSPECIFIC 10 54 #define CLOCK_TAI 11 55 #define NR_CLOCKIDS 12 56 57 58 #define NSEC_PER_SEC 1000000000ULL 59 #define UNRESONABLE_LATENCY 40000000 /* 40ms in nanosecs */ 60 61 #define TIMER_SECS 1 62 int alarmcount; 63 int clock_id; 64 struct timespec start_time; 65 long long max_latency_ns; 66 67 char *clockstring(int clockid) 68 { 69 switch (clockid) { 70 case CLOCK_REALTIME: 71 return "CLOCK_REALTIME"; 72 case CLOCK_MONOTONIC: 73 return "CLOCK_MONOTONIC"; 74 case CLOCK_PROCESS_CPUTIME_ID: 75 return "CLOCK_PROCESS_CPUTIME_ID"; 76 case CLOCK_THREAD_CPUTIME_ID: 77 return "CLOCK_THREAD_CPUTIME_ID"; 78 case CLOCK_MONOTONIC_RAW: 79 return "CLOCK_MONOTONIC_RAW"; 80 case CLOCK_REALTIME_COARSE: 81 return "CLOCK_REALTIME_COARSE"; 82 case CLOCK_MONOTONIC_COARSE: 83 return "CLOCK_MONOTONIC_COARSE"; 84 case CLOCK_BOOTTIME: 85 return "CLOCK_BOOTTIME"; 86 case CLOCK_REALTIME_ALARM: 87 return "CLOCK_REALTIME_ALARM"; 88 case CLOCK_BOOTTIME_ALARM: 89 return "CLOCK_BOOTTIME_ALARM"; 90 case CLOCK_TAI: 91 return "CLOCK_TAI"; 92 }; 93 return "UNKNOWN_CLOCKID"; 94 } 95 96 97 long long timespec_sub(struct timespec a, struct timespec b) 98 { 99 long long ret = NSEC_PER_SEC * b.tv_sec + b.tv_nsec; 100 101 ret -= NSEC_PER_SEC * a.tv_sec + a.tv_nsec; 102 return ret; 103 } 104 105 106 void sigalarm(int signo) 107 { 108 long long delta_ns; 109 struct timespec ts; 110 111 clock_gettime(clock_id, &ts); 112 alarmcount++; 113 114 delta_ns = timespec_sub(start_time, ts); 115 delta_ns -= NSEC_PER_SEC * TIMER_SECS * alarmcount; 116 117 if (delta_ns < 0) 118 printf("%s timer fired early: FAIL\n", clockstring(clock_id)); 119 120 if (delta_ns > max_latency_ns) 121 max_latency_ns = delta_ns; 122 } 123 124 int do_timer(int clock_id, int flags) 125 { 126 struct sigevent se; 127 timer_t tm1; 128 struct itimerspec its1, its2; 129 int err; 130 131 /* Set up timer: */ 132 memset(&se, 0, sizeof(se)); 133 se.sigev_notify = SIGEV_SIGNAL; 134 se.sigev_signo = SIGRTMAX; 135 se.sigev_value.sival_int = 0; 136 137 max_latency_ns = 0; 138 alarmcount = 0; 139 140 err = timer_create(clock_id, &se, &tm1); 141 if (err) { 142 if ((clock_id == CLOCK_REALTIME_ALARM) || 143 (clock_id == CLOCK_BOOTTIME_ALARM)) { 144 printf("%-22s %s missing CAP_WAKE_ALARM? : [UNSUPPORTED]\n", 145 clockstring(clock_id), 146 flags ? "ABSTIME":"RELTIME"); 147 return 0; 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 = TIMER_SECS; 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 while (alarmcount < 5) 171 sleep(1); 172 173 printf("%-22s %s max latency: %10lld ns : ", 174 clockstring(clock_id), 175 flags ? "ABSTIME":"RELTIME", 176 max_latency_ns); 177 178 timer_delete(tm1); 179 if (max_latency_ns < UNRESONABLE_LATENCY) { 180 printf("[OK]\n"); 181 return 0; 182 } 183 printf("[FAILED]\n"); 184 return -1; 185 } 186 187 int main(void) 188 { 189 struct sigaction act; 190 int signum = SIGRTMAX; 191 int ret = 0; 192 193 /* Set up signal handler: */ 194 sigfillset(&act.sa_mask); 195 act.sa_flags = 0; 196 act.sa_handler = sigalarm; 197 sigaction(signum, &act, NULL); 198 199 printf("Setting timers for every %i seconds\n", TIMER_SECS); 200 for (clock_id = 0; clock_id < NR_CLOCKIDS; clock_id++) { 201 202 if ((clock_id == CLOCK_PROCESS_CPUTIME_ID) || 203 (clock_id == CLOCK_THREAD_CPUTIME_ID) || 204 (clock_id == CLOCK_MONOTONIC_RAW) || 205 (clock_id == CLOCK_REALTIME_COARSE) || 206 (clock_id == CLOCK_MONOTONIC_COARSE) || 207 (clock_id == CLOCK_HWSPECIFIC)) 208 continue; 209 210 ret |= do_timer(clock_id, TIMER_ABSTIME); 211 ret |= do_timer(clock_id, 0); 212 } 213 if (ret) 214 return ksft_exit_fail(); 215 return ksft_exit_pass(); 216 } 217