1 /* Measure nanosleep timer latency 2 * by: john stultz (john.stultz@linaro.org) 3 * (C) Copyright Linaro 2013 4 * Licensed under the GPLv2 5 * 6 * To build: 7 * $ gcc nsleep-lat.c -o nsleep-lat -lrt 8 * 9 * This program is free software: you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation, either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 */ 19 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <time.h> 23 #include <sys/time.h> 24 #include <sys/timex.h> 25 #include <string.h> 26 #include <signal.h> 27 #include <include/vdso/time64.h> 28 #include "../kselftest.h" 29 30 #define UNRESONABLE_LATENCY 40000000 /* 40ms in nanosecs */ 31 32 /* CLOCK_HWSPECIFIC == CLOCK_SGI_CYCLE (Deprecated) */ 33 #define CLOCK_HWSPECIFIC 10 34 35 #define UNSUPPORTED 0xf00f 36 37 char *clockstring(int clockid) 38 { 39 switch (clockid) { 40 case CLOCK_REALTIME: 41 return "CLOCK_REALTIME"; 42 case CLOCK_MONOTONIC: 43 return "CLOCK_MONOTONIC"; 44 case CLOCK_PROCESS_CPUTIME_ID: 45 return "CLOCK_PROCESS_CPUTIME_ID"; 46 case CLOCK_THREAD_CPUTIME_ID: 47 return "CLOCK_THREAD_CPUTIME_ID"; 48 case CLOCK_MONOTONIC_RAW: 49 return "CLOCK_MONOTONIC_RAW"; 50 case CLOCK_REALTIME_COARSE: 51 return "CLOCK_REALTIME_COARSE"; 52 case CLOCK_MONOTONIC_COARSE: 53 return "CLOCK_MONOTONIC_COARSE"; 54 case CLOCK_BOOTTIME: 55 return "CLOCK_BOOTTIME"; 56 case CLOCK_REALTIME_ALARM: 57 return "CLOCK_REALTIME_ALARM"; 58 case CLOCK_BOOTTIME_ALARM: 59 return "CLOCK_BOOTTIME_ALARM"; 60 case CLOCK_TAI: 61 return "CLOCK_TAI"; 62 }; 63 return "UNKNOWN_CLOCKID"; 64 } 65 66 struct timespec timespec_add(struct timespec ts, unsigned long long ns) 67 { 68 ts.tv_nsec += ns; 69 while (ts.tv_nsec >= NSEC_PER_SEC) { 70 ts.tv_nsec -= NSEC_PER_SEC; 71 ts.tv_sec++; 72 } 73 return ts; 74 } 75 76 77 long long timespec_sub(struct timespec a, struct timespec b) 78 { 79 long long ret = NSEC_PER_SEC * b.tv_sec + b.tv_nsec; 80 81 ret -= NSEC_PER_SEC * a.tv_sec + a.tv_nsec; 82 return ret; 83 } 84 85 int nanosleep_lat_test(int clockid, long long ns) 86 { 87 struct timespec start, end, target; 88 long long latency = 0; 89 int i, count; 90 91 target.tv_sec = ns/NSEC_PER_SEC; 92 target.tv_nsec = ns%NSEC_PER_SEC; 93 94 if (clock_gettime(clockid, &start)) 95 return UNSUPPORTED; 96 if (clock_nanosleep(clockid, 0, &target, NULL)) 97 return UNSUPPORTED; 98 99 count = 10; 100 101 /* First check relative latency */ 102 clock_gettime(clockid, &start); 103 for (i = 0; i < count; i++) 104 clock_nanosleep(clockid, 0, &target, NULL); 105 clock_gettime(clockid, &end); 106 107 if (((timespec_sub(start, end)/count)-ns) > UNRESONABLE_LATENCY) { 108 ksft_print_msg("Large rel latency: %lld ns :", (timespec_sub(start, end)/count)-ns); 109 return -1; 110 } 111 112 /* Next check absolute latency */ 113 for (i = 0; i < count; i++) { 114 clock_gettime(clockid, &start); 115 target = timespec_add(start, ns); 116 clock_nanosleep(clockid, TIMER_ABSTIME, &target, NULL); 117 clock_gettime(clockid, &end); 118 latency += timespec_sub(target, end); 119 } 120 121 if (latency/count > UNRESONABLE_LATENCY) { 122 ksft_print_msg("Large abs latency: %lld ns :", latency/count); 123 return -1; 124 } 125 126 return 0; 127 } 128 129 #define SKIPPED_CLOCK_COUNT 3 130 131 int main(int argc, char **argv) 132 { 133 long long length; 134 int clockid, ret; 135 int max_clocks = CLOCK_TAI + 1; 136 137 ksft_print_header(); 138 ksft_set_plan(max_clocks - CLOCK_REALTIME - SKIPPED_CLOCK_COUNT); 139 140 for (clockid = CLOCK_REALTIME; clockid < max_clocks; clockid++) { 141 142 /* Skip cputime clockids since nanosleep won't increment cputime */ 143 if (clockid == CLOCK_PROCESS_CPUTIME_ID || 144 clockid == CLOCK_THREAD_CPUTIME_ID || 145 clockid == CLOCK_HWSPECIFIC) 146 continue; 147 148 length = 10; 149 while (length <= (NSEC_PER_SEC * 10)) { 150 ret = nanosleep_lat_test(clockid, length); 151 if (ret) 152 break; 153 length *= 100; 154 155 } 156 157 if (ret == UNSUPPORTED) { 158 ksft_test_result_skip("%s\n", clockstring(clockid)); 159 } else { 160 ksft_test_result(ret >= 0, "%s\n", 161 clockstring(clockid)); 162 } 163 } 164 165 ksft_finished(); 166 } 167