1/******************************************************************************
2 *
3 *   Copyright © International Business Machines  Corp., 2006-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 *       librttest.c
21 *
22 * DESCRIPTION
23 *      A set of commonly used convenience functions for writing
24 *      threaded realtime test cases.
25 *
26 * USAGE:
27 *       To be included in testcases.
28 *
29 * AUTHOR
30 *	Darren Hart <dvhltc@us.ibm.com>
31 *
32 * HISTORY
33 *      2006-Apr-26: Initial version by Darren Hart
34 *      2006-May-08: Added atomic_{inc,set,get}, thread struct, debug function,
35 *		      rt_init, buffered printing -- Vernon Mauery
36 *      2006-May-09: improved command line argument handling
37 *      2007-Jul-12: Added latency tracing functions and I/O helper functions
38 *					      -- Josh triplett
39 *	2008-Jan-10: Added RR thread support to tests -- Chirag Jog
40 *
41 *****************************************************************************/
42
43#include <librttest.h>
44#include <libstats.h>
45
46#include <stdio.h>
47#include <stdlib.h>
48#include <signal.h>
49#include <time.h>
50#include <string.h>
51#include <pthread.h>
52#include <sched.h>
53#include <errno.h>
54#include <unistd.h>
55#include <getopt.h>
56#include <sys/prctl.h>
57#include <sys/stat.h>
58#include <sys/syscall.h>
59#include <sys/types.h>
60#include <sys/mman.h>
61#include <fcntl.h>
62#include <math.h>
63
64static LIST_HEAD(_threads);
65static atomic_t _thread_count = { -1 };
66
67pthread_mutex_t _buffer_mutex;
68char *_print_buffer = NULL;
69int _print_buffer_offset = 0;
70int _dbg_lvl = 0;
71double pass_criteria;
72
73static int _use_pi = 1;
74
75/* function implementations */
76void rt_help(void)
77{
78	printf("librt standard options:\n");
79	printf
80	    ("  -b(0,1)	1:enable buffered output, 0:diable buffered output\n");
81	printf("  -p(0,1)	0:don't use pi mutexes, 1:use pi mutexes\n");
82	printf("  -m		use mlockall\n");
83	printf
84	    ("  -v[0-4]	0:no debug, 1:DBG_ERR, 2:DBG_WARN, 3:DBG_INFO, 4:DBG_DEBUG\n");
85	printf("  -s		Enable saving stats data (default disabled)\n");
86	printf("  -c		Set pass criteria\n");
87}
88
89/* Calibrate the busy work loop */
90void calibrate_busyloop(void)
91{
92	volatile int i = CALIBRATE_LOOPS;
93	nsec_t start, end;
94
95	start = rt_gettime();
96	while (--i > 0) {
97		continue;
98	}
99	end = rt_gettime();
100
101	iters_per_us = (CALIBRATE_LOOPS * NS_PER_US) / (end - start);
102}
103
104int rt_init_long(const char *options, const struct option *longopts,
105		 int (*parse_arg) (int option, char *value), int argc,
106		 char *argv[])
107{
108	const struct option *cur_opt;
109	int use_buffer = 1;
110	char *longopt_vals;
111	size_t i;
112	int c;
113	opterr = 0;
114	int mlock = 0;
115	char *all_options;
116
117	if (asprintf(&all_options, ":b:mp:v:sc:%s", options) == -1) {
118		fprintf(stderr,
119			"Failed to allocate string for option string\n");
120		exit(1);
121	}
122
123	/* Check for duplicate options in optstring */
124	for (i = 0; i < strlen(all_options); i++) {
125		char opt = all_options[i];
126
127		if (opt == ':')
128			continue;
129
130		/* Search ahead */
131		if (strchr(&all_options[i + 1], opt)) {
132			fprintf(stderr,
133				"Programmer error -- argument -%c already used at least twice\n",
134				opt);
135			exit(1);
136		}
137	}
138
139	/* Ensure each long options has a known unique short option in val. */
140	longopt_vals = "";
141	cur_opt = longopts;
142	while (cur_opt && cur_opt->name) {
143		if (cur_opt->flag) {
144			fprintf(stderr, "Programmer error -- argument --%s flag"
145				" is non-null\n", cur_opt->name);
146			exit(1);
147		}
148		if (!strchr(all_options, cur_opt->val)) {
149			fprintf(stderr, "Programmer error -- argument --%s "
150				"shortopt -%c wasn't listed in options (%s)\n",
151				cur_opt->name, cur_opt->val, all_options);
152			exit(1);
153		}
154		if (strchr(longopt_vals, cur_opt->val)) {
155			fprintf(stderr, "Programmer error -- argument --%s "
156				"shortopt -%c is used more than once\n",
157				cur_opt->name, cur_opt->val);
158			exit(1);
159		}
160		if (asprintf(&longopt_vals, "%s%c", longopt_vals, cur_opt->val)
161		    < 0) {
162			perror("asprintf");
163			exit(2);
164		}
165		cur_opt++;
166	}
167
168	while ((c = getopt_long(argc, argv, all_options, longopts, NULL)) != -1) {
169		switch (c) {
170		case 'c':
171			pass_criteria = atof(optarg);
172			break;
173		case 'b':
174			use_buffer = atoi(optarg);
175			break;
176		case 'p':
177			_use_pi = atoi(optarg);
178			break;
179		case 'm':
180			mlock = 1;
181			break;
182		case 'v':
183			_dbg_lvl = atoi(optarg);
184			break;
185		case 's':
186			save_stats = 1;
187			break;
188		case ':':
189			if (optopt == '-')
190				fprintf(stderr, "long option missing arg\n");
191			else
192				fprintf(stderr, "option -%c: missing arg\n",
193					optopt);
194			parse_arg('h', optarg);	/* Just to display usage */
195			exit(1);	/* Just in case. (should normally be done by usage()) */
196		case '?':
197			if (optopt == '-')
198				fprintf(stderr, "unrecognized long option\n");
199			else
200				fprintf(stderr, "option -%c not recognized\n",
201					optopt);
202			parse_arg('h', optarg);	/* Just to display usage */
203			exit(1);	/* Just in case. (should normally be done by usage()) */
204		default:
205			if (parse_arg && parse_arg(c, optarg))
206				break;	/* Application option */
207
208			fprintf(stderr,
209				"Programmer error -- option -%c defined but not handled\n",
210				c);
211			exit(1);
212		}
213	}
214	if (!_use_pi)
215		printf
216		    ("Priority Inheritance has been disabled for this run.\n");
217	if (use_buffer)
218		buffer_init();
219	if (mlock) {
220		if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
221			perror("failed to lock memory\n");
222			exit(1);
223		}
224	}
225
226	calibrate_busyloop();
227
228	/*
229	 * atexit() order matters here - buffer_print() will be called before
230	 * buffer_fini().
231	 */
232	atexit(buffer_fini);
233	atexit(buffer_print);
234	return 0;
235}
236
237int rt_init(const char *options, int (*parse_arg) (int option, char *value),
238	    int argc, char *argv[])
239{
240	return rt_init_long(options, NULL, parse_arg, argc, argv);
241}
242
243void buffer_init(void)
244{
245	_print_buffer = malloc(PRINT_BUFFER_SIZE);
246	if (!_print_buffer)
247		fprintf(stderr,
248			"insufficient memory for print buffer - printing directly to stderr\n");
249	else
250		memset(_print_buffer, 0, PRINT_BUFFER_SIZE);
251}
252
253void buffer_print(void)
254{
255	if (_print_buffer) {
256		fprintf(stderr, "%s", _print_buffer);
257		memset(_print_buffer, 0, PRINT_BUFFER_SIZE);
258		_print_buffer_offset = 0;
259	}
260}
261
262void buffer_fini(void)
263{
264	if (_print_buffer)
265		free(_print_buffer);
266	_print_buffer = NULL;
267}
268
269void cleanup(int i)
270{
271	printf("Test terminated with asynchronous signal\n");
272	buffer_print();
273	buffer_fini();
274	if (i)
275		exit(i);
276}
277
278void setup()
279{
280	signal(SIGINT, cleanup);
281	signal(SIGQUIT, cleanup);
282	signal(SIGTERM, cleanup);
283}
284
285int create_thread(void *(*func) (void *), void *arg, int prio, int policy)
286{
287	struct sched_param param;
288	int id, ret;
289	struct thread *thread;
290
291	id = atomic_inc(&_thread_count);
292
293	thread = malloc(sizeof(struct thread));
294	if (!thread)
295		return -1;
296
297	list_add_tail(&thread->_threads, &_threads);
298	pthread_cond_init(&thread->cond, NULL);	// Accept the defaults
299	init_pi_mutex(&thread->mutex);
300	thread->id = id;
301	thread->priority = prio;
302	thread->policy = policy;
303	thread->flags = 0;
304	thread->arg = arg;
305	thread->func = func;
306	param.sched_priority = prio;
307
308	pthread_attr_init(&thread->attr);
309	pthread_attr_setinheritsched(&thread->attr, PTHREAD_EXPLICIT_SCHED);
310	pthread_attr_setschedpolicy(&thread->attr, thread->policy);
311	pthread_attr_setschedparam(&thread->attr, &param);
312
313	if ((ret =
314	     pthread_create(&thread->pthread, &thread->attr, func,
315			    (void *)thread))) {
316		printf("pthread_create failed: %d (%s)\n", ret, strerror(ret));
317		list_del(&thread->_threads);
318		pthread_attr_destroy(&thread->attr);
319		free(thread);
320		return -1;
321	}
322	pthread_attr_destroy(&thread->attr);
323
324	return id;
325}
326
327int create_fifo_thread(void *(*func) (void *), void *arg, int prio)
328{
329	return create_thread(func, arg, prio, SCHED_FIFO);
330}
331
332int create_rr_thread(void *(*func) (void *), void *arg, int prio)
333{
334	return create_thread(func, arg, prio, SCHED_RR);
335}
336
337int create_other_thread(void *(*func) (void *), void *arg)
338{
339	return create_thread(func, arg, 0, SCHED_OTHER);
340}
341
342int set_thread_priority(pthread_t pthread, int prio)
343{
344	struct sched_param sched_param;
345	sched_param.sched_priority = prio;
346	int policy;
347
348	policy = (prio > 0) ? SCHED_FIFO : SCHED_OTHER;
349
350	return pthread_setschedparam(pthread, policy, &sched_param);
351}
352
353int set_priority(int prio)
354{
355	struct sched_param sp;
356	int ret = 0;
357
358	sp.sched_priority = prio;
359	if (sched_setscheduler(0, SCHED_FIFO, &sp) != 0) {
360		perror("sched_setscheduler");
361		ret = -1;
362	}
363	return ret;
364}
365
366void join_thread(int i)
367{
368	struct thread *p, *t = NULL;
369	list_for_each_entry(p, &_threads, _threads) {
370		if (p->id == i) {
371			t = p;
372			break;
373		}
374	}
375	if (t) {
376		t->flags |= THREAD_QUIT;
377		if (t->pthread)
378			pthread_join(t->pthread, NULL);
379		list_del(&t->_threads);
380	}
381}
382
383void all_threads_quit(void)
384{
385	struct thread *p;
386	list_for_each_entry(p, &_threads, _threads) {
387		p->flags |= THREAD_QUIT;
388	}
389}
390
391void join_threads(void)
392{
393	all_threads_quit();
394	struct thread *p, *t;
395	list_for_each_entry_safe(p, t, &_threads, _threads) {
396		if (p->pthread)
397			pthread_join(p->pthread, NULL);
398		list_del(&p->_threads);
399	}
400}
401
402struct thread *get_thread(int i)
403{
404	struct thread *p;
405	list_for_each_entry(p, &_threads, _threads) {
406		if (p->id == i) {
407			return p;
408		}
409	}
410	return NULL;
411}
412
413void ts_minus(struct timespec *ts_end, struct timespec *ts_start,
414	      struct timespec *ts_delta)
415{
416	if (ts_end == NULL || ts_start == NULL || ts_delta == NULL) {
417		printf("ERROR in %s: one or more of the timespecs is NULL",
418		       __FUNCTION__);
419		return;
420	}
421
422	ts_delta->tv_sec = ts_end->tv_sec - ts_start->tv_sec;
423	ts_delta->tv_nsec = ts_end->tv_nsec - ts_start->tv_nsec;
424	ts_normalize(ts_delta);
425}
426
427void ts_plus(struct timespec *ts_a, struct timespec *ts_b,
428	     struct timespec *ts_sum)
429{
430	if (ts_a == NULL || ts_b == NULL || ts_sum == NULL) {
431		printf("ERROR in %s: one or more of the timespecs is NULL",
432		       __FUNCTION__);
433		return;
434	}
435
436	ts_sum->tv_sec = ts_a->tv_sec + ts_b->tv_sec;
437	ts_sum->tv_nsec = ts_a->tv_nsec + ts_b->tv_nsec;
438	ts_normalize(ts_sum);
439}
440
441void ts_normalize(struct timespec *ts)
442{
443	if (ts == NULL) {
444		/* FIXME: write a real error logging system */
445		printf("ERROR in %s: ts is NULL\n", __FUNCTION__);
446		return;
447	}
448
449	/* get the abs(nsec) < NS_PER_SEC */
450	while (ts->tv_nsec > NS_PER_SEC) {
451		ts->tv_sec++;
452		ts->tv_nsec -= NS_PER_SEC;
453	}
454	while (ts->tv_nsec < -NS_PER_SEC) {
455		ts->tv_sec--;
456		ts->tv_nsec += NS_PER_SEC;
457	}
458
459	/* get the values to the same polarity */
460	if (ts->tv_sec > 0 && ts->tv_nsec < 0) {
461		ts->tv_sec--;
462		ts->tv_nsec += NS_PER_SEC;
463	}
464	if (ts->tv_sec < 0 && ts->tv_nsec > 0) {
465		ts->tv_sec++;
466		ts->tv_nsec -= NS_PER_SEC;
467	}
468}
469
470int ts_to_nsec(struct timespec *ts, nsec_t * ns)
471{
472	struct timespec t;
473	if (ts == NULL) {
474		/* FIXME: write a real error logging system */
475		printf("ERROR in %s: ts is NULL\n", __FUNCTION__);
476		return -1;
477	}
478	t.tv_sec = ts->tv_sec;
479	t.tv_nsec = ts->tv_nsec;
480	ts_normalize(&t);
481
482	if (t.tv_sec <= 0 && t.tv_nsec < 0) {
483		printf("ERROR in %s: ts is negative\n", __FUNCTION__);
484		return -1;
485	}
486
487	*ns = (nsec_t) ts->tv_sec * NS_PER_SEC + ts->tv_nsec;
488	return 0;
489}
490
491void nsec_to_ts(nsec_t ns, struct timespec *ts)
492{
493	if (ts == NULL) {
494		/* FIXME: write a real error logging system */
495		printf("ERROR in %s: ts is NULL\n", __FUNCTION__);
496		return;
497	}
498	ts->tv_sec = ns / NS_PER_SEC;
499	ts->tv_nsec = ns % NS_PER_SEC;
500}
501
502/* return difference in microseconds */
503unsigned long long tsc_minus(unsigned long long tsc_start,
504			     unsigned long long tsc_end)
505{
506	unsigned long long delta;
507	if (tsc_start <= tsc_end)
508		delta = tsc_end - tsc_start;
509	else {
510		delta = ULL_MAX - (tsc_end - tsc_start) + 1;
511		printf("TSC wrapped, delta=%llu\n", delta);
512	}
513	return delta;
514}
515
516void rt_nanosleep_until(nsec_t ns)
517{
518	struct timespec ts_sleep, ts_rem;
519	int rc;
520	nsec_to_ts(ns, &ts_sleep);
521	rc = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts_sleep,
522			     &ts_rem);
523	/* FIXME: when should we display the remainder ? */
524	if (rc != 0) {
525		printf("WARNING: rt_nanosleep() returned early by %d s %d ns\n",
526		       (int)ts_rem.tv_sec, (int)ts_rem.tv_nsec);
527	}
528}
529
530void rt_nanosleep(nsec_t ns)
531{
532	struct timespec ts_sleep, ts_rem;
533	int rc;
534	nsec_to_ts(ns, &ts_sleep);
535	rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts_sleep, &ts_rem);
536	/* FIXME: when should we display the remainder ? */
537	if (rc != 0) {
538		printf("WARNING: rt_nanosleep() returned early by %d s %d ns\n",
539		       (int)ts_rem.tv_sec, (int)ts_rem.tv_nsec);
540	}
541}
542
543nsec_t rt_gettime(void)
544{
545	struct timespec ts;
546	nsec_t ns;
547	int rc;
548
549	rc = clock_gettime(CLOCK_MONOTONIC, &ts);
550	if (rc != 0) {
551		printf("ERROR in %s: clock_gettime() returned %d\n",
552		       __FUNCTION__, rc);
553		perror("clock_gettime() failed");
554		return 0;
555	}
556
557	ts_to_nsec(&ts, &ns);
558	return ns;
559}
560
561void *busy_work_ms(int ms)
562{
563	busy_work_us(ms * US_PER_MS);
564	return NULL;
565}
566
567void *busy_work_us(int us)
568{
569	volatile int i;
570	nsec_t start, now;
571	int delta;		/* time in us */
572
573	i = us * iters_per_us;
574
575	start = rt_gettime();
576	while (--i > 0) {
577		continue;
578	}
579	now = rt_gettime();
580
581	delta = (now - start) / NS_PER_US;
582	/* uncomment to tune to your machine */
583	/* printf("busy_work_us requested: %dus  actual: %dus\n", us, delta); */
584	return NULL;
585}
586
587void init_pi_mutex(pthread_mutex_t * m)
588{
589#if HAS_PRIORITY_INHERIT
590	pthread_mutexattr_t attr;
591	int ret;
592	int protocol;
593
594	if ((ret = pthread_mutexattr_init(&attr)) != 0) {
595		printf("Failed to init mutexattr: %d (%s)\n", ret,
596		       strerror(ret));
597	};
598	if (_use_pi
599	    && (ret =
600		pthread_mutexattr_setprotocol(&attr,
601					      PTHREAD_PRIO_INHERIT)) != 0) {
602		printf("Can't set protocol prio inherit: %d (%s)\n", ret,
603		       strerror(ret));
604	}
605	if ((ret = pthread_mutexattr_getprotocol(&attr, &protocol)) != 0) {
606		printf("Can't get mutexattr protocol: %d (%s)\n", ret,
607		       strerror(ret));
608	}
609	if ((ret = pthread_mutex_init(m, &attr)) != 0) {
610		printf("Failed to init mutex: %d (%s)\n", ret, strerror(ret));
611	}
612#endif
613
614	/* FIXME: does any of this need to be destroyed ? */
615}
616
617/* Write the entirety of data.  Complain if unable to do so. */
618static void write_or_complain(int fd, const void *data, size_t len)
619{
620	const char *remaining = data;
621
622	while (len > 0) {
623		ssize_t ret = write(fd, remaining, len);
624		if (ret <= 0) {
625			if (errno != EAGAIN && errno != EINTR) {
626				perror("write");
627				return;
628			}
629		} else {
630			remaining += ret;
631			len -= ret;
632		}
633	}
634}
635
636/* Write the given data to the existing file specified by pathname.  Complain
637 * if unable to do so. */
638static void write_file(const char *pathname, const void *data, size_t len)
639{
640	int fd = open(pathname, O_WRONLY);
641	if (fd < 0) {
642		printf("Failed to open file \"%s\": %d (%s)\n",
643		       pathname, errno, strerror(errno));
644		return;
645	}
646
647	write_or_complain(fd, data, len);
648
649	if (close(fd) < 0) {
650		printf("Failed to close file \"%s\": %d (%s)\n",
651		       pathname, errno, strerror(errno));
652	}
653}
654
655/* Write the given '\0'-terminated string to the existing file specified by
656 * pathname.  Complain if unable to do so. */
657static void write_string_to_file(const char *pathname, const char *string)
658{
659	write_file(pathname, string, strlen(string));
660}
661
662static void read_and_print(const char *pathname, int output_fd)
663{
664	char data[4096];
665	int fd = open(pathname, O_RDONLY);
666	if (fd < 0) {
667		printf("Failed to open file \"%s\": %d (%s)\n",
668		       pathname, errno, strerror(errno));
669		return;
670	}
671
672	while (1) {
673		ssize_t ret = read(fd, data, sizeof(data));
674		if (ret < 0) {
675			if (errno != EAGAIN && errno != EINTR) {
676				printf
677				    ("Failed to read from file \"%s\": %d (%s)\n",
678				     pathname, errno, strerror(errno));
679				break;
680			}
681		} else if (ret == 0)
682			break;
683		else
684			write_or_complain(output_fd, data, ret);
685	}
686
687	if (close(fd) < 0) {
688		printf("Failed to close file \"%s\": %d (%s)\n",
689		       pathname, errno, strerror(errno));
690	}
691}
692
693void latency_trace_enable(void)
694{
695	printf("Enabling latency tracer.\n");
696	write_string_to_file("/proc/sys/kernel/trace_use_raw_cycles", "1");
697	write_string_to_file("/proc/sys/kernel/trace_all_cpus", "1");
698	write_string_to_file("/proc/sys/kernel/trace_enabled", "1");
699	write_string_to_file("/proc/sys/kernel/trace_freerunning", "1");
700	write_string_to_file("/proc/sys/kernel/trace_print_on_crash", "0");
701	write_string_to_file("/proc/sys/kernel/trace_user_triggered", "1");
702	write_string_to_file("/proc/sys/kernel/trace_user_trigger_irq", "-1");
703	write_string_to_file("/proc/sys/kernel/trace_verbose", "0");
704	write_string_to_file("/proc/sys/kernel/preempt_thresh", "0");
705	write_string_to_file("/proc/sys/kernel/wakeup_timing", "0");
706	write_string_to_file("/proc/sys/kernel/mcount_enabled", "1");
707	write_string_to_file("/proc/sys/kernel/preempt_max_latency", "0");
708}
709
710#ifndef PR_SET_TRACING
711#define PR_SET_TRACING 0
712#endif
713
714void latency_trace_start(void)
715{
716	if (prctl(PR_SET_TRACING, 1) < 0)
717		perror("Failed to start tracing");
718}
719
720void latency_trace_stop(void)
721{
722	if (prctl(PR_SET_TRACING, 0) < 0)
723		perror("Failed to stop tracing");
724}
725
726void latency_trace_print(void)
727{
728	read_and_print("/proc/latency_trace", STDOUT_FILENO);
729}
730