• 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 #define _GNU_SOURCE
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <signal.h>
66 #include <time.h>
67 #include <pthread.h>
68 #include <sched.h>
69 #include <errno.h>
70 #include <sys/syscall.h>
71 #include "librttest.h"
72 
73 #define NUM_WORKERS	27
74 #define CHECK_LIMIT	1
75 
76 volatile int busy_threads = 0;
77 volatile int test_over = 0;
78 volatile int threads_running = 0;
79 static int rt_threads = -1;
80 static int int_threads = 0;
81 static pthread_mutex_t bmutex = PTHREAD_MUTEX_INITIALIZER;
82 
83 static pthread_mutex_t mutex[NUM_WORKERS + 1];
84 static pthread_cond_t cond[NUM_WORKERS + 1];
85 static int t_after_wait[NUM_WORKERS];
86 
87 static int ret = 0;
88 
89 pthread_barrier_t barrier;
90 
usage(void)91 void usage(void)
92 {
93 	rt_help();
94 	printf("prio-preempt specific options:\n");
95 	printf("  -i	    #: enable interrupter threads\n");
96 	printf("  -n#	   #: number of busy threads\n");
97 }
98 
parse_args(int c,char * v)99 int parse_args(int c, char *v)
100 {
101 
102 	int handled = 1;
103 	switch (c) {
104 	case 'h':
105 		usage();
106 		exit(0);
107 	case 'i':
108 		int_threads = 1;
109 		break;
110 	case 'n':
111 		rt_threads = atoi(v);
112 		break;
113 	default:
114 		handled = 0;
115 		break;
116 	}
117 	return handled;
118 }
119 
int_thread(void * arg)120 void *int_thread(void *arg)
121 {
122 	intptr_t a = 0;
123 	while (!test_over) {
124 		/* do some busy work */
125 		if (!(a % 4))
126 			a = a * 3;
127 		else if (!(a % 6))
128 			a = a / 2;
129 		else
130 			a++;
131 		usleep(20);
132 	}
133 	return (void *)a;
134 }
135 
busy_thread(void * arg)136 void *busy_thread(void *arg)
137 {
138 	struct sched_param sched_param;
139 	int policy, mypri = 0, tid;
140 	tid = (intptr_t) (((struct thread *)arg)->arg);
141 
142 	if (pthread_getschedparam(pthread_self(), &policy, &sched_param) != 0) {
143 		printf("ERR: Couldn't get pthread info \n");
144 	} else {
145 		mypri = sched_param.sched_priority;
146 	}
147 
148 	pthread_mutex_lock(&bmutex);
149 	busy_threads++;
150 	printf("Busy Thread %d(%d): Running...\n", tid, mypri);
151 	pthread_mutex_unlock(&bmutex);
152 
153 	/* TODO: Add sched set affinity here */
154 
155 	/* Busy loop */
156 	while (!test_over) ;
157 
158 	printf("Busy Thread %d(%d): Exiting\n", tid, mypri);
159 	return NULL;
160 }
161 
worker_thread(void * arg)162 void *worker_thread(void *arg)
163 {
164 	struct sched_param sched_param;
165 	int policy, rc, mypri = 0, tid, times = 0;
166 	tid = (intptr_t) (((struct thread *)arg)->arg);
167 	nsec_t pstart, pend;
168 
169 	if (pthread_getschedparam(pthread_self(), &policy, &sched_param) != 0) {
170 		printf("ERR: Couldn't get pthread info \n");
171 	} else {
172 		mypri = sched_param.sched_priority;
173 	}
174 	/* check in */
175 	pthread_mutex_lock(&bmutex);
176 	threads_running++;
177 	pthread_mutex_unlock(&bmutex);
178 
179 	/* block */
180 	rc = pthread_mutex_lock(&mutex[tid]);
181 	if (tid == 0)
182 		pthread_barrier_wait(&barrier);
183 	rc = pthread_cond_wait(&cond[tid], &mutex[tid]);
184 	rc = pthread_mutex_unlock(&mutex[tid]);
185 
186 	debug(DBG_INFO, "%llu: Thread %d(%d) wakes up from sleep \n",
187 	      rt_gettime(), tid, mypri);
188 
189 	/*check if we're the last thread */
190 	if (tid == NUM_WORKERS - 1) {
191 		t_after_wait[tid] = 1;
192 		pthread_mutex_lock(&bmutex);
193 		threads_running--;
194 		pthread_mutex_unlock(&bmutex);
195 		return NULL;
196 	}
197 
198 	/* Signal next thread */
199 	rc = pthread_mutex_lock(&mutex[tid + 1]);
200 	rc = pthread_cond_signal(&cond[tid + 1]);
201 	debug(DBG_INFO, "%llu: Thread %d(%d): Sent signal (%d) to (%d)\n",
202 	      rt_gettime(), tid, mypri, rc, tid + 1);
203 
204 	pstart = pend = rt_gettime();
205 	rc = pthread_mutex_unlock(&mutex[tid + 1]);
206 
207 	debug(DBG_INFO, "%llu: Thread %d(%d) setting it's bit \n", rt_gettime(),
208 	      tid, mypri);
209 
210 	t_after_wait[tid] = 1;
211 
212 	while (t_after_wait[tid + 1] != 1) {
213 		pend = rt_gettime();
214 		times++;
215 	}
216 
217 	if (times >= (int)pass_criteria) {
218 		printf
219 		    ("Thread %d(%d): Non-Preempt limit reached. %llu ns latency\n",
220 		     tid, mypri, pend - pstart);
221 		ret = 1;
222 	}
223 
224 	/* check out */
225 	pthread_mutex_lock(&bmutex);
226 	threads_running--;
227 	pthread_mutex_unlock(&bmutex);
228 
229 	return NULL;
230 }
231 
master_thread(void * arg)232 void *master_thread(void *arg)
233 {
234 	int i, pri_boost;
235 
236 	pthread_barrier_init(&barrier, NULL, 2);
237 
238 	/* start interrupter thread */
239 	if (int_threads) {
240 		pri_boost = 90;
241 		for (i = 0; i < rt_threads; i++) {
242 			create_fifo_thread(int_thread, NULL,
243 					   sched_get_priority_min(SCHED_FIFO) +
244 					   pri_boost);
245 		}
246 	}
247 
248 	/* start the (N-1) busy threads */
249 	pri_boost = 80;
250 	for (i = rt_threads; i > 1; i--) {
251 		create_fifo_thread(busy_thread, (void *)(intptr_t) i,
252 				   sched_get_priority_min(SCHED_FIFO) +
253 				   pri_boost);
254 	}
255 
256 	/* make sure children are started */
257 	while (busy_threads < (rt_threads - 1))
258 		usleep(100);
259 
260 	printf("Busy threads created!\n");
261 
262 	/* start NUM_WORKERS worker threads */
263 	for (i = 0, pri_boost = 10; i < NUM_WORKERS; i++, pri_boost += 2) {
264 		pthread_mutex_init(&mutex[i], NULL);
265 		pthread_cond_init(&cond[i], NULL);
266 		create_fifo_thread(worker_thread, (void *)(intptr_t) i,
267 				   sched_get_priority_min(SCHED_FIFO) +
268 				   pri_boost);
269 	}
270 
271 	printf("Worker threads created\n");
272 	/* Let the worker threads wait on the cond vars */
273 	while (threads_running < NUM_WORKERS)
274 		usleep(100);
275 
276 	/* Ensure the first worker has called cond_wait */
277 	pthread_barrier_wait(&barrier);
278 
279 	printf("Signaling first thread\n");
280 	pthread_mutex_lock(&mutex[0]);
281 	pthread_cond_signal(&cond[0]);
282 	pthread_mutex_unlock(&mutex[0]);
283 
284 	while (threads_running)
285 		usleep(500000);	/* this period greatly affects the number of failures! */
286 
287 	test_over = 1;
288 	return NULL;
289 }
290 
main(int argc,char * argv[])291 int main(int argc, char *argv[])
292 {
293 	int pri_boost, numcpus;
294 	setup();
295 
296 	pass_criteria = CHECK_LIMIT;
297 	rt_init("hin:", parse_args, argc, argv);
298 
299 	numcpus = get_numcpus();
300 
301 	/* Max no. of busy threads should always be less than/equal the no. of
302 	   housekeeping cpus. Otherwise, the box will hang */
303 
304 	if (rt_threads == -1 || rt_threads > numcpus) {
305 		rt_threads = numcpus;
306 		printf("Maximum busy thread count(%d), "
307 		       "should not exceed number of cpus(%d)\n", rt_threads,
308 		       numcpus);
309 		printf("Using %d\n", numcpus);
310 	}
311 
312 	/* Test boilder plate: title and parameters */
313 	printf("\n-------------------\n");
314 	printf("Priority Preemption\n");
315 	printf("-------------------\n\n");
316 	printf("Busy Threads: %d\n", rt_threads);
317 	printf("Interrupter Threads: %s\n",
318 	       int_threads ? "Enabled" : "Disabled");
319 	printf("Worker Threads: %d\n\n", NUM_WORKERS);
320 
321 	pri_boost = 81;
322 	create_fifo_thread(master_thread, NULL,
323 			   sched_get_priority_min(SCHED_FIFO) + pri_boost);
324 
325 	/* wait for threads to complete */
326 	join_threads();
327 
328 	printf
329 	    ("\nCriteria: All threads appropriately preempted within %d loop(s)\n",
330 	     (int)pass_criteria);
331 	printf("Result: %s\n", ret ? "FAIL" : "PASS");
332 	return ret;
333 }
334