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