1f08c3bdfSopenharmony_ci/******************************************************************************
2f08c3bdfSopenharmony_ci *
3f08c3bdfSopenharmony_ci *   Copyright © International Business Machines  Corp., 2006-2008
4f08c3bdfSopenharmony_ci *
5f08c3bdfSopenharmony_ci *   This program is free software;  you can redistribute it and/or modify
6f08c3bdfSopenharmony_ci *   it under the terms of the GNU General Public License as published by
7f08c3bdfSopenharmony_ci *   the Free Software Foundation; either version 2 of the License, or
8f08c3bdfSopenharmony_ci *   (at your option) any later version.
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci *   This program is distributed in the hope that it will be useful,
11f08c3bdfSopenharmony_ci *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12f08c3bdfSopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13f08c3bdfSopenharmony_ci *   the GNU General Public License for more details.
14f08c3bdfSopenharmony_ci *
15f08c3bdfSopenharmony_ci *   You should have received a copy of the GNU General Public License
16f08c3bdfSopenharmony_ci *   along with this program;  if not, write to the Free Software
17f08c3bdfSopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18f08c3bdfSopenharmony_ci *
19f08c3bdfSopenharmony_ci * NAME
20f08c3bdfSopenharmony_ci *      sched_latency.c
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci * DESCRIPTION
23f08c3bdfSopenharmony_ci *	Measure the latency involved with periodic scheduling.
24f08c3bdfSopenharmony_ci *   Steps:
25f08c3bdfSopenharmony_ci *    - A thread is created at a priority of 89.
26f08c3bdfSopenharmony_ci *    -	It periodically sleeps for a specified duration(period).
27f08c3bdfSopenharmony_ci *    - The delay is measured as
28f08c3bdfSopenharmony_ci *
29f08c3bdfSopenharmony_ci *      delay = (now - start - i*period) converted to microseconds
30f08c3bdfSopenharmony_ci *
31f08c3bdfSopenharmony_ci *	where,
32f08c3bdfSopenharmony_ci *	now = CLOCK_MONOTONIC gettime in ns, start = CLOCK_MONOTONIC gettime
33f08c3bdfSopenharmony_ci *	at the start of the test, i = iteration number, period = period chosen
34f08c3bdfSopenharmony_ci *
35f08c3bdfSopenharmony_ci * USAGE:
36f08c3bdfSopenharmony_ci *      Use run_auto.sh script in current directory to build and run test.
37f08c3bdfSopenharmony_ci *
38f08c3bdfSopenharmony_ci * AUTHOR
39f08c3bdfSopenharmony_ci *      Darren Hart <dvhltc@us.ibm.com>
40f08c3bdfSopenharmony_ci *
41f08c3bdfSopenharmony_ci * HISTORY
42f08c3bdfSopenharmony_ci *      2006-May-10: Initial version by Darren Hart <dvhltc@us.ibm.com>
43f08c3bdfSopenharmony_ci *      2007-Jul-11: Quantiles added by Josh Triplett <josh@kernel.org>
44f08c3bdfSopenharmony_ci *      2007-Jul-12: Latency tracing added by Josh Triplett <josh@kernel.org>
45f08c3bdfSopenharmony_ci *
46f08c3bdfSopenharmony_ci *      This line has to be added to avoid a stupid CVS problem
47f08c3bdfSopenharmony_ci *****************************************************************************/
48f08c3bdfSopenharmony_ci
49f08c3bdfSopenharmony_ci#include <stdio.h>
50f08c3bdfSopenharmony_ci#include <stdlib.h>
51f08c3bdfSopenharmony_ci#include <math.h>
52f08c3bdfSopenharmony_ci#include <librttest.h>
53f08c3bdfSopenharmony_ci#include <libstats.h>
54f08c3bdfSopenharmony_ci
55f08c3bdfSopenharmony_ci#define PRIO 89
56f08c3bdfSopenharmony_ci//#define PERIOD 17*NS_PER_MS
57f08c3bdfSopenharmony_ci//#define ITERATIONS 100
58f08c3bdfSopenharmony_ci#define MIN_ITERATIONS 100
59f08c3bdfSopenharmony_ci#define DEFAULT_ITERATIONS 10000
60f08c3bdfSopenharmony_ci#define DEF_PERIOD 5*NS_PER_MS
61f08c3bdfSopenharmony_ci#define DEF_LOAD_MS 1
62f08c3bdfSopenharmony_ci#define PASS_US 100
63f08c3bdfSopenharmony_ci#define HIST_BUCKETS 100
64f08c3bdfSopenharmony_ci#define OVERHEAD 50000		// allow for 50 us of periodic overhead (context switch, etc.)
65f08c3bdfSopenharmony_ci
66f08c3bdfSopenharmony_cinsec_t start;
67f08c3bdfSopenharmony_cinsec_t end;
68f08c3bdfSopenharmony_cistatic int ret = 0;
69f08c3bdfSopenharmony_cistatic int iterations = 0;
70f08c3bdfSopenharmony_cistatic unsigned long long latency_threshold = 0;
71f08c3bdfSopenharmony_cistatic nsec_t period = DEF_PERIOD;
72f08c3bdfSopenharmony_cistatic unsigned int load_ms = DEF_LOAD_MS;
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_cistats_container_t dat;
75f08c3bdfSopenharmony_cistats_container_t hist;
76f08c3bdfSopenharmony_cistats_quantiles_t quantiles;
77f08c3bdfSopenharmony_cistats_record_t rec;
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_civoid usage(void)
80f08c3bdfSopenharmony_ci{
81f08c3bdfSopenharmony_ci	rt_help();
82f08c3bdfSopenharmony_ci	printf("sched_latency specific options:\n");
83f08c3bdfSopenharmony_ci	printf("  -dLOAD	periodic load in ms (default 1)\n");
84f08c3bdfSopenharmony_ci	printf("  -lTHRESHOLD   trace latency, with given threshold in us\n");
85f08c3bdfSopenharmony_ci	printf("  -tPERIOD      period in ms (default 5)\n");
86f08c3bdfSopenharmony_ci	printf("  -iITERATIONS  number of iterations (default %d)\n",
87f08c3bdfSopenharmony_ci	       DEFAULT_ITERATIONS);
88f08c3bdfSopenharmony_ci}
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_ciint parse_args(int c, char *v)
91f08c3bdfSopenharmony_ci{
92f08c3bdfSopenharmony_ci
93f08c3bdfSopenharmony_ci	int handled = 1;
94f08c3bdfSopenharmony_ci	switch (c) {
95f08c3bdfSopenharmony_ci	case 'h':
96f08c3bdfSopenharmony_ci		usage();
97f08c3bdfSopenharmony_ci		exit(0);
98f08c3bdfSopenharmony_ci	case 'd':
99f08c3bdfSopenharmony_ci		load_ms = atoi(v);
100f08c3bdfSopenharmony_ci		break;
101f08c3bdfSopenharmony_ci	case 'i':
102f08c3bdfSopenharmony_ci		iterations = atoi(v);
103f08c3bdfSopenharmony_ci		break;
104f08c3bdfSopenharmony_ci	case 'l':
105f08c3bdfSopenharmony_ci		latency_threshold = strtoull(v, NULL, 0);
106f08c3bdfSopenharmony_ci		break;
107f08c3bdfSopenharmony_ci	case 't':
108f08c3bdfSopenharmony_ci		period = strtoull(v, NULL, 0) * NS_PER_MS;
109f08c3bdfSopenharmony_ci		break;
110f08c3bdfSopenharmony_ci	default:
111f08c3bdfSopenharmony_ci		handled = 0;
112f08c3bdfSopenharmony_ci		break;
113f08c3bdfSopenharmony_ci	}
114f08c3bdfSopenharmony_ci	return handled;
115f08c3bdfSopenharmony_ci}
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_civoid *periodic_thread(void *arg)
118f08c3bdfSopenharmony_ci{
119f08c3bdfSopenharmony_ci	int i;
120f08c3bdfSopenharmony_ci	nsec_t delay, avg_delay = 0, start_delay, min_delay = -1ULL, max_delay =
121f08c3bdfSopenharmony_ci	    0;
122f08c3bdfSopenharmony_ci	int failures = 0;
123f08c3bdfSopenharmony_ci	nsec_t next = 0, now = 0, sched_delta = 0, delta = 0, prev =
124f08c3bdfSopenharmony_ci	    0, iter_start;
125f08c3bdfSopenharmony_ci
126f08c3bdfSopenharmony_ci	/* wait for the specified start time */
127f08c3bdfSopenharmony_ci	rt_nanosleep_until(start);
128f08c3bdfSopenharmony_ci
129f08c3bdfSopenharmony_ci	now = rt_gettime();
130f08c3bdfSopenharmony_ci	start_delay = (now - start) / NS_PER_US;
131f08c3bdfSopenharmony_ci	iter_start = next = now;
132f08c3bdfSopenharmony_ci
133f08c3bdfSopenharmony_ci	debug(DBG_INFO, "ITERATION DELAY(US) MAX_DELAY(US) FAILURES\n");
134f08c3bdfSopenharmony_ci	debug(DBG_INFO, "--------- --------- ------------- --------\n");
135f08c3bdfSopenharmony_ci
136f08c3bdfSopenharmony_ci	if (latency_threshold) {
137f08c3bdfSopenharmony_ci		latency_trace_enable();
138f08c3bdfSopenharmony_ci		latency_trace_start();
139f08c3bdfSopenharmony_ci	}
140f08c3bdfSopenharmony_ci	for (i = 0; i < iterations; i++) {
141f08c3bdfSopenharmony_ci		/* wait for the period to start */
142f08c3bdfSopenharmony_ci		next += period;
143f08c3bdfSopenharmony_ci		prev = now;
144f08c3bdfSopenharmony_ci		now = rt_gettime();
145f08c3bdfSopenharmony_ci
146f08c3bdfSopenharmony_ci		if (next < now) {
147f08c3bdfSopenharmony_ci			printf("\nPERIOD MISSED!\n");
148f08c3bdfSopenharmony_ci			printf("     scheduled delta: %8llu us\n",
149f08c3bdfSopenharmony_ci			       sched_delta / 1000);
150f08c3bdfSopenharmony_ci			printf("	actual delta: %8llu us\n",
151f08c3bdfSopenharmony_ci			       delta / 1000);
152f08c3bdfSopenharmony_ci			printf("	     latency: %8llu us\n",
153f08c3bdfSopenharmony_ci			       (delta - sched_delta) / 1000);
154f08c3bdfSopenharmony_ci			printf("---------------------------------------\n");
155f08c3bdfSopenharmony_ci			printf("      previous start: %8llu us\n",
156f08c3bdfSopenharmony_ci			       (prev - iter_start) / 1000);
157f08c3bdfSopenharmony_ci			printf("		 now: %8llu us\n",
158f08c3bdfSopenharmony_ci			       (now - iter_start) / 1000);
159f08c3bdfSopenharmony_ci			printf("     scheduled start: %8llu us\n",
160f08c3bdfSopenharmony_ci			       (next - iter_start) / 1000);
161f08c3bdfSopenharmony_ci			printf("next scheduled start is in the past!\n");
162f08c3bdfSopenharmony_ci			ret = 1;
163f08c3bdfSopenharmony_ci			break;
164f08c3bdfSopenharmony_ci		}
165f08c3bdfSopenharmony_ci
166f08c3bdfSopenharmony_ci		sched_delta = next - now;	/* how long we should sleep */
167f08c3bdfSopenharmony_ci		delta = 0;
168f08c3bdfSopenharmony_ci		do {
169f08c3bdfSopenharmony_ci			nsec_t new_now;
170f08c3bdfSopenharmony_ci
171f08c3bdfSopenharmony_ci			rt_nanosleep(next - now);
172f08c3bdfSopenharmony_ci			new_now = rt_gettime();
173f08c3bdfSopenharmony_ci			delta += new_now - now;	/* how long we did sleep */
174f08c3bdfSopenharmony_ci			now = new_now;
175f08c3bdfSopenharmony_ci		} while (now < next);
176f08c3bdfSopenharmony_ci
177f08c3bdfSopenharmony_ci		/* start of period */
178f08c3bdfSopenharmony_ci		delay =
179f08c3bdfSopenharmony_ci		    (now - iter_start - (nsec_t) (i + 1) * period) / NS_PER_US;
180f08c3bdfSopenharmony_ci		rec.x = i;
181f08c3bdfSopenharmony_ci		rec.y = delay;
182f08c3bdfSopenharmony_ci		stats_container_append(&dat, rec);
183f08c3bdfSopenharmony_ci
184f08c3bdfSopenharmony_ci		if (delay < min_delay)
185f08c3bdfSopenharmony_ci			min_delay = delay;
186f08c3bdfSopenharmony_ci		if (delay > max_delay)
187f08c3bdfSopenharmony_ci			max_delay = delay;
188f08c3bdfSopenharmony_ci		if (delay > pass_criteria) {
189f08c3bdfSopenharmony_ci			failures++;
190f08c3bdfSopenharmony_ci			ret = 1;
191f08c3bdfSopenharmony_ci		}
192f08c3bdfSopenharmony_ci		avg_delay += delay;
193f08c3bdfSopenharmony_ci		if (latency_threshold && delay > latency_threshold)
194f08c3bdfSopenharmony_ci			break;
195f08c3bdfSopenharmony_ci
196f08c3bdfSopenharmony_ci		/* continuous status ticker */
197f08c3bdfSopenharmony_ci		debug(DBG_INFO, "%9i %9llu %13llu %8i\r", i, delay, max_delay,
198f08c3bdfSopenharmony_ci		      failures);
199f08c3bdfSopenharmony_ci		fflush(stdout);
200f08c3bdfSopenharmony_ci
201f08c3bdfSopenharmony_ci		busy_work_ms(load_ms);
202f08c3bdfSopenharmony_ci	}
203f08c3bdfSopenharmony_ci	if (latency_threshold) {
204f08c3bdfSopenharmony_ci		latency_trace_stop();
205f08c3bdfSopenharmony_ci		if (i != iterations) {
206f08c3bdfSopenharmony_ci			printf
207f08c3bdfSopenharmony_ci			    ("Latency threshold (%lluus) exceeded at iteration %d\n",
208f08c3bdfSopenharmony_ci			     latency_threshold, i);
209f08c3bdfSopenharmony_ci			latency_trace_print();
210f08c3bdfSopenharmony_ci			stats_container_resize(&dat, i + 1);
211f08c3bdfSopenharmony_ci		}
212f08c3bdfSopenharmony_ci	}
213f08c3bdfSopenharmony_ci
214f08c3bdfSopenharmony_ci	/* save samples before the quantile calculation messes things up! */
215f08c3bdfSopenharmony_ci	stats_hist(&hist, &dat);
216f08c3bdfSopenharmony_ci	stats_container_save("samples",
217f08c3bdfSopenharmony_ci			     "Periodic Scheduling Latency Scatter Plot",
218f08c3bdfSopenharmony_ci			     "Iteration", "Latency (us)", &dat, "points");
219f08c3bdfSopenharmony_ci	stats_container_save("hist", "Periodic Scheduling Latency Histogram",
220f08c3bdfSopenharmony_ci			     "Latency (us)", "Samples", &hist, "steps");
221f08c3bdfSopenharmony_ci
222f08c3bdfSopenharmony_ci	avg_delay /= i;
223f08c3bdfSopenharmony_ci	printf("\n\n");
224f08c3bdfSopenharmony_ci	printf("Start: %4llu us: %s\n", start_delay,
225f08c3bdfSopenharmony_ci	       start_delay < pass_criteria ? "PASS" : "FAIL");
226f08c3bdfSopenharmony_ci	printf("Min:   %4llu us: %s\n", min_delay,
227f08c3bdfSopenharmony_ci	       min_delay < pass_criteria ? "PASS" : "FAIL");
228f08c3bdfSopenharmony_ci	printf("Max:   %4llu us: %s\n", max_delay,
229f08c3bdfSopenharmony_ci	       max_delay < pass_criteria ? "PASS" : "FAIL");
230f08c3bdfSopenharmony_ci	printf("Avg:   %4llu us: %s\n", avg_delay,
231f08c3bdfSopenharmony_ci	       avg_delay < pass_criteria ? "PASS" : "FAIL");
232f08c3bdfSopenharmony_ci	printf("StdDev: %.4f us\n", stats_stddev(&dat));
233f08c3bdfSopenharmony_ci	printf("Quantiles:\n");
234f08c3bdfSopenharmony_ci	stats_quantiles_calc(&dat, &quantiles);
235f08c3bdfSopenharmony_ci	stats_quantiles_print(&quantiles);
236f08c3bdfSopenharmony_ci	printf("Failed Iterations: %d\n", failures);
237f08c3bdfSopenharmony_ci
238f08c3bdfSopenharmony_ci	return NULL;
239f08c3bdfSopenharmony_ci}
240f08c3bdfSopenharmony_ci
241f08c3bdfSopenharmony_ciint main(int argc, char *argv[])
242f08c3bdfSopenharmony_ci{
243f08c3bdfSopenharmony_ci	int per_id;
244f08c3bdfSopenharmony_ci	setup();
245f08c3bdfSopenharmony_ci
246f08c3bdfSopenharmony_ci	pass_criteria = PASS_US;
247f08c3bdfSopenharmony_ci	rt_init("d:l:ht:i:", parse_args, argc, argv);
248f08c3bdfSopenharmony_ci
249f08c3bdfSopenharmony_ci	printf("-------------------------------\n");
250f08c3bdfSopenharmony_ci	printf("Scheduling Latency\n");
251f08c3bdfSopenharmony_ci	printf("-------------------------------\n\n");
252f08c3bdfSopenharmony_ci
253f08c3bdfSopenharmony_ci	if (load_ms * NS_PER_MS >= period - OVERHEAD) {
254f08c3bdfSopenharmony_ci		printf("ERROR: load must be < period - %d us\n",
255f08c3bdfSopenharmony_ci		       OVERHEAD / NS_PER_US);
256f08c3bdfSopenharmony_ci		exit(1);
257f08c3bdfSopenharmony_ci	}
258f08c3bdfSopenharmony_ci
259f08c3bdfSopenharmony_ci	if (iterations == 0)
260f08c3bdfSopenharmony_ci		iterations = DEFAULT_ITERATIONS;
261f08c3bdfSopenharmony_ci	if (iterations < MIN_ITERATIONS) {
262f08c3bdfSopenharmony_ci		printf
263f08c3bdfSopenharmony_ci		    ("Too few iterations (%d), use min iteration instead (%d)\n",
264f08c3bdfSopenharmony_ci		     iterations, MIN_ITERATIONS);
265f08c3bdfSopenharmony_ci		iterations = MIN_ITERATIONS;
266f08c3bdfSopenharmony_ci	}
267f08c3bdfSopenharmony_ci
268f08c3bdfSopenharmony_ci	printf("Running %d iterations with a period of %llu ms\n", iterations,
269f08c3bdfSopenharmony_ci	       period / NS_PER_MS);
270f08c3bdfSopenharmony_ci	printf("Periodic load duration: %d ms\n", load_ms);
271f08c3bdfSopenharmony_ci	printf("Expected running time: %d s\n",
272f08c3bdfSopenharmony_ci	       (int)(iterations * ((float)period / NS_PER_SEC)));
273f08c3bdfSopenharmony_ci
274f08c3bdfSopenharmony_ci	if (stats_container_init(&dat, iterations))
275f08c3bdfSopenharmony_ci		exit(1);
276f08c3bdfSopenharmony_ci
277f08c3bdfSopenharmony_ci	if (stats_container_init(&hist, HIST_BUCKETS)) {
278f08c3bdfSopenharmony_ci		stats_container_free(&dat);
279f08c3bdfSopenharmony_ci		exit(1);
280f08c3bdfSopenharmony_ci	}
281f08c3bdfSopenharmony_ci
282f08c3bdfSopenharmony_ci	/* use the highest value for the quantiles */
283f08c3bdfSopenharmony_ci	if (stats_quantiles_init(&quantiles, (int)log10(iterations))) {
284f08c3bdfSopenharmony_ci		stats_container_free(&hist);
285f08c3bdfSopenharmony_ci		stats_container_free(&dat);
286f08c3bdfSopenharmony_ci		exit(1);
287f08c3bdfSopenharmony_ci	}
288f08c3bdfSopenharmony_ci
289f08c3bdfSopenharmony_ci	/* wait one quarter second to execute */
290f08c3bdfSopenharmony_ci	start = rt_gettime() + 250 * NS_PER_MS;
291f08c3bdfSopenharmony_ci	per_id = create_fifo_thread(periodic_thread, NULL, PRIO);
292f08c3bdfSopenharmony_ci
293f08c3bdfSopenharmony_ci	join_thread(per_id);
294f08c3bdfSopenharmony_ci	join_threads();
295f08c3bdfSopenharmony_ci
296f08c3bdfSopenharmony_ci	printf("\nCriteria: latencies < %d us\n", (int)pass_criteria);
297f08c3bdfSopenharmony_ci	printf("Result: %s\n", ret ? "FAIL" : "PASS");
298f08c3bdfSopenharmony_ci
299f08c3bdfSopenharmony_ci	stats_container_free(&dat);
300f08c3bdfSopenharmony_ci	stats_container_free(&hist);
301f08c3bdfSopenharmony_ci	stats_quantiles_free(&quantiles);
302f08c3bdfSopenharmony_ci
303f08c3bdfSopenharmony_ci	return ret;
304f08c3bdfSopenharmony_ci}
305