1
2 /******************************************************************************
3 *
4 * Copyright (C) 2007-2009 Steven Rostedt <[email protected]>
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 <[email protected]>
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 #define CLAMP(x, lower, upper) (MIN(upper, MAX(x, lower)))
78 #define CLAMP_PRIO(prio) CLAMP(prio, prio_min, prio_max)
79
80 int nr_tasks;
81 int lfd;
82
83 int numcpus;
84 static int mark_fd = -1;
85 static __thread char buff[BUFSIZ + 1];
86
setup_ftrace_marker(void)87 static void setup_ftrace_marker(void)
88 {
89 struct stat st;
90 char *files[] = {
91 "/sys/kernel/debug/tracing/trace_marker",
92 "/debug/tracing/trace_marker",
93 "/debugfs/tracing/trace_marker",
94 };
95 int ret;
96 int i;
97
98 for (i = 0; i < (sizeof(files) / sizeof(char *)); i++) {
99 ret = stat(files[i], &st);
100 if (ret >= 0)
101 goto found;
102 }
103 /* todo, check mounts system */
104 return;
105 found:
106 mark_fd = open(files[i], O_WRONLY);
107 }
108
ftrace_write(const char * fmt,...)109 static void ftrace_write(const char *fmt, ...)
110 {
111 va_list ap;
112 int n;
113
114 if (mark_fd < 0)
115 return;
116
117 va_start(ap, fmt);
118 n = vsnprintf(buff, BUFSIZ, fmt, ap);
119 va_end(ap);
120
121 /*
122 * This doesn't return any valid vs invalid exit codes, so printing out
123 * a perror to warn the end-user of an issue is sufficient.
124 */
125 if (write(mark_fd, buff, n) < 0) {
126 perror("write");
127 }
128 }
129
130 #define INTERVAL 100ULL * NS_PER_MS
131 #define RUN_INTERVAL 20ULL * NS_PER_MS
132 #define NR_RUNS 50
133 #define PRIO_START 2
134 /* 1 millisec off */
135 #define MAX_ERR 1000 * NS_PER_US
136
137 #define PROGRESS_CHARS 70
138
139 static unsigned long long interval = INTERVAL;
140 static unsigned long long run_interval = RUN_INTERVAL;
141 static unsigned long long max_err = MAX_ERR;
142 static int nr_runs = NR_RUNS;
143 static int prio_start = PRIO_START, prio_min, prio_max;
144 static int check = 1;
145 static int stop;
146
147 static unsigned long long now;
148
149 static int done;
150 static int loop;
151
152 static pthread_barrier_t start_barrier;
153 static pthread_barrier_t end_barrier;
154 stats_container_t *intervals;
155 stats_container_t *intervals_length;
156 stats_container_t *intervals_loops;
157 static long *thread_pids;
158
print_progress_bar(int percent)159 static void print_progress_bar(int percent)
160 {
161 int i;
162 int p;
163
164 if (percent > 100)
165 percent = 100;
166
167 /* Use stderr, so we don't capture it */
168 putc('\r', stderr);
169 putc('|', stderr);
170 for (i = 0; i < PROGRESS_CHARS; i++)
171 putc(' ', stderr);
172 putc('|', stderr);
173 putc('\r', stderr);
174 putc('|', stderr);
175
176 p = PROGRESS_CHARS * percent / 100;
177
178 for (i = 0; i < p; i++)
179 putc('-', stderr);
180
181 fflush(stderr);
182 }
183
usage()184 static void usage()
185 {
186 rt_help();
187 printf("Usage:\n"
188 "-a priority Priority of the threads"
189 "-r time Run time (ms) to busy loop the threads (20)\n"
190 "-t time Sleep time (ms) between intervals (100)\n"
191 "-e time Max allowed error (microsecs)\n"
192 "-l loops Number of iterations to run (50)\n");
193 }
194
195 /*
196 int rt_init(const char *options, int (*parse_arg)(int option, char *value),
197 int argc, char *argv[]);
198 */
parse_args(int c,char * v)199 static int parse_args(int c, char *v)
200 {
201 int handled = 1;
202 switch (c) {
203 case 'a':
204 prio_start = atoi(v);
205 break;
206 case 'r':
207 run_interval = atoi(v);
208 break;
209 case 't':
210 interval = atoi(v);
211 break;
212 case 'l':
213 nr_runs = atoi(v);
214 break;
215 case 'e':
216 max_err = atoi(v) * NS_PER_US;
217 break;
218 case '?':
219 case 'h':
220 usage();
221 handled = 0;
222 }
223 return handled;
224 }
225
record_time(int id,unsigned long long time,unsigned long l)226 static void record_time(int id, unsigned long long time, unsigned long l)
227 {
228 unsigned long long ltime;
229 stats_record_t rec;
230 if (loop >= nr_runs)
231 return;
232 time -= now;
233 ltime = rt_gettime() / NS_PER_US;
234 ltime -= now;
235 rec.x = loop;
236 rec.y = time;
237 stats_container_append(&intervals[id], rec);
238 rec.x = loop;
239 rec.y = ltime;
240 stats_container_append(&intervals_length[id], rec);
241 rec.x = loop;
242 rec.y = l;
243 stats_container_append(&intervals_loops[id], rec);
244 }
245
print_results(void)246 static void print_results(void)
247 {
248 int i;
249 int t;
250 unsigned long long tasks_max[nr_tasks];
251 unsigned long long tasks_min[nr_tasks];
252 unsigned long long tasks_avg[nr_tasks];
253
254 memset(tasks_max, 0, sizeof(tasks_max[0]) * nr_tasks);
255 memset(tasks_min, 0xff, sizeof(tasks_min[0]) * nr_tasks);
256 memset(tasks_avg, 0, sizeof(tasks_avg[0]) * nr_tasks);
257
258 printf("Iter: ");
259 for (t = 0; t < nr_tasks; t++)
260 printf("%6d ", t);
261 printf("\n");
262
263 for (t = 0; t < nr_tasks; t++) {
264 tasks_max[t] = stats_max(&intervals[t]);
265 tasks_min[t] = stats_min(&intervals[t]);
266 tasks_avg[t] = stats_avg(&intervals[t]);
267 }
268 for (i = 0; i < nr_runs; i++) {
269 printf("%4d: ", i);
270 for (t = 0; t < nr_tasks; t++)
271 printf("%6ld ", intervals[t].records[i].y);
272
273 printf("\n");
274 printf(" len: ");
275 for (t = 0; t < nr_tasks; t++)
276 printf("%6ld ", intervals_length[t].records[i].y);
277
278 printf("\n");
279 printf(" loops: ");
280 for (t = 0; t < nr_tasks; t++)
281 printf("%6ld ", intervals_loops[t].records[i].y);
282
283 printf("\n");
284 printf("\n");
285 }
286
287 printf("Parent pid: %d\n", getpid());
288
289 for (t = 0; t < nr_tasks; t++) {
290 printf(" Task %d (prio %d) (pid %ld):\n", t,
291 CLAMP_PRIO(t + prio_start), thread_pids[t]);
292 printf(" Max: %lld us\n", tasks_max[t]);
293 printf(" Min: %lld us\n", tasks_min[t]);
294 printf(" Tot: %lld us\n", tasks_avg[t] * nr_runs);
295 printf(" Avg: %lld us\n", tasks_avg[t]);
296 printf("\n");
297 }
298
299 printf(" Result: %s\n", (check < 0) ? "FAIL" : "PASS");
300 }
301
busy_loop(unsigned long long start_time)302 static unsigned long busy_loop(unsigned long long start_time)
303 {
304 unsigned long long time;
305 unsigned long l = 0;
306
307 do {
308 l++;
309 time = rt_gettime();
310 } while ((time - start_time) < RUN_INTERVAL);
311
312 return l;
313 }
314
start_task(void * data)315 void *start_task(void *data)
316 {
317 struct thread *thr = (struct thread *)data;
318 long id = (long)thr->arg;
319 thread_pids[id] = gettid();
320 unsigned long long start_time;
321 int ret;
322 int high = 0;
323 cpu_set_t cpumask;
324 cpu_set_t save_cpumask;
325 int cpu = 0;
326 unsigned long l;
327 long pid;
328
329 ret = sched_getaffinity(0, sizeof(save_cpumask), &save_cpumask);
330 if (ret < 0)
331 debug(DBG_ERR, "sched_getaffinity failed: %s\n", strerror(ret));
332
333 pid = gettid();
334
335 /* Check if we are the highest prio task */
336 if (id == nr_tasks - 1)
337 high = 1;
338
339 while (!done) {
340 if (high) {
341 /* rotate around the CPUS */
342 if (!CPU_ISSET(cpu, &save_cpumask))
343 cpu = 0;
344 CPU_ZERO(&cpumask);
345 CPU_SET(cpu, &cpumask);
346 cpu++;
347 sched_setaffinity(0, sizeof(cpumask), &cpumask);
348 }
349 pthread_barrier_wait(&start_barrier);
350 start_time = rt_gettime();
351 ftrace_write("Thread %d: started %lld diff %lld\n",
352 pid, start_time, start_time - now);
353 l = busy_loop(start_time);
354 record_time(id, start_time / NS_PER_US, l);
355 pthread_barrier_wait(&end_barrier);
356 }
357
358 return (void *)pid;
359 }
360
check_times(int l)361 static int check_times(int l)
362 {
363 int i;
364 unsigned long long last;
365 unsigned long long last_loops;
366 unsigned long long last_length;
367
368 for (i = 0; i < nr_tasks; i++) {
369 if (i && last < intervals[i].records[l].y &&
370 ((intervals[i].records[l].y - last) > max_err)) {
371 /*
372 * May be a false positive.
373 * Make sure that we did more loops
374 * our start is before the end
375 * and the end should be tested.
376 */
377 if (intervals_loops[i].records[l].y < last_loops ||
378 intervals[i].records[l].y > last_length ||
379 (intervals_length[i].records[l].y > last_length &&
380 intervals_length[i].records[l].y - last_length >
381 max_err)) {
382 check = -1;
383 return 1;
384 }
385 }
386 last = intervals[i].records[l].y;
387 last_loops = intervals_loops[i].records[l].y;
388 last_length = intervals_length[i].records[l].y;
389 }
390 return 0;
391 }
392
stop_log(int sig)393 static void stop_log(int sig)
394 {
395 stop = 1;
396 }
397
main(int argc,char ** argv)398 int main(int argc, char **argv)
399 {
400 /*
401 * Determine the valid priority range; subtracting one from the
402 * maximum to reserve the highest prio for main thread.
403 */
404 prio_min = sched_get_priority_min(SCHED_FIFO);
405 prio_max = sched_get_priority_max(SCHED_FIFO) - 1;
406
407 int *threads;
408 long i;
409 int ret;
410 struct timespec intv;
411 struct sched_param param;
412
413 rt_init("a:r:t:e:l:h:", parse_args, argc, argv);
414 signal(SIGINT, stop_log);
415
416 if (argc >= (optind + 1))
417 nr_tasks = atoi(argv[optind]);
418 else {
419 numcpus = sysconf(_SC_NPROCESSORS_ONLN);
420 nr_tasks = numcpus + 1;
421 }
422 if (nr_tasks < 0) {
423 printf("The number of tasks must not be negative.\n");
424 exit(EXIT_FAILURE);
425 }
426
427 intervals = malloc(sizeof(stats_container_t) * nr_tasks);
428 if (!intervals)
429 debug(DBG_ERR, "malloc failed\n");
430 memset(intervals, 0, sizeof(stats_container_t) * nr_tasks);
431
432 intervals_length = malloc(sizeof(stats_container_t) * nr_tasks);
433 if (!intervals_length)
434 debug(DBG_ERR, "malloc failed\n");
435 memset(intervals_length, 0, sizeof(stats_container_t) * nr_tasks);
436
437 if (!intervals_loops)
438 debug(DBG_ERR, "malloc failed\n");
439 intervals_loops = malloc(sizeof(stats_container_t) * nr_tasks);
440 memset(intervals_loops, 0, sizeof(stats_container_t) * nr_tasks);
441
442 threads = malloc(sizeof(*threads) * nr_tasks);
443 if (!threads)
444 debug(DBG_ERR, "malloc failed\n");
445 memset(threads, 0, sizeof(*threads) * nr_tasks);
446
447 ret = pthread_barrier_init(&start_barrier, NULL, nr_tasks + 1);
448 ret = pthread_barrier_init(&end_barrier, NULL, nr_tasks + 1);
449 if (ret < 0)
450 debug(DBG_ERR, "pthread_barrier_init failed: %s\n",
451 strerror(ret));
452
453 for (i = 0; i < nr_tasks; i++) {
454 stats_container_init(&intervals[i], nr_runs);
455 stats_container_init(&intervals_length[i], nr_runs);
456 stats_container_init(&intervals_loops[i], nr_runs);
457 }
458
459 thread_pids = malloc(sizeof(long) * nr_tasks);
460 if (!thread_pids)
461 debug(DBG_ERR, "malloc thread_pids failed\n");
462
463 for (i = 0; i < nr_tasks; i++) {
464 threads[i] = create_fifo_thread(start_task, (void *)i,
465 CLAMP_PRIO(prio_start + i));
466 }
467
468 /*
469 * Progress bar uses stderr to let users see it when
470 * redirecting output. So we convert stderr to use line
471 * buffering so the progress bar doesn't flicker.
472 */
473 setlinebuf(stderr);
474
475 /* up our prio above all tasks */
476 memset(¶m, 0, sizeof(param));
477 param.sched_priority = CLAMP(nr_tasks + prio_start, prio_min,
478 prio_max + 1);
479 if (sched_setscheduler(0, SCHED_FIFO, ¶m))
480 debug(DBG_WARN, "Warning, can't set priority of main thread!\n");
481 intv.tv_sec = INTERVAL / NS_PER_SEC;
482 intv.tv_nsec = INTERVAL % (1 * NS_PER_SEC);
483
484 print_progress_bar(0);
485
486 setup_ftrace_marker();
487
488 for (loop = 0; loop < nr_runs; loop++) {
489 unsigned long long end;
490
491 now = rt_gettime() / NS_PER_US;
492
493 ftrace_write("Loop %d now=%lld\n", loop, now);
494
495 pthread_barrier_wait(&start_barrier);
496
497 ftrace_write("All running!!!\n");
498
499 rt_nanosleep(intv.tv_nsec);
500 print_progress_bar((loop * 100) / nr_runs);
501
502 end = rt_gettime() / NS_PER_US;
503 ftrace_write("Loop %d end now=%lld diff=%lld\n",
504 loop, end, end - now);
505 ret = pthread_barrier_wait(&end_barrier);
506
507 if (stop || (check && check_times(loop))) {
508 loop++;
509 nr_runs = loop;
510 break;
511 }
512 }
513 putc('\n', stderr);
514
515 pthread_barrier_wait(&start_barrier);
516 done = 1;
517 pthread_barrier_wait(&end_barrier);
518
519 join_threads();
520 print_results();
521
522 if (stop) {
523 /*
524 * We use this test in bash while loops
525 * So if we hit Ctrl-C then let the while
526 * loop know to break.
527 */
528 if (check < 0)
529 exit(-1);
530 else
531 exit(1);
532 }
533
534 if (check < 0)
535 exit(-1);
536 else
537 exit(0);
538 }
539