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 *      gtod_infinite.c
21 *
22 * DESCRIPTION
23 *       This 'test' is designed to run forever.  It must manually be killed,
24 *       so it is not ideally suited tobe part of a validation suite of tests.
25 *       This test was initially designed to look for 'delays' between two
26 *       calls to clock_gettime(), and helped locate SMI induced delays on
27 *       several hardware platforms.
28 *
29 *       As mentioned above, this test is designed to be run on a system for
30 *       an unspecified period of time.  It would not be unusual to let this
31 *       test run for several days.
32 *
33 *       During startup, the test will print out the 'maximum' delay between
34 *       clock_gettime() calls.  It starts with a predefined maximum of
35 *       START_MAX(300ns) to eliminate noise during startup.  In addition,
36 *       it will print out every delay that exceeds REPORT_MIN (1000000ns).
37 *
38 * USAGE:
39 *      Use run_auto.sh script in current directory to build and run test.
40 *
41 * AUTHOR
42 *      Darren Hart <dvhltc@us.ibm.com>
43 *
44 * HISTORY
45 *      2006-Aug-17: Initial version by Darren Hart <dvhltc@us.ibm.com>
46 *
47 *****************************************************************************/
48
49#include <stdio.h>
50#include <stdlib.h>
51#include <time.h>
52#include <sched.h>
53#include <librttest.h>
54#include <sys/mman.h>
55#include <unistd.h>
56#include <signal.h>
57
58#define CLOCK_TO_USE CLOCK_MONOTONIC
59
60#define START_MAX	3000
61#define REPORT_MIN	1000000
62
63static unsigned int max_window = 0;	/* infinite, don't use a window */
64static unsigned int test_duration = 0;	/* infinite duration */
65static int test_stop = 0;	/* 1 to stop */
66
67void usage(void)
68{
69	rt_help();
70	printf("gtod_infinite specific options:\n");
71	printf
72	    ("  -wWINDOW      iterations in max value window (default inf)\n");
73	printf("  -tDURATION    test duration in finite hours (default inf)\n");
74}
75
76int parse_args(int c, char *v)
77{
78	int handled = 1;
79	switch (c) {
80	case 'h':
81		usage();
82		exit(0);
83	case 'w':
84		max_window = atoi(v);
85		break;
86	case 't':
87		test_duration = atoi(v);
88		break;
89	default:
90		handled = 0;
91		break;
92	}
93	return handled;
94}
95
96void alarm_handler(int sig)
97{
98	/* Stop test execution */
99	test_stop = 1;
100}
101
102int main(int argc, char *argv[])
103{
104	int /* i, */ rc;
105	struct timespec ts, p_ts;
106	nsec_t s_time, e_time, diff_time;
107	nsec_t max_time = START_MAX;
108//      cpu_set_t mask;
109	struct sched_param param;
110	time_t tt;
111	unsigned int wi;
112	struct sigaction sact;
113	setup();
114
115	/* Set signal handler for SIGALRM */
116	sigfillset(&sact.sa_mask);
117	sact.sa_handler = alarm_handler;
118	rc = sigaction(SIGALRM, &sact, NULL);
119	if (rc) {
120		perror("sigaction");
121		exit(1);
122	}
123/*
124	CPU_ZERO(&mask);
125	CPU_SET(0, &mask);
126	rc = sched_setaffinity(0, sizeof(mask), &mask);
127	if (rc) {
128		perror("sched_setaffinity");
129		exit(1);
130	}
131*/
132	rt_init("hw:t:", parse_args, argc, argv);
133
134	mlockall(MCL_CURRENT | MCL_FUTURE);
135
136	if (max_window > 0) {
137		printf("%d iterations in max calculation window\n", max_window);
138	}
139
140	param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 80;
141	rc = sched_setscheduler(0, SCHED_FIFO, &param);
142	if (rc) {
143		perror("sched_setscheduler");
144		exit(1);
145	}
146
147	rc = clock_gettime(CLOCK_TO_USE, &p_ts);
148	if (rc) {
149		perror("clock_gettime");
150		exit(1);
151	}
152
153	/* Set alarm for test duration, if specified */
154	if (test_duration > 0) {
155		rc = alarm(test_duration * 60 * 60);
156		if (rc) {
157			perror("alarm");
158			exit(1);
159		}
160	}
161
162	wi = 0;
163	while (test_stop != 1) {
164		rc = clock_gettime(CLOCK_TO_USE, &p_ts);
165		rc = clock_gettime(CLOCK_TO_USE, &ts);
166		if (rc) {
167			perror("clock_gettime");
168			exit(1);
169		}
170
171		ts_to_nsec(&p_ts, &s_time);
172		ts_to_nsec(&ts, &e_time);
173
174		diff_time = e_time - s_time;
175
176		if (max_window > 0 ||
177		    ((diff_time > max_time) || (diff_time > REPORT_MIN))) {
178			if (diff_time > max_time)
179				max_time = diff_time;
180
181			if (max_window == 0 || ++wi == max_window) {
182				tt = (time_t) ts.tv_sec;
183				printf("Task delayed for %lld nsec!!! %s",
184				       max_time, ctime(&tt));
185				fflush(stdout);
186
187				if (wi == max_window) {
188					max_time = 0;
189					wi = 0;
190				}
191			}
192
193		}
194	}
195
196	return 0;
197}
198