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@us.ibm.com>
35 *
36 * HISTORY
37 * 2007-Aug-08: Initial version by Darren Hart <dvhltc@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
usage(void)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
parse_args(int c,char * v)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
busy_thread(void * thread)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
timer_thread(void * thread)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
main(int argc,char * argv[])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