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