• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /******************************************************************************
3  *
4  * Copyright (C) 2007-2009 Steven Rostedt <srostedt@redhat.com>
5  *
6  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; version 2 of the License (not later!)
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * NAME
22  *      rt-migrate-test.c
23  *
24  * DESCRIPTION
25  *	This test makes sure that all the high prio tasks that are in the
26  *	running state are actually running on a CPU if it can.
27  ** Steps:
28  *	- Creates N+1 threads with lower real time priorities.
29  *	  Where N is the number of CPUs in the system.
30  *	- If the thread is high priority, and if a CPU is available, the
31  *	  thread runs on that CPU.
32  *	- The thread records the start time and the number of ticks in the run
33  *	  interval.
34  *	- The output indicates if lower prio task is quicker than higher
35  *	  priority task.
36  *
37  * USAGE:
38  *	Use run_auto.sh in the current directory to build and run the test.
39  *
40  * AUTHOR
41  *      Steven Rostedt <srostedt@redhat.com>
42  *
43  * HISTORY
44  *      30 July, 2009: Initial version by Steven Rostedt
45  *      11 Aug, 2009: Converted the coding style to the one used by the realtime
46  *		    testcases by Kiran Prakash
47  *
48  */
49 #ifndef _GNU_SOURCE
50 #define _GNU_SOURCE
51 #endif
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <getopt.h>
56 #include <stdarg.h>
57 #include <unistd.h>
58 #include <ctype.h>
59 #include <time.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <fcntl.h>
63 #include <signal.h>
64 #include <sys/time.h>
65 #include <linux/unistd.h>
66 #include <sys/syscall.h>
67 #include <errno.h>
68 #include <sched.h>
69 #include <pthread.h>
70 #include <librttest.h>
71 #include <libstats.h>
72 
73 #define gettid() syscall(__NR_gettid)
74 
75 #define VERSION_STRING "V 0.4LTP"
76 
77 int nr_tasks;
78 int lfd;
79 
80 int numcpus;
81 static int mark_fd = -1;
82 static __thread char buff[BUFSIZ + 1];
83 
setup_ftrace_marker(void)84 static void setup_ftrace_marker(void)
85 {
86 	struct stat st;
87 	char *files[] = {
88 		"/sys/kernel/debug/tracing/trace_marker",
89 		"/debug/tracing/trace_marker",
90 		"/debugfs/tracing/trace_marker",
91 	};
92 	int ret;
93 	int i;
94 
95 	for (i = 0; i < (sizeof(files) / sizeof(char *)); i++) {
96 		ret = stat(files[i], &st);
97 		if (ret >= 0)
98 			goto found;
99 	}
100 	/* todo, check mounts system */
101 	return;
102 found:
103 	mark_fd = open(files[i], O_WRONLY);
104 }
105 
ftrace_write(const char * fmt,...)106 static void ftrace_write(const char *fmt, ...)
107 {
108 	va_list ap;
109 	int n;
110 
111 	if (mark_fd < 0)
112 		return;
113 
114 	va_start(ap, fmt);
115 	n = vsnprintf(buff, BUFSIZ, fmt, ap);
116 	va_end(ap);
117 
118 	/*
119 	 * This doesn't return any valid vs invalid exit codes, so printing out
120 	 * a perror to warn the end-user of an issue is sufficient.
121 	 */
122 	if (write(mark_fd, buff, n) < 0) {
123 		perror("write");
124 	}
125 }
126 
127 #define INTERVAL 100ULL * NS_PER_MS
128 #define RUN_INTERVAL 20ULL * NS_PER_MS
129 #define NR_RUNS 50
130 #define PRIO_START 2
131 /* 1 millisec off */
132 #define MAX_ERR  1000 * NS_PER_US
133 
134 #define PROGRESS_CHARS 70
135 
136 static unsigned long long interval = INTERVAL;
137 static unsigned long long run_interval = RUN_INTERVAL;
138 static unsigned long long max_err = MAX_ERR;
139 static int nr_runs = NR_RUNS;
140 static int prio_start = PRIO_START;
141 static int check = 1;
142 static int stop;
143 
144 static unsigned long long now;
145 
146 static int done;
147 static int loop;
148 
149 static pthread_barrier_t start_barrier;
150 static pthread_barrier_t end_barrier;
151 stats_container_t *intervals;
152 stats_container_t *intervals_length;
153 stats_container_t *intervals_loops;
154 static long *thread_pids;
155 
print_progress_bar(int percent)156 static void print_progress_bar(int percent)
157 {
158 	int i;
159 	int p;
160 
161 	if (percent > 100)
162 		percent = 100;
163 
164 	/* Use stderr, so we don't capture it */
165 	putc('\r', stderr);
166 	putc('|', stderr);
167 	for (i = 0; i < PROGRESS_CHARS; i++)
168 		putc(' ', stderr);
169 	putc('|', stderr);
170 	putc('\r', stderr);
171 	putc('|', stderr);
172 
173 	p = PROGRESS_CHARS * percent / 100;
174 
175 	for (i = 0; i < p; i++)
176 		putc('-', stderr);
177 
178 	fflush(stderr);
179 }
180 
usage()181 static void usage()
182 {
183 	rt_help();
184 	printf("Usage:\n"
185 	       "-a priority Priority of the threads"
186 	       "-r time     Run time (ms) to busy loop the threads (20)\n"
187 	       "-t time     Sleep time (ms) between intervals (100)\n"
188 	       "-e time     Max allowed error (microsecs)\n"
189 	       "-l loops    Number of iterations to run (50)\n");
190 }
191 
192 /*
193 int rt_init(const char *options, int (*parse_arg)(int option, char *value),
194 	    int argc, char *argv[]);
195  */
parse_args(int c,char * v)196 static int parse_args(int c, char *v)
197 {
198 	int handled = 1;
199 	switch (c) {
200 	case 'a':
201 		prio_start = atoi(v);
202 		break;
203 	case 'r':
204 		run_interval = atoi(v);
205 		break;
206 	case 't':
207 		interval = atoi(v);
208 		break;
209 	case 'l':
210 		nr_runs = atoi(v);
211 		break;
212 	case 'e':
213 		max_err = atoi(v) * NS_PER_US;
214 		break;
215 	case '?':
216 	case 'h':
217 		usage();
218 		handled = 0;
219 	}
220 	return handled;
221 }
222 
record_time(int id,unsigned long long time,unsigned long l)223 static void record_time(int id, unsigned long long time, unsigned long l)
224 {
225 	unsigned long long ltime;
226 	stats_record_t rec;
227 	if (loop >= nr_runs)
228 		return;
229 	time -= now;
230 	ltime = rt_gettime() / NS_PER_US;
231 	ltime -= now;
232 	rec.x = loop;
233 	rec.y = time;
234 	stats_container_append(&intervals[id], rec);
235 	rec.x = loop;
236 	rec.y = ltime;
237 	stats_container_append(&intervals_length[id], rec);
238 	rec.x = loop;
239 	rec.y = l;
240 	stats_container_append(&intervals_loops[id], rec);
241 }
242 
print_results(void)243 static void print_results(void)
244 {
245 	int i;
246 	int t;
247 	unsigned long long tasks_max[nr_tasks];
248 	unsigned long long tasks_min[nr_tasks];
249 	unsigned long long tasks_avg[nr_tasks];
250 
251 	memset(tasks_max, 0, sizeof(tasks_max[0]) * nr_tasks);
252 	memset(tasks_min, 0xff, sizeof(tasks_min[0]) * nr_tasks);
253 	memset(tasks_avg, 0, sizeof(tasks_avg[0]) * nr_tasks);
254 
255 	printf("Iter: ");
256 	for (t = 0; t < nr_tasks; t++)
257 		printf("%6d  ", t);
258 	printf("\n");
259 
260 	for (t = 0; t < nr_tasks; t++) {
261 		tasks_max[t] = stats_max(&intervals[t]);
262 		tasks_min[t] = stats_min(&intervals[t]);
263 		tasks_avg[t] = stats_avg(&intervals[t]);
264 	}
265 	for (i = 0; i < nr_runs; i++) {
266 		printf("%4d:   ", i);
267 		for (t = 0; t < nr_tasks; t++)
268 			printf("%6ld  ", intervals[t].records[i].y);
269 
270 		printf("\n");
271 		printf(" len:   ");
272 		for (t = 0; t < nr_tasks; t++)
273 			printf("%6ld  ", intervals_length[t].records[i].y);
274 
275 		printf("\n");
276 		printf(" loops: ");
277 		for (t = 0; t < nr_tasks; t++)
278 			printf("%6ld  ", intervals_loops[t].records[i].y);
279 
280 		printf("\n");
281 		printf("\n");
282 	}
283 
284 	printf("Parent pid: %d\n", getpid());
285 
286 	for (t = 0; t < nr_tasks; t++) {
287 		printf(" Task %d (prio %d) (pid %ld):\n", t, t + prio_start,
288 		       thread_pids[t]);
289 		printf("   Max: %lld us\n", tasks_max[t]);
290 		printf("   Min: %lld us\n", tasks_min[t]);
291 		printf("   Tot: %lld us\n", tasks_avg[t] * nr_runs);
292 		printf("   Avg: %lld us\n", tasks_avg[t]);
293 		printf("\n");
294 	}
295 
296 	printf(" Result: %s\n", (check < 0) ? "FAIL" : "PASS");
297 }
298 
busy_loop(unsigned long long start_time)299 static unsigned long busy_loop(unsigned long long start_time)
300 {
301 	unsigned long long time;
302 	unsigned long l = 0;
303 
304 	do {
305 		l++;
306 		time = rt_gettime();
307 	} while ((time - start_time) < RUN_INTERVAL);
308 
309 	return l;
310 }
311 
start_task(void * data)312 void *start_task(void *data)
313 {
314 	struct thread *thr = (struct thread *)data;
315 	long id = (long)thr->arg;
316 	thread_pids[id] = gettid();
317 	unsigned long long start_time;
318 	int ret;
319 	int high = 0;
320 	cpu_set_t cpumask;
321 	cpu_set_t save_cpumask;
322 	int cpu = 0;
323 	unsigned long l;
324 	long pid;
325 
326 	ret = sched_getaffinity(0, sizeof(save_cpumask), &save_cpumask);
327 	if (ret < 0)
328 		debug(DBG_ERR, "sched_getaffinity failed: %s\n", strerror(ret));
329 
330 	pid = gettid();
331 
332 	/* Check if we are the highest prio task */
333 	if (id == nr_tasks - 1)
334 		high = 1;
335 
336 	while (!done) {
337 		if (high) {
338 			/* rotate around the CPUS */
339 			if (!CPU_ISSET(cpu, &save_cpumask))
340 				cpu = 0;
341 			CPU_ZERO(&cpumask);
342 			CPU_SET(cpu, &cpumask);
343 			cpu++;
344 			sched_setaffinity(0, sizeof(cpumask), &cpumask);
345 		}
346 		pthread_barrier_wait(&start_barrier);
347 		start_time = rt_gettime();
348 		ftrace_write("Thread %d: started %lld diff %lld\n",
349 			     pid, start_time, start_time - now);
350 		l = busy_loop(start_time);
351 		record_time(id, start_time / NS_PER_US, l);
352 		pthread_barrier_wait(&end_barrier);
353 	}
354 
355 	return (void *)pid;
356 }
357 
check_times(int l)358 static int check_times(int l)
359 {
360 	int i;
361 	unsigned long long last;
362 	unsigned long long last_loops;
363 	unsigned long long last_length;
364 
365 	for (i = 0; i < nr_tasks; i++) {
366 		if (i && last < intervals[i].records[l].y &&
367 		    ((intervals[i].records[l].y - last) > max_err)) {
368 			/*
369 			 * May be a false positive.
370 			 * Make sure that we did more loops
371 			 * our start is before the end
372 			 * and the end should be tested.
373 			 */
374 			if (intervals_loops[i].records[l].y < last_loops ||
375 			    intervals[i].records[l].y > last_length ||
376 			    (intervals_length[i].records[l].y > last_length &&
377 			     intervals_length[i].records[l].y - last_length >
378 			     max_err)) {
379 				check = -1;
380 				return 1;
381 			}
382 		}
383 		last = intervals[i].records[l].y;
384 		last_loops = intervals_loops[i].records[l].y;
385 		last_length = intervals_length[i].records[l].y;
386 	}
387 	return 0;
388 }
389 
stop_log(int sig)390 static void stop_log(int sig)
391 {
392 	stop = 1;
393 }
394 
main(int argc,char ** argv)395 int main(int argc, char **argv)
396 {
397 	pthread_t *threads;
398 	long i;
399 	int ret;
400 	struct timespec intv;
401 	struct sched_param param;
402 
403 	rt_init("a:r:t:e:l:h:", parse_args, argc, argv);
404 	signal(SIGINT, stop_log);
405 
406 	if (argc >= (optind + 1))
407 		nr_tasks = atoi(argv[optind]);
408 	else {
409 		numcpus = sysconf(_SC_NPROCESSORS_ONLN);
410 		nr_tasks = numcpus + 1;
411 	}
412 
413 	intervals = malloc(sizeof(stats_container_t) * nr_tasks);
414 	if (!intervals)
415 		debug(DBG_ERR, "malloc failed\n");
416 	memset(intervals, 0, sizeof(stats_container_t) * nr_tasks);
417 
418 	intervals_length = malloc(sizeof(stats_container_t) * nr_tasks);
419 	if (!intervals_length)
420 		debug(DBG_ERR, "malloc failed\n");
421 	memset(intervals_length, 0, sizeof(stats_container_t) * nr_tasks);
422 
423 	if (!intervals_loops)
424 		debug(DBG_ERR, "malloc failed\n");
425 	intervals_loops = malloc(sizeof(stats_container_t) * nr_tasks);
426 	memset(intervals_loops, 0, sizeof(stats_container_t) * nr_tasks);
427 
428 	threads = malloc(sizeof(*threads) * nr_tasks);
429 	if (!threads)
430 		debug(DBG_ERR, "malloc failed\n");
431 	memset(threads, 0, sizeof(*threads) * nr_tasks);
432 
433 	ret = pthread_barrier_init(&start_barrier, NULL, nr_tasks + 1);
434 	ret = pthread_barrier_init(&end_barrier, NULL, nr_tasks + 1);
435 	if (ret < 0)
436 		debug(DBG_ERR, "pthread_barrier_init failed: %s\n",
437 		      strerror(ret));
438 
439 	for (i = 0; i < nr_tasks; i++) {
440 		stats_container_init(&intervals[i], nr_runs);
441 		stats_container_init(&intervals_length[i], nr_runs);
442 		stats_container_init(&intervals_loops[i], nr_runs);
443 	}
444 
445 	thread_pids = malloc(sizeof(long) * nr_tasks);
446 	if (!thread_pids)
447 		debug(DBG_ERR, "malloc thread_pids failed\n");
448 
449 	for (i = 0; i < nr_tasks; i++) {
450 		threads[i] = create_fifo_thread(start_task, (void *)i,
451 						prio_start + i);
452 	}
453 
454 	/*
455 	 * Progress bar uses stderr to let users see it when
456 	 * redirecting output. So we convert stderr to use line
457 	 * buffering so the progress bar doesn't flicker.
458 	 */
459 	setlinebuf(stderr);
460 
461 	/* up our prio above all tasks */
462 	memset(&param, 0, sizeof(param));
463 	param.sched_priority = nr_tasks + prio_start;
464 	if (sched_setscheduler(0, SCHED_FIFO, &param))
465 		debug(DBG_WARN, "Warning, can't set priority of"
466 		      "main thread !\n");
467 	intv.tv_sec = INTERVAL / NS_PER_SEC;
468 	intv.tv_nsec = INTERVAL % (1 * NS_PER_SEC);
469 
470 	print_progress_bar(0);
471 
472 	setup_ftrace_marker();
473 
474 	for (loop = 0; loop < nr_runs; loop++) {
475 		unsigned long long end;
476 
477 		now = rt_gettime() / NS_PER_US;
478 
479 		ftrace_write("Loop %d now=%lld\n", loop, now);
480 
481 		pthread_barrier_wait(&start_barrier);
482 
483 		ftrace_write("All running!!!\n");
484 
485 		rt_nanosleep(intv.tv_nsec);
486 		print_progress_bar((loop * 100) / nr_runs);
487 
488 		end = rt_gettime() / NS_PER_US;
489 		ftrace_write("Loop %d end now=%lld diff=%lld\n",
490 			     loop, end, end - now);
491 		ret = pthread_barrier_wait(&end_barrier);
492 
493 		if (stop || (check && check_times(loop))) {
494 			loop++;
495 			nr_runs = loop;
496 			break;
497 		}
498 	}
499 	putc('\n', stderr);
500 
501 	pthread_barrier_wait(&start_barrier);
502 	done = 1;
503 	pthread_barrier_wait(&end_barrier);
504 
505 	join_threads();
506 	print_results();
507 
508 	if (stop) {
509 		/*
510 		 * We use this test in bash while loops
511 		 * So if we hit Ctrl-C then let the while
512 		 * loop know to break.
513 		 */
514 		if (check < 0)
515 			exit(-1);
516 		else
517 			exit(1);
518 	}
519 
520 	if (check < 0)
521 		exit(-1);
522 	else
523 		exit(0);
524 }
525