1f08c3bdfSopenharmony_ci/******************************************************************************
2f08c3bdfSopenharmony_ci *
3f08c3bdfSopenharmony_ci *   Copyright © International Business Machines  Corp., 2007, 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 *     hrtimer-prio.c
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci * DESCRIPTION
23f08c3bdfSopenharmony_ci *     Test the latency of hrtimers under rt load.
24f08c3bdfSopenharmony_ci *     The busy_threads should run at a priority higher than the system
25f08c3bdfSopenharmony_ci *     softirq_hrtimer, but lower than the timer_thread.  The timer_thread
26f08c3bdfSopenharmony_ci *     measure the time it takes to return from a nanosleep call.  If the
27f08c3bdfSopenharmony_ci *     lower priority threads can increase the latency of the higher
28f08c3bdfSopenharmony_ci *     priority thread, it is considered a failure.
29f08c3bdfSopenharmony_ci *
30f08c3bdfSopenharmony_ci * USAGE:
31f08c3bdfSopenharmony_ci *     Use run_auto.sh script in current directory to build and run test.
32f08c3bdfSopenharmony_ci *
33f08c3bdfSopenharmony_ci * AUTHOR
34f08c3bdfSopenharmony_ci *      Darren Hart <dvhltc@us.ibm.com>
35f08c3bdfSopenharmony_ci *
36f08c3bdfSopenharmony_ci * HISTORY
37f08c3bdfSopenharmony_ci *      2007-Aug-08:      Initial version by Darren Hart <dvhltc@us.ibm.com>
38f08c3bdfSopenharmony_ci *
39f08c3bdfSopenharmony_ci *      This line has to be added to avoid a stupid CVS problem
40f08c3bdfSopenharmony_ci *****************************************************************************/
41f08c3bdfSopenharmony_ci
42f08c3bdfSopenharmony_ci#include <stdio.h>
43f08c3bdfSopenharmony_ci#include <stdlib.h>
44f08c3bdfSopenharmony_ci#include <math.h>
45f08c3bdfSopenharmony_ci#include <librttest.h>
46f08c3bdfSopenharmony_ci#include <libstats.h>
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_ci#define DEF_MED_PRIO 60		// (softirqd-hrtimer,98)
49f08c3bdfSopenharmony_ci#define DEF_ITERATIONS 10000
50f08c3bdfSopenharmony_ci#define HIST_BUCKETS 100
51f08c3bdfSopenharmony_ci#define DEF_BUSY_TIME 10	// Duration of busy work in milliseconds
52f08c3bdfSopenharmony_ci#define DEF_SLEEP_TIME 10000	// Duration of nanosleep in nanoseconds
53f08c3bdfSopenharmony_ci#define DEF_CRITERIA 10		// maximum timer latency in microseconds
54f08c3bdfSopenharmony_ci
55f08c3bdfSopenharmony_cistatic int med_prio = DEF_MED_PRIO;
56f08c3bdfSopenharmony_cistatic int high_prio;
57f08c3bdfSopenharmony_cistatic int busy_time = DEF_BUSY_TIME;
58f08c3bdfSopenharmony_cistatic int iterations = DEF_ITERATIONS;
59f08c3bdfSopenharmony_cistatic int busy_threads;
60f08c3bdfSopenharmony_ci
61f08c3bdfSopenharmony_cistatic stats_container_t dat;
62f08c3bdfSopenharmony_cistatic stats_record_t rec;
63f08c3bdfSopenharmony_cistatic atomic_t busy_threads_started;
64f08c3bdfSopenharmony_cistatic unsigned long min_delta;
65f08c3bdfSopenharmony_cistatic unsigned long max_delta;
66f08c3bdfSopenharmony_ci
67f08c3bdfSopenharmony_civoid usage(void)
68f08c3bdfSopenharmony_ci{
69f08c3bdfSopenharmony_ci	rt_help();
70f08c3bdfSopenharmony_ci	printf("hrtimer-prio specific options:\n");
71f08c3bdfSopenharmony_ci	printf("  -t#	   #:busy work time in ms, defaults to %d ms\n",
72f08c3bdfSopenharmony_ci	       DEF_BUSY_TIME);
73f08c3bdfSopenharmony_ci	printf("  -i#	   #:number of iterations, defaults to %d\n",
74f08c3bdfSopenharmony_ci	       DEF_ITERATIONS);
75f08c3bdfSopenharmony_ci	printf("  -n#	   #:number of busy threads, defaults to NR_CPUS*2\n");
76f08c3bdfSopenharmony_ci	printf
77f08c3bdfSopenharmony_ci	    ("  -f#	   #:rt fifo priority of busy threads (1,98), defaults to %d\n",
78f08c3bdfSopenharmony_ci	     DEF_MED_PRIO);
79f08c3bdfSopenharmony_ci}
80f08c3bdfSopenharmony_ci
81f08c3bdfSopenharmony_ciint parse_args(int c, char *v)
82f08c3bdfSopenharmony_ci{
83f08c3bdfSopenharmony_ci
84f08c3bdfSopenharmony_ci	int handled = 1;
85f08c3bdfSopenharmony_ci	switch (c) {
86f08c3bdfSopenharmony_ci	case 'h':
87f08c3bdfSopenharmony_ci		usage();
88f08c3bdfSopenharmony_ci		exit(0);
89f08c3bdfSopenharmony_ci	case 't':
90f08c3bdfSopenharmony_ci		busy_time = atoi(v);
91f08c3bdfSopenharmony_ci		break;
92f08c3bdfSopenharmony_ci	case 'n':
93f08c3bdfSopenharmony_ci		busy_threads = atoi(v);
94f08c3bdfSopenharmony_ci		break;
95f08c3bdfSopenharmony_ci	case 'f':
96f08c3bdfSopenharmony_ci		med_prio = MIN(atoi(v), 98);
97f08c3bdfSopenharmony_ci		break;
98f08c3bdfSopenharmony_ci	case 'i':
99f08c3bdfSopenharmony_ci		iterations = atoi(v);
100f08c3bdfSopenharmony_ci		if (iterations < 100) {
101f08c3bdfSopenharmony_ci			fprintf(stderr,
102f08c3bdfSopenharmony_ci				"Number of iterations cannot be less than 100.\n");
103f08c3bdfSopenharmony_ci			exit(1);
104f08c3bdfSopenharmony_ci		}
105f08c3bdfSopenharmony_ci		break;
106f08c3bdfSopenharmony_ci	default:
107f08c3bdfSopenharmony_ci		handled = 0;
108f08c3bdfSopenharmony_ci		break;
109f08c3bdfSopenharmony_ci	}
110f08c3bdfSopenharmony_ci	return handled;
111f08c3bdfSopenharmony_ci}
112f08c3bdfSopenharmony_ci
113f08c3bdfSopenharmony_civoid *busy_thread(void *thread)
114f08c3bdfSopenharmony_ci{
115f08c3bdfSopenharmony_ci	atomic_inc(&busy_threads_started);
116f08c3bdfSopenharmony_ci	while (1) {
117f08c3bdfSopenharmony_ci		busy_work_ms(busy_time);
118f08c3bdfSopenharmony_ci		sched_yield();
119f08c3bdfSopenharmony_ci	}
120f08c3bdfSopenharmony_ci	return NULL;
121f08c3bdfSopenharmony_ci}
122f08c3bdfSopenharmony_ci
123f08c3bdfSopenharmony_civoid *timer_thread(void *thread)
124f08c3bdfSopenharmony_ci{
125f08c3bdfSopenharmony_ci	int i;
126f08c3bdfSopenharmony_ci	nsec_t start, end;
127f08c3bdfSopenharmony_ci	unsigned long delta_us;
128f08c3bdfSopenharmony_ci	while (atomic_get(&busy_threads_started) < busy_threads) {
129f08c3bdfSopenharmony_ci		rt_nanosleep(10000);
130f08c3bdfSopenharmony_ci	}
131f08c3bdfSopenharmony_ci	printf("All Busy Threads started, commencing test\n");	// FIXME: use debug infrastructure
132f08c3bdfSopenharmony_ci	max_delta = 0;
133f08c3bdfSopenharmony_ci	for (i = 0; i < iterations; i++) {
134f08c3bdfSopenharmony_ci		start = rt_gettime();
135f08c3bdfSopenharmony_ci		rt_nanosleep(DEF_SLEEP_TIME);
136f08c3bdfSopenharmony_ci		end = rt_gettime();
137f08c3bdfSopenharmony_ci		delta_us =
138f08c3bdfSopenharmony_ci		    ((unsigned long)(end - start) - DEF_SLEEP_TIME) / NS_PER_US;
139f08c3bdfSopenharmony_ci		rec.x = i;
140f08c3bdfSopenharmony_ci		rec.y = delta_us;
141f08c3bdfSopenharmony_ci		stats_container_append(&dat, rec);
142f08c3bdfSopenharmony_ci		max_delta = MAX(max_delta, delta_us);
143f08c3bdfSopenharmony_ci		min_delta = (i == 0) ? delta_us : MIN(min_delta, delta_us);
144f08c3bdfSopenharmony_ci	}
145f08c3bdfSopenharmony_ci	return NULL;
146f08c3bdfSopenharmony_ci}
147f08c3bdfSopenharmony_ci
148f08c3bdfSopenharmony_ciint main(int argc, char *argv[])
149f08c3bdfSopenharmony_ci{
150f08c3bdfSopenharmony_ci	int ret = 1;
151f08c3bdfSopenharmony_ci	int b;
152f08c3bdfSopenharmony_ci	float avg_delta;
153f08c3bdfSopenharmony_ci	int t_id;
154f08c3bdfSopenharmony_ci	setup();
155f08c3bdfSopenharmony_ci	busy_threads = 2 * sysconf(_SC_NPROCESSORS_ONLN);	// default busy_threads
156f08c3bdfSopenharmony_ci	pass_criteria = DEF_CRITERIA;
157f08c3bdfSopenharmony_ci	rt_init("f:i:jhn:t:", parse_args, argc, argv);
158f08c3bdfSopenharmony_ci	high_prio = med_prio + 1;
159f08c3bdfSopenharmony_ci
160f08c3bdfSopenharmony_ci	// Set main()'s prio to one above the timer_thread so it is sure to not
161f08c3bdfSopenharmony_ci	// be starved
162f08c3bdfSopenharmony_ci	if (set_priority(high_prio + 1) < 0) {
163f08c3bdfSopenharmony_ci		printf("Failed to set main()'s priority to %d\n",
164f08c3bdfSopenharmony_ci		       high_prio + 1);
165f08c3bdfSopenharmony_ci		exit(1);
166f08c3bdfSopenharmony_ci	}
167f08c3bdfSopenharmony_ci
168f08c3bdfSopenharmony_ci	printf("\n-------------------------------------------\n");
169f08c3bdfSopenharmony_ci	printf("High Resolution Timer Priority (Starvation)\n");
170f08c3bdfSopenharmony_ci	printf("-------------------------------------------\n\n");
171f08c3bdfSopenharmony_ci	printf("Running %d iterations\n", iterations);
172f08c3bdfSopenharmony_ci	printf("Running with %d busy threads\n", busy_threads);
173f08c3bdfSopenharmony_ci	printf("Busy thread work time: %d\n", busy_time);
174f08c3bdfSopenharmony_ci	printf("Busy thread priority: %d\n", med_prio);
175f08c3bdfSopenharmony_ci	printf("Timer thread priority: %d\n", high_prio);
176f08c3bdfSopenharmony_ci
177f08c3bdfSopenharmony_ci	stats_container_t hist;
178f08c3bdfSopenharmony_ci	stats_quantiles_t quantiles;
179f08c3bdfSopenharmony_ci	if (stats_container_init(&dat, iterations)) {
180f08c3bdfSopenharmony_ci		printf("Cannot init stat containers for dat\n");
181f08c3bdfSopenharmony_ci		exit(1);
182f08c3bdfSopenharmony_ci	}
183f08c3bdfSopenharmony_ci	if (stats_container_init(&hist, HIST_BUCKETS)) {
184f08c3bdfSopenharmony_ci		printf("Cannot init stat containers for hist\n");
185f08c3bdfSopenharmony_ci		exit(1);
186f08c3bdfSopenharmony_ci	}
187f08c3bdfSopenharmony_ci	if (stats_quantiles_init(&quantiles, (int)log10(iterations))) {
188f08c3bdfSopenharmony_ci		printf("Cannot init stat quantiles\n");
189f08c3bdfSopenharmony_ci		exit(1);
190f08c3bdfSopenharmony_ci	}
191f08c3bdfSopenharmony_ci
192f08c3bdfSopenharmony_ci	t_id = create_fifo_thread(timer_thread, NULL, high_prio);
193f08c3bdfSopenharmony_ci	if (t_id == -1) {
194f08c3bdfSopenharmony_ci		printf("Failed to create timer thread\n");
195f08c3bdfSopenharmony_ci		exit(1);
196f08c3bdfSopenharmony_ci	}
197f08c3bdfSopenharmony_ci	for (b = 0; b < busy_threads; b++) {
198f08c3bdfSopenharmony_ci		if (create_fifo_thread(busy_thread, NULL, med_prio) < 0) {
199f08c3bdfSopenharmony_ci			printf("Failed to create a busy thread\n");
200f08c3bdfSopenharmony_ci			exit(1);
201f08c3bdfSopenharmony_ci		}
202f08c3bdfSopenharmony_ci	}
203f08c3bdfSopenharmony_ci	join_thread(t_id);
204f08c3bdfSopenharmony_ci
205f08c3bdfSopenharmony_ci	avg_delta = stats_avg(&dat);
206f08c3bdfSopenharmony_ci	stats_hist(&hist, &dat);
207f08c3bdfSopenharmony_ci	stats_container_save("samples",
208f08c3bdfSopenharmony_ci			     "High Resolution Timer Latency Scatter Plot",
209f08c3bdfSopenharmony_ci			     "Iteration", "Latency (us)", &dat, "points");
210f08c3bdfSopenharmony_ci	stats_container_save("hist", "High Resolution Timer Latency Histogram",
211f08c3bdfSopenharmony_ci			     "Latency (us)", "Samples", &hist, "steps");
212f08c3bdfSopenharmony_ci
213f08c3bdfSopenharmony_ci	if (max_delta <= pass_criteria)
214f08c3bdfSopenharmony_ci		ret = 0;
215f08c3bdfSopenharmony_ci
216f08c3bdfSopenharmony_ci	printf("Minimum: %ld us\n", min_delta);
217f08c3bdfSopenharmony_ci	printf("Maximum: %ld us\n", max_delta);
218f08c3bdfSopenharmony_ci	printf("Average: %f us\n", avg_delta);
219f08c3bdfSopenharmony_ci	printf("Standard Deviation: %f\n", stats_stddev(&dat));
220f08c3bdfSopenharmony_ci	printf("Quantiles:\n");
221f08c3bdfSopenharmony_ci	stats_quantiles_calc(&dat, &quantiles);
222f08c3bdfSopenharmony_ci	stats_quantiles_print(&quantiles);
223f08c3bdfSopenharmony_ci	printf("\nCriteria: Maximum wakeup latency < %lu us\n",
224f08c3bdfSopenharmony_ci	       (unsigned long)pass_criteria);
225f08c3bdfSopenharmony_ci	printf("Result: %s\n", ret ? "FAIL" : "PASS");
226f08c3bdfSopenharmony_ci
227f08c3bdfSopenharmony_ci	return ret;
228f08c3bdfSopenharmony_ci}
229