1 /****************************************************************************** 2 * 3 * Copyright International Business Machines Corp., 2007, 2008 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 * NAME 20 * hrtimer-prio.c 21 * 22 * DESCRIPTION 23 * Test the latency of hrtimers under rt load. 24 * The busy_threads should run at a priority higher than the system 25 * softirq_hrtimer, but lower than the timer_thread. The timer_thread 26 * measure the time it takes to return from a nanosleep call. If the 27 * lower priority threads can increase the latency of the higher 28 * priority thread, it is considered a failure. 29 * 30 * USAGE: 31 * Use run_auto.sh script in current directory to build and run test. 32 * 33 * AUTHOR 34 * Darren Hart <dvhltc (at) us.ibm.com> 35 * 36 * HISTORY 37 * 2007-Aug-08: Initial version by Darren Hart <dvhltc (at) us.ibm.com> 38 * 39 * This line has to be added to avoid a stupid CVS problem 40 *****************************************************************************/ 41 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <math.h> 45 #include <librttest.h> 46 #include <libstats.h> 47 48 #define DEF_MED_PRIO 60 // (softirqd-hrtimer,98) 49 #define DEF_ITERATIONS 10000 50 #define HIST_BUCKETS 100 51 #define DEF_BUSY_TIME 10 // Duration of busy work in milliseconds 52 #define DEF_SLEEP_TIME 10000 // Duration of nanosleep in nanoseconds 53 #define DEF_CRITERIA 10 // maximum timer latency in microseconds 54 55 static int med_prio = DEF_MED_PRIO; 56 static int high_prio; 57 static int busy_time = DEF_BUSY_TIME; 58 static int iterations = DEF_ITERATIONS; 59 static int busy_threads; 60 61 static stats_container_t dat; 62 static stats_record_t rec; 63 static atomic_t busy_threads_started; 64 static unsigned long min_delta; 65 static unsigned long max_delta; 66 67 void usage(void) 68 { 69 rt_help(); 70 printf("hrtimer-prio specific options:\n"); 71 printf(" -t# #:busy work time in ms, defaults to %d ms\n", 72 DEF_BUSY_TIME); 73 printf(" -i# #:number of iterations, defaults to %d\n", 74 DEF_ITERATIONS); 75 printf(" -n# #:number of busy threads, defaults to NR_CPUS*2\n"); 76 printf 77 (" -f# #:rt fifo priority of busy threads (1,98), defaults to %d\n", 78 DEF_MED_PRIO); 79 printf 80 (" -m# #:maximum timer latency in microseconds, defaults to %d\n", 81 DEF_CRITERIA); 82 } 83 84 int parse_args(int c, char *v) 85 { 86 87 int handled = 1; 88 switch (c) { 89 case 'h': 90 usage(); 91 exit(0); 92 case 't': 93 busy_time = atoi(v); 94 break; 95 case 'n': 96 busy_threads = atoi(v); 97 break; 98 case 'f': 99 med_prio = MIN(atoi(v), 98); 100 break; 101 case 'i': 102 iterations = atoi(v); 103 if (iterations < 100) { 104 fprintf(stderr, 105 "Number of iterations cannot be less than 100.\n"); 106 exit(1); 107 } 108 break; 109 default: 110 handled = 0; 111 break; 112 } 113 return handled; 114 } 115 116 void *busy_thread(void *thread) 117 { 118 atomic_inc(&busy_threads_started); 119 while (1) { 120 busy_work_ms(busy_time); 121 sched_yield(); 122 } 123 return NULL; 124 } 125 126 void *timer_thread(void *thread) 127 { 128 int i; 129 nsec_t start, end; 130 unsigned long delta_us; 131 while (atomic_get(&busy_threads_started) < busy_threads) { 132 rt_nanosleep(10000); 133 } 134 printf("All Busy Threads started, commencing test\n"); // FIXME: use debug infrastructure 135 max_delta = 0; 136 for (i = 0; i < iterations; i++) { 137 start = rt_gettime(); 138 rt_nanosleep(DEF_SLEEP_TIME); 139 end = rt_gettime(); 140 delta_us = 141 ((unsigned long)(end - start) - DEF_SLEEP_TIME) / NS_PER_US; 142 rec.x = i; 143 rec.y = delta_us; 144 stats_container_append(&dat, rec); 145 max_delta = MAX(max_delta, delta_us); 146 min_delta = (i == 0) ? delta_us : MIN(min_delta, delta_us); 147 } 148 return NULL; 149 } 150 151 int main(int argc, char *argv[]) 152 { 153 int ret = 1; 154 int b; 155 float avg_delta; 156 int t_id; 157 setup(); 158 busy_threads = 2 * sysconf(_SC_NPROCESSORS_ONLN); // default busy_threads 159 pass_criteria = DEF_CRITERIA; 160 rt_init("f:i:jhn:t:", parse_args, argc, argv); 161 high_prio = med_prio + 1; 162 163 // Set main()'s prio to one above the timer_thread so it is sure to not 164 // be starved 165 if (set_priority(high_prio + 1) < 0) { 166 printf("Failed to set main()'s priority to %d\n", 167 high_prio + 1); 168 exit(1); 169 } 170 171 printf("\n-------------------------------------------\n"); 172 printf("High Resolution Timer Priority (Starvation)\n"); 173 printf("-------------------------------------------\n\n"); 174 printf("Running %d iterations\n", iterations); 175 printf("Running with %d busy threads\n", busy_threads); 176 printf("Busy thread work time: %d\n", busy_time); 177 printf("Busy thread priority: %d\n", med_prio); 178 printf("Timer thread priority: %d\n", high_prio); 179 180 stats_container_t hist; 181 stats_quantiles_t quantiles; 182 if (stats_container_init(&dat, iterations)) { 183 printf("Cannot init stat containers for dat\n"); 184 exit(1); 185 } 186 if (stats_container_init(&hist, HIST_BUCKETS)) { 187 printf("Cannot init stat containers for hist\n"); 188 exit(1); 189 } 190 if (stats_quantiles_init(&quantiles, (int)log10(iterations))) { 191 printf("Cannot init stat quantiles\n"); 192 exit(1); 193 } 194 195 t_id = create_fifo_thread(timer_thread, NULL, high_prio); 196 if (t_id == -1) { 197 printf("Failed to create timer thread\n"); 198 exit(1); 199 } 200 for (b = 0; b < busy_threads; b++) { 201 if (create_fifo_thread(busy_thread, NULL, med_prio) < 0) { 202 printf("Failed to create a busy thread\n"); 203 exit(1); 204 } 205 } 206 join_thread(t_id); 207 208 avg_delta = stats_avg(&dat); 209 stats_hist(&hist, &dat); 210 stats_container_save("samples", 211 "High Resolution Timer Latency Scatter Plot", 212 "Iteration", "Latency (us)", &dat, "points"); 213 stats_container_save("hist", "High Resolution Timer Latency Histogram", 214 "Latency (us)", "Samples", &hist, "steps"); 215 216 if (max_delta <= pass_criteria) 217 ret = 0; 218 219 printf("Minimum: %ld us\n", min_delta); 220 printf("Maximum: %ld us\n", max_delta); 221 printf("Average: %f us\n", avg_delta); 222 printf("Standard Deviation: %f\n", stats_stddev(&dat)); 223 printf("Quantiles:\n"); 224 stats_quantiles_calc(&dat, &quantiles); 225 stats_quantiles_print(&quantiles); 226 printf("\nCriteria: Maximum wakeup latency < %lu us\n", 227 (unsigned long)pass_criteria); 228 printf("Result: %s\n", ret ? "FAIL" : "PASS"); 229 230 return ret; 231 } 232