xref: /aosp_15_r20/external/ltp/testcases/realtime/func/rt-migrate/rt-migrate.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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(&param, 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, &param))
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