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 *     prio-preempt.c
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci * DESCRIPTION
23f08c3bdfSopenharmony_ci *     Test whether priority pre-emption works fine.
24f08c3bdfSopenharmony_ci *
25f08c3bdfSopenharmony_ci *    The main thread:
26f08c3bdfSopenharmony_ci *     - Creates a minimum of (N-1) busy threads at priority starting at
27f08c3bdfSopenharmony_ci *		     SCHED_FIFO + 80
28f08c3bdfSopenharmony_ci *     - Creates 26 FIFO (T1, T2,...,T26) threads with priorities 10, 11,...,36.
29f08c3bdfSopenharmony_ci *     - Each of these worker threads executes the following piece of code:
30f08c3bdfSopenharmony_ci *		   pthread_mutex_lock(Mi);
31f08c3bdfSopenharmony_ci *		   pthread_cond_wait(CVi);
32f08c3bdfSopenharmony_ci *		   pthread_mutex_unlock(Mi);
33f08c3bdfSopenharmony_ci *
34f08c3bdfSopenharmony_ci *       where Mi is the ith pthread_mutex_t and CVi is the ith conditional
35f08c3bdfSopenharmony_ci *       variable.So, at the end of this loop, 26 threads are all waiting on
36f08c3bdfSopenharmony_ci *       seperate condvars and mutexes.
37f08c3bdfSopenharmony_ci *     - Wakes up thread at priority 10 (T1) by executing:
38f08c3bdfSopenharmony_ci *	   pthread_mutex_lock(M1);
39f08c3bdfSopenharmony_ci *	   pthread_cond_signal(CV1);
40f08c3bdfSopenharmony_ci *	   pthread_mutex_unlock(M1);
41f08c3bdfSopenharmony_ci *
42f08c3bdfSopenharmony_ci *     - Waits for all the worker threads to finish execution.
43f08c3bdfSopenharmony_ci *	 T1 then wakes up T2 by signalling on the condvar CV2 and sets a flag
44f08c3bdfSopenharmony_ci *	 called T1_after_wait to indicate that it is after the wait. It then
45f08c3bdfSopenharmony_ci *	 checks if T2_after_wait has been set or not. If not, the test fails,
46f08c3bdfSopenharmony_ci *	 else the process continues with other threads. The thread T1 expects
47f08c3bdfSopenharmony_ci *	 T2_after_wait to be set as, the moment T1 signals on CV2, T2 is
48f08c3bdfSopenharmony_ci *	 supposed to be scheduled (in accordance with priority preemption).
49f08c3bdfSopenharmony_ci *
50f08c3bdfSopenharmony_ci * USAGE:
51f08c3bdfSopenharmony_ci *      Use run_auto.sh script in current directory to build and run test.
52f08c3bdfSopenharmony_ci *
53f08c3bdfSopenharmony_ci * AUTHOR
54f08c3bdfSopenharmony_ci *      Dinakar Guniguntala <dino@us.ibm.com>
55f08c3bdfSopenharmony_ci *
56f08c3bdfSopenharmony_ci * HISTORY
57f08c3bdfSopenharmony_ci *      2006-Jun-01: Initial version by Dinakar Guniguntala
58f08c3bdfSopenharmony_ci *		    Changes from John Stultz and Vivek Pallantla
59f08c3bdfSopenharmony_ci *
60f08c3bdfSopenharmony_ci *****************************************************************************/
61f08c3bdfSopenharmony_ci
62f08c3bdfSopenharmony_ci#include <stdio.h>
63f08c3bdfSopenharmony_ci#include <stdlib.h>
64f08c3bdfSopenharmony_ci#include <signal.h>
65f08c3bdfSopenharmony_ci#include <time.h>
66f08c3bdfSopenharmony_ci#include <pthread.h>
67f08c3bdfSopenharmony_ci#include <sched.h>
68f08c3bdfSopenharmony_ci#include <errno.h>
69f08c3bdfSopenharmony_ci#include <sys/syscall.h>
70f08c3bdfSopenharmony_ci#include <librttest.h>
71f08c3bdfSopenharmony_ci
72f08c3bdfSopenharmony_ci#define NUM_WORKERS	27
73f08c3bdfSopenharmony_ci#define CHECK_LIMIT	1
74f08c3bdfSopenharmony_ci
75f08c3bdfSopenharmony_civolatile int busy_threads = 0;
76f08c3bdfSopenharmony_civolatile int test_over = 0;
77f08c3bdfSopenharmony_civolatile int threads_running = 0;
78f08c3bdfSopenharmony_cistatic int rt_threads = -1;
79f08c3bdfSopenharmony_cistatic int int_threads = 0;
80f08c3bdfSopenharmony_cistatic pthread_mutex_t bmutex = PTHREAD_MUTEX_INITIALIZER;
81f08c3bdfSopenharmony_ci
82f08c3bdfSopenharmony_cistatic pthread_mutex_t mutex[NUM_WORKERS + 1];
83f08c3bdfSopenharmony_cistatic pthread_cond_t cond[NUM_WORKERS + 1];
84f08c3bdfSopenharmony_cistatic int t_after_wait[NUM_WORKERS];
85f08c3bdfSopenharmony_ci
86f08c3bdfSopenharmony_cistatic int ret = 0;
87f08c3bdfSopenharmony_ci
88f08c3bdfSopenharmony_cipthread_barrier_t barrier;
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_civoid usage(void)
91f08c3bdfSopenharmony_ci{
92f08c3bdfSopenharmony_ci	rt_help();
93f08c3bdfSopenharmony_ci	printf("prio-preempt specific options:\n");
94f08c3bdfSopenharmony_ci	printf("  -i	    #: enable interrupter threads\n");
95f08c3bdfSopenharmony_ci	printf("  -n#	   #: number of busy threads\n");
96f08c3bdfSopenharmony_ci}
97f08c3bdfSopenharmony_ci
98f08c3bdfSopenharmony_ciint parse_args(int c, char *v)
99f08c3bdfSopenharmony_ci{
100f08c3bdfSopenharmony_ci
101f08c3bdfSopenharmony_ci	int handled = 1;
102f08c3bdfSopenharmony_ci	switch (c) {
103f08c3bdfSopenharmony_ci	case 'h':
104f08c3bdfSopenharmony_ci		usage();
105f08c3bdfSopenharmony_ci		exit(0);
106f08c3bdfSopenharmony_ci	case 'i':
107f08c3bdfSopenharmony_ci		int_threads = 1;
108f08c3bdfSopenharmony_ci		break;
109f08c3bdfSopenharmony_ci	case 'n':
110f08c3bdfSopenharmony_ci		rt_threads = atoi(v);
111f08c3bdfSopenharmony_ci		break;
112f08c3bdfSopenharmony_ci	default:
113f08c3bdfSopenharmony_ci		handled = 0;
114f08c3bdfSopenharmony_ci		break;
115f08c3bdfSopenharmony_ci	}
116f08c3bdfSopenharmony_ci	return handled;
117f08c3bdfSopenharmony_ci}
118f08c3bdfSopenharmony_ci
119f08c3bdfSopenharmony_civoid *int_thread(void *arg)
120f08c3bdfSopenharmony_ci{
121f08c3bdfSopenharmony_ci	intptr_t a = 0;
122f08c3bdfSopenharmony_ci	while (!test_over) {
123f08c3bdfSopenharmony_ci		/* do some busy work */
124f08c3bdfSopenharmony_ci		if (!(a % 4))
125f08c3bdfSopenharmony_ci			a = a * 3;
126f08c3bdfSopenharmony_ci		else if (!(a % 6))
127f08c3bdfSopenharmony_ci			a = a / 2;
128f08c3bdfSopenharmony_ci		else
129f08c3bdfSopenharmony_ci			a++;
130f08c3bdfSopenharmony_ci		usleep(20);
131f08c3bdfSopenharmony_ci	}
132f08c3bdfSopenharmony_ci	return (void *)a;
133f08c3bdfSopenharmony_ci}
134f08c3bdfSopenharmony_ci
135f08c3bdfSopenharmony_civoid *busy_thread(void *arg)
136f08c3bdfSopenharmony_ci{
137f08c3bdfSopenharmony_ci	struct sched_param sched_param;
138f08c3bdfSopenharmony_ci	int policy, mypri = 0, tid;
139f08c3bdfSopenharmony_ci	tid = (intptr_t) (((struct thread *)arg)->arg);
140f08c3bdfSopenharmony_ci
141f08c3bdfSopenharmony_ci	if (pthread_getschedparam(pthread_self(), &policy, &sched_param) != 0) {
142f08c3bdfSopenharmony_ci		printf("ERR: Couldn't get pthread info \n");
143f08c3bdfSopenharmony_ci	} else {
144f08c3bdfSopenharmony_ci		mypri = sched_param.sched_priority;
145f08c3bdfSopenharmony_ci	}
146f08c3bdfSopenharmony_ci
147f08c3bdfSopenharmony_ci	pthread_mutex_lock(&bmutex);
148f08c3bdfSopenharmony_ci	busy_threads++;
149f08c3bdfSopenharmony_ci	printf("Busy Thread %d(%d): Running...\n", tid, mypri);
150f08c3bdfSopenharmony_ci	pthread_mutex_unlock(&bmutex);
151f08c3bdfSopenharmony_ci
152f08c3bdfSopenharmony_ci	/* TODO: Add sched set affinity here */
153f08c3bdfSopenharmony_ci
154f08c3bdfSopenharmony_ci	/* Busy loop */
155f08c3bdfSopenharmony_ci	while (!test_over) ;
156f08c3bdfSopenharmony_ci
157f08c3bdfSopenharmony_ci	printf("Busy Thread %d(%d): Exiting\n", tid, mypri);
158f08c3bdfSopenharmony_ci	return NULL;
159f08c3bdfSopenharmony_ci}
160f08c3bdfSopenharmony_ci
161f08c3bdfSopenharmony_civoid *worker_thread(void *arg)
162f08c3bdfSopenharmony_ci{
163f08c3bdfSopenharmony_ci	struct sched_param sched_param;
164f08c3bdfSopenharmony_ci	int policy, rc, mypri = 0, tid, times = 0;
165f08c3bdfSopenharmony_ci	tid = (intptr_t) (((struct thread *)arg)->arg);
166f08c3bdfSopenharmony_ci	nsec_t pstart, pend;
167f08c3bdfSopenharmony_ci
168f08c3bdfSopenharmony_ci	if (pthread_getschedparam(pthread_self(), &policy, &sched_param) != 0) {
169f08c3bdfSopenharmony_ci		printf("ERR: Couldn't get pthread info \n");
170f08c3bdfSopenharmony_ci	} else {
171f08c3bdfSopenharmony_ci		mypri = sched_param.sched_priority;
172f08c3bdfSopenharmony_ci	}
173f08c3bdfSopenharmony_ci	/* check in */
174f08c3bdfSopenharmony_ci	pthread_mutex_lock(&bmutex);
175f08c3bdfSopenharmony_ci	threads_running++;
176f08c3bdfSopenharmony_ci	pthread_mutex_unlock(&bmutex);
177f08c3bdfSopenharmony_ci
178f08c3bdfSopenharmony_ci	/* block */
179f08c3bdfSopenharmony_ci	rc = pthread_mutex_lock(&mutex[tid]);
180f08c3bdfSopenharmony_ci	if (tid == 0)
181f08c3bdfSopenharmony_ci		pthread_barrier_wait(&barrier);
182f08c3bdfSopenharmony_ci	rc = pthread_cond_wait(&cond[tid], &mutex[tid]);
183f08c3bdfSopenharmony_ci	rc = pthread_mutex_unlock(&mutex[tid]);
184f08c3bdfSopenharmony_ci
185f08c3bdfSopenharmony_ci	debug(DBG_INFO, "%llu: Thread %d(%d) wakes up from sleep \n",
186f08c3bdfSopenharmony_ci	      rt_gettime(), tid, mypri);
187f08c3bdfSopenharmony_ci
188f08c3bdfSopenharmony_ci	/*check if we're the last thread */
189f08c3bdfSopenharmony_ci	if (tid == NUM_WORKERS - 1) {
190f08c3bdfSopenharmony_ci		t_after_wait[tid] = 1;
191f08c3bdfSopenharmony_ci		pthread_mutex_lock(&bmutex);
192f08c3bdfSopenharmony_ci		threads_running--;
193f08c3bdfSopenharmony_ci		pthread_mutex_unlock(&bmutex);
194f08c3bdfSopenharmony_ci		return NULL;
195f08c3bdfSopenharmony_ci	}
196f08c3bdfSopenharmony_ci
197f08c3bdfSopenharmony_ci	/* Signal next thread */
198f08c3bdfSopenharmony_ci	rc = pthread_mutex_lock(&mutex[tid + 1]);
199f08c3bdfSopenharmony_ci	rc = pthread_cond_signal(&cond[tid + 1]);
200f08c3bdfSopenharmony_ci	debug(DBG_INFO, "%llu: Thread %d(%d): Sent signal (%d) to (%d)\n",
201f08c3bdfSopenharmony_ci	      rt_gettime(), tid, mypri, rc, tid + 1);
202f08c3bdfSopenharmony_ci
203f08c3bdfSopenharmony_ci	pstart = pend = rt_gettime();
204f08c3bdfSopenharmony_ci	rc = pthread_mutex_unlock(&mutex[tid + 1]);
205f08c3bdfSopenharmony_ci
206f08c3bdfSopenharmony_ci	debug(DBG_INFO, "%llu: Thread %d(%d) setting it's bit \n", rt_gettime(),
207f08c3bdfSopenharmony_ci	      tid, mypri);
208f08c3bdfSopenharmony_ci
209f08c3bdfSopenharmony_ci	t_after_wait[tid] = 1;
210f08c3bdfSopenharmony_ci
211f08c3bdfSopenharmony_ci	while (t_after_wait[tid + 1] != 1) {
212f08c3bdfSopenharmony_ci		pend = rt_gettime();
213f08c3bdfSopenharmony_ci		times++;
214f08c3bdfSopenharmony_ci	}
215f08c3bdfSopenharmony_ci
216f08c3bdfSopenharmony_ci	if (times >= (int)pass_criteria) {
217f08c3bdfSopenharmony_ci		printf
218f08c3bdfSopenharmony_ci		    ("Thread %d(%d): Non-Preempt limit reached. %llu ns latency\n",
219f08c3bdfSopenharmony_ci		     tid, mypri, pend - pstart);
220f08c3bdfSopenharmony_ci		ret = 1;
221f08c3bdfSopenharmony_ci	}
222f08c3bdfSopenharmony_ci
223f08c3bdfSopenharmony_ci	/* check out */
224f08c3bdfSopenharmony_ci	pthread_mutex_lock(&bmutex);
225f08c3bdfSopenharmony_ci	threads_running--;
226f08c3bdfSopenharmony_ci	pthread_mutex_unlock(&bmutex);
227f08c3bdfSopenharmony_ci
228f08c3bdfSopenharmony_ci	return NULL;
229f08c3bdfSopenharmony_ci}
230f08c3bdfSopenharmony_ci
231f08c3bdfSopenharmony_civoid *master_thread(void *arg)
232f08c3bdfSopenharmony_ci{
233f08c3bdfSopenharmony_ci	int i, pri_boost;
234f08c3bdfSopenharmony_ci
235f08c3bdfSopenharmony_ci	pthread_barrier_init(&barrier, NULL, 2);
236f08c3bdfSopenharmony_ci
237f08c3bdfSopenharmony_ci	/* start interrupter thread */
238f08c3bdfSopenharmony_ci	if (int_threads) {
239f08c3bdfSopenharmony_ci		pri_boost = 90;
240f08c3bdfSopenharmony_ci		for (i = 0; i < rt_threads; i++) {
241f08c3bdfSopenharmony_ci			create_fifo_thread(int_thread, NULL,
242f08c3bdfSopenharmony_ci					   sched_get_priority_min(SCHED_FIFO) +
243f08c3bdfSopenharmony_ci					   pri_boost);
244f08c3bdfSopenharmony_ci		}
245f08c3bdfSopenharmony_ci	}
246f08c3bdfSopenharmony_ci
247f08c3bdfSopenharmony_ci	/* start the (N-1) busy threads */
248f08c3bdfSopenharmony_ci	pri_boost = 80;
249f08c3bdfSopenharmony_ci	for (i = rt_threads; i > 1; i--) {
250f08c3bdfSopenharmony_ci		create_fifo_thread(busy_thread, (void *)(intptr_t) i,
251f08c3bdfSopenharmony_ci				   sched_get_priority_min(SCHED_FIFO) +
252f08c3bdfSopenharmony_ci				   pri_boost);
253f08c3bdfSopenharmony_ci	}
254f08c3bdfSopenharmony_ci
255f08c3bdfSopenharmony_ci	/* make sure children are started */
256f08c3bdfSopenharmony_ci	while (busy_threads < (rt_threads - 1))
257f08c3bdfSopenharmony_ci		usleep(100);
258f08c3bdfSopenharmony_ci
259f08c3bdfSopenharmony_ci	printf("Busy threads created!\n");
260f08c3bdfSopenharmony_ci
261f08c3bdfSopenharmony_ci	/* start NUM_WORKERS worker threads */
262f08c3bdfSopenharmony_ci	for (i = 0, pri_boost = 10; i < NUM_WORKERS; i++, pri_boost += 2) {
263f08c3bdfSopenharmony_ci		pthread_mutex_init(&mutex[i], NULL);
264f08c3bdfSopenharmony_ci		pthread_cond_init(&cond[i], NULL);
265f08c3bdfSopenharmony_ci		create_fifo_thread(worker_thread, (void *)(intptr_t) i,
266f08c3bdfSopenharmony_ci				   sched_get_priority_min(SCHED_FIFO) +
267f08c3bdfSopenharmony_ci				   pri_boost);
268f08c3bdfSopenharmony_ci	}
269f08c3bdfSopenharmony_ci
270f08c3bdfSopenharmony_ci	printf("Worker threads created\n");
271f08c3bdfSopenharmony_ci	/* Let the worker threads wait on the cond vars */
272f08c3bdfSopenharmony_ci	while (threads_running < NUM_WORKERS)
273f08c3bdfSopenharmony_ci		usleep(100);
274f08c3bdfSopenharmony_ci
275f08c3bdfSopenharmony_ci	/* Ensure the first worker has called cond_wait */
276f08c3bdfSopenharmony_ci	pthread_barrier_wait(&barrier);
277f08c3bdfSopenharmony_ci
278f08c3bdfSopenharmony_ci	printf("Signaling first thread\n");
279f08c3bdfSopenharmony_ci	pthread_mutex_lock(&mutex[0]);
280f08c3bdfSopenharmony_ci	pthread_cond_signal(&cond[0]);
281f08c3bdfSopenharmony_ci	pthread_mutex_unlock(&mutex[0]);
282f08c3bdfSopenharmony_ci
283f08c3bdfSopenharmony_ci	while (threads_running)
284f08c3bdfSopenharmony_ci		usleep(500000);	/* this period greatly affects the number of failures! */
285f08c3bdfSopenharmony_ci
286f08c3bdfSopenharmony_ci	test_over = 1;
287f08c3bdfSopenharmony_ci	return NULL;
288f08c3bdfSopenharmony_ci}
289f08c3bdfSopenharmony_ci
290f08c3bdfSopenharmony_ciint main(int argc, char *argv[])
291f08c3bdfSopenharmony_ci{
292f08c3bdfSopenharmony_ci	int pri_boost, numcpus;
293f08c3bdfSopenharmony_ci	setup();
294f08c3bdfSopenharmony_ci
295f08c3bdfSopenharmony_ci	pass_criteria = CHECK_LIMIT;
296f08c3bdfSopenharmony_ci	rt_init("hin:", parse_args, argc, argv);
297f08c3bdfSopenharmony_ci
298f08c3bdfSopenharmony_ci	numcpus = sysconf(_SC_NPROCESSORS_ONLN);
299f08c3bdfSopenharmony_ci
300f08c3bdfSopenharmony_ci	/* Max no. of busy threads should always be less than/equal the no. of cpus
301f08c3bdfSopenharmony_ci	   Otherwise, the box will hang */
302f08c3bdfSopenharmony_ci
303f08c3bdfSopenharmony_ci	if (rt_threads == -1 || rt_threads > numcpus) {
304f08c3bdfSopenharmony_ci		rt_threads = numcpus;
305f08c3bdfSopenharmony_ci		printf("Maximum busy thread count(%d), "
306f08c3bdfSopenharmony_ci		       "should not exceed number of cpus(%d)\n", rt_threads,
307f08c3bdfSopenharmony_ci		       numcpus);
308f08c3bdfSopenharmony_ci		printf("Using %d\n", numcpus);
309f08c3bdfSopenharmony_ci	}
310f08c3bdfSopenharmony_ci
311f08c3bdfSopenharmony_ci	/* Test boilder plate: title and parameters */
312f08c3bdfSopenharmony_ci	printf("\n-------------------\n");
313f08c3bdfSopenharmony_ci	printf("Priority Preemption\n");
314f08c3bdfSopenharmony_ci	printf("-------------------\n\n");
315f08c3bdfSopenharmony_ci	printf("Busy Threads: %d\n", rt_threads);
316f08c3bdfSopenharmony_ci	printf("Interrupter Threads: %s\n",
317f08c3bdfSopenharmony_ci	       int_threads ? "Enabled" : "Disabled");
318f08c3bdfSopenharmony_ci	printf("Worker Threads: %d\n\n", NUM_WORKERS);
319f08c3bdfSopenharmony_ci
320f08c3bdfSopenharmony_ci	pri_boost = 81;
321f08c3bdfSopenharmony_ci	create_fifo_thread(master_thread, NULL,
322f08c3bdfSopenharmony_ci			   sched_get_priority_min(SCHED_FIFO) + pri_boost);
323f08c3bdfSopenharmony_ci
324f08c3bdfSopenharmony_ci	/* wait for threads to complete */
325f08c3bdfSopenharmony_ci	join_threads();
326f08c3bdfSopenharmony_ci
327f08c3bdfSopenharmony_ci	printf
328f08c3bdfSopenharmony_ci	    ("\nCriteria: All threads appropriately preempted within %d loop(s)\n",
329f08c3bdfSopenharmony_ci	     (int)pass_criteria);
330f08c3bdfSopenharmony_ci	printf("Result: %s\n", ret ? "FAIL" : "PASS");
331f08c3bdfSopenharmony_ci	return ret;
332f08c3bdfSopenharmony_ci}
333