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