• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
75 volatile int busy_threads = 0;
76 volatile int test_over = 0;
77 volatile int threads_running = 0;
78 static int rt_threads = -1;
79 static int int_threads = 0;
80 static pthread_mutex_t bmutex = PTHREAD_MUTEX_INITIALIZER;
81 
82 static pthread_mutex_t mutex[NUM_WORKERS + 1];
83 static pthread_cond_t cond[NUM_WORKERS + 1];
84 static int t_after_wait[NUM_WORKERS];
85 
86 static int ret = 0;
87 
88 pthread_barrier_t barrier;
89 
usage(void)90 void 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 
parse_args(int c,char * v)98 int 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 
int_thread(void * arg)119 void *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 
busy_thread(void * arg)135 void *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 
worker_thread(void * arg)161 void *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 
master_thread(void * arg)231 void *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 
main(int argc,char * argv[])290 int 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