• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }
80 
parse_args(int c,char * v)81 int parse_args(int c, char *v)
82 {
83 
84 	int handled = 1;
85 	switch (c) {
86 	case 'h':
87 		usage();
88 		exit(0);
89 	case 't':
90 		busy_time = atoi(v);
91 		break;
92 	case 'n':
93 		busy_threads = atoi(v);
94 		break;
95 	case 'f':
96 		med_prio = MIN(atoi(v), 98);
97 		break;
98 	case 'i':
99 		iterations = atoi(v);
100 		if (iterations < 100) {
101 			fprintf(stderr,
102 				"Number of iterations cannot be less than 100.\n");
103 			exit(1);
104 		}
105 		break;
106 	default:
107 		handled = 0;
108 		break;
109 	}
110 	return handled;
111 }
112 
busy_thread(void * thread)113 void *busy_thread(void *thread)
114 {
115 	atomic_inc(&busy_threads_started);
116 	while (1) {
117 		busy_work_ms(busy_time);
118 		sched_yield();
119 	}
120 	return NULL;
121 }
122 
timer_thread(void * thread)123 void *timer_thread(void *thread)
124 {
125 	int i;
126 	nsec_t start, end;
127 	unsigned long delta_us;
128 	while (atomic_get(&busy_threads_started) < busy_threads) {
129 		rt_nanosleep(10000);
130 	}
131 	printf("All Busy Threads started, commencing test\n");	// FIXME: use debug infrastructure
132 	max_delta = 0;
133 	for (i = 0; i < iterations; i++) {
134 		start = rt_gettime();
135 		rt_nanosleep(DEF_SLEEP_TIME);
136 		end = rt_gettime();
137 		delta_us =
138 		    ((unsigned long)(end - start) - DEF_SLEEP_TIME) / NS_PER_US;
139 		rec.x = i;
140 		rec.y = delta_us;
141 		stats_container_append(&dat, rec);
142 		max_delta = MAX(max_delta, delta_us);
143 		min_delta = (i == 0) ? delta_us : MIN(min_delta, delta_us);
144 	}
145 	return NULL;
146 }
147 
main(int argc,char * argv[])148 int main(int argc, char *argv[])
149 {
150 	int ret = 1;
151 	int b;
152 	float avg_delta;
153 	int t_id;
154 	setup();
155 	busy_threads = 2 * sysconf(_SC_NPROCESSORS_ONLN);	// default busy_threads
156 	pass_criteria = DEF_CRITERIA;
157 	rt_init("f:i:jhn:t:", parse_args, argc, argv);
158 	high_prio = med_prio + 1;
159 
160 	// Set main()'s prio to one above the timer_thread so it is sure to not
161 	// be starved
162 	if (set_priority(high_prio + 1) < 0) {
163 		printf("Failed to set main()'s priority to %d\n",
164 		       high_prio + 1);
165 		exit(1);
166 	}
167 
168 	printf("\n-------------------------------------------\n");
169 	printf("High Resolution Timer Priority (Starvation)\n");
170 	printf("-------------------------------------------\n\n");
171 	printf("Running %d iterations\n", iterations);
172 	printf("Running with %d busy threads\n", busy_threads);
173 	printf("Busy thread work time: %d\n", busy_time);
174 	printf("Busy thread priority: %d\n", med_prio);
175 	printf("Timer thread priority: %d\n", high_prio);
176 
177 	stats_container_t hist;
178 	stats_quantiles_t quantiles;
179 	if (stats_container_init(&dat, iterations)) {
180 		printf("Cannot init stat containers for dat\n");
181 		exit(1);
182 	}
183 	if (stats_container_init(&hist, HIST_BUCKETS)) {
184 		printf("Cannot init stat containers for hist\n");
185 		exit(1);
186 	}
187 	if (stats_quantiles_init(&quantiles, (int)log10(iterations))) {
188 		printf("Cannot init stat quantiles\n");
189 		exit(1);
190 	}
191 
192 	t_id = create_fifo_thread(timer_thread, NULL, high_prio);
193 	if (t_id == -1) {
194 		printf("Failed to create timer thread\n");
195 		exit(1);
196 	}
197 	for (b = 0; b < busy_threads; b++) {
198 		if (create_fifo_thread(busy_thread, NULL, med_prio) < 0) {
199 			printf("Failed to create a busy thread\n");
200 			exit(1);
201 		}
202 	}
203 	join_thread(t_id);
204 
205 	avg_delta = stats_avg(&dat);
206 	stats_hist(&hist, &dat);
207 	stats_container_save("samples",
208 			     "High Resolution Timer Latency Scatter Plot",
209 			     "Iteration", "Latency (us)", &dat, "points");
210 	stats_container_save("hist", "High Resolution Timer Latency Histogram",
211 			     "Latency (us)", "Samples", &hist, "steps");
212 
213 	if (max_delta <= pass_criteria)
214 		ret = 0;
215 
216 	printf("Minimum: %ld us\n", min_delta);
217 	printf("Maximum: %ld us\n", max_delta);
218 	printf("Average: %f us\n", avg_delta);
219 	printf("Standard Deviation: %f\n", stats_stddev(&dat));
220 	printf("Quantiles:\n");
221 	stats_quantiles_calc(&dat, &quantiles);
222 	stats_quantiles_print(&quantiles);
223 	printf("\nCriteria: Maximum wakeup latency < %lu us\n",
224 	       (unsigned long)pass_criteria);
225 	printf("Result: %s\n", ret ? "FAIL" : "PASS");
226 
227 	return ret;
228 }
229