1 /*
2 * Copyright (c) 2003, Intel Corporation. All rights reserved.
3 * Created by: crystal.xiong REMOVE-THIS AT intel DOT com
4 * This file is licensed under the GPL license. For the full content
5 * of this license, see the COPYING file at the top level of this
6 * source tree.
7 */
8
9 /* There are n TF threads, n is equal to the processors in the system minus
10 * one. TFs are used to keep busy these CPUs, which have priority 3. A
11 * TL thread with lower priority 1 is created, which locks a mutex and
12 * does workload. A TB thread with higher priority 4 is created and try
13 * to lock TL's mutex. A TP thread with priority 2 is created to reflect the
14 * priority change of TL. Main thread has the highest priority 6, which will
15 * control the running steps of those threads, including creating threads,
16 * stopping threads. There is another thread to collect the sample data with
17 * priority 5.
18 *
19 * Steps:
20 * 1. Create n TF threads, n is equal to processors number minus one. TF
21 * will do workload.
22 * 2. Create 1 TP thread and do workload. The thread will keep running when
23 * TL is created.
24 * 3. Create 1 TL thread to lock a mutex. TL will get a chance to run
25 * when TP sleep a wee bit in between.
26 * 4. Create 1 TB thread to lock the mutex. TL's priority will boost to
27 * TB's priority, which will cause TP having no chance to run.
28 * 5. TB timeout from the lock, TL's priority will decrease, so TP and TL
29 * will keep working as before.
30 * 5. Keep running for a while to let TL stabilize.
31 * 6. Stop these threads.
32 *
33 * Thanks Inaky.Perez-Gonzalez's suggestion and code
34 */
35
36 #warning "Contains Linux-isms that need fixing."
37
38 #include <errno.h>
39 #include <pthread.h>
40 #include <sched.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <unistd.h>
46 #include "test.h"
47 #include "pitest.h"
48
49 int cpus;
50 pthread_mutex_t mutex;
51
52 volatile int ts_stop = 0;
53 volatile double base_time;
54
55 struct thread_param {
56 int index;
57 volatile int stop;
58 int sleep_ms;
59 int priority;
60 int policy;
61 const char *name;
62 int cpu;
63 volatile unsigned futex;
64 volatile unsigned should_stall;
65 volatile unsigned progress;
66 } tp[] = {
67 {
68 0, 0, 0, 1, SCHED_FIFO, "TL", 0, 0, 0, 0}, {
69 1, 0, 50, 2, SCHED_FIFO, "TP", 0, 0, 0, 0}, {
70 2, 0, 0, 3, SCHED_FIFO, "TF", 1, 0, 0, 0}, {
71 3, 0, 0, 3, SCHED_FIFO, "TF", 2, 0, 0, 0}, {
72 4, 0, 0, 3, SCHED_FIFO, "TF", 3, 0, 0, 0}, {
73 5, 0, 0, 3, SCHED_FIFO, "TF", 4, 0, 0, 0}, {
74 6, 0, 0, 3, SCHED_FIFO, "TF", 5, 0, 0, 0}, {
75 7, 0, 0, 3, SCHED_FIFO, "TF", 6, 0, 0, 0}
76 };
77
78 volatile unsigned do_work_dummy;
do_work(unsigned granularity_top,volatile unsigned * progress)79 void do_work(unsigned granularity_top, volatile unsigned *progress)
80 {
81 unsigned granularity_cnt, i;
82 unsigned top = 5 * 1000 * 1000;
83 unsigned dummy = do_work_dummy;
84
85 for (granularity_cnt = 0; granularity_cnt < granularity_top;
86 granularity_cnt++) {
87 for (i = 0; i < top; i++)
88 dummy = i | dummy;
89 (*progress)++;
90 }
91 return;
92 }
93
thread_fn(void * param)94 void *thread_fn(void *param)
95 {
96 struct thread_param *tp = param;
97 struct timespec ts;
98 int rc;
99 unsigned long mask = 1 << tp->cpu;
100
101 #if __linux__
102 rc = sched_setaffinity(0, sizeof(mask), &mask);
103 if (rc < 0) {
104 EPRINTF("UNRESOLVED: Thread %s index %d: Can't set affinity: "
105 "%d %s", tp->name, tp->index, rc, strerror(rc));
106 exit(UNRESOLVED);
107 }
108 #endif
109 test_set_priority(pthread_self(), SCHED_FIFO, tp->priority);
110
111 DPRINTF(stdout, "#EVENT %f %s Thread Started\n",
112 seconds_read() - base_time, tp->name);
113 tp->progress = 0;
114 ts.tv_sec = 0;
115 ts.tv_nsec = tp->sleep_ms * 1000 * 1000;
116 while (!tp->stop) {
117 do_work(5, &tp->progress);
118 if (tp->sleep_ms == 0)
119 continue;
120 rc = nanosleep(&ts, NULL);
121 if (rc < 0) {
122 EPRINTF("UNRESOLVED: Thread %s %d: nanosleep returned "
123 "%d %s \n", tp->name, tp->index, rc,
124 strerror(rc));
125 exit(UNRESOLVED);
126 }
127 }
128
129 DPRINTF(stdout, "#EVENT %f %s Thread Stopped\n",
130 seconds_read() - base_time, tp->name);
131 return NULL;
132 }
133
thread_tl(void * param)134 void *thread_tl(void *param)
135 {
136 struct thread_param *tp = param;
137 unsigned long mask = 1 << tp->cpu;
138 int rc;
139
140 #if __linux__
141 rc = sched_setaffinity((pid_t) 0, sizeof(mask), &mask);
142 #endif
143 test_set_priority(pthread_self(), SCHED_FIFO, tp->priority);
144 if (rc < 0) {
145 EPRINTF
146 ("UNRESOLVED: Thread %s index %d: Can't set affinity: %d %s",
147 tp->name, tp->index, rc, strerror(rc));
148 exit(UNRESOLVED);
149 }
150
151 DPRINTF(stdout, "#EVENT %f Thread TL Started\n",
152 seconds_read() - base_time);
153 tp->progress = 0;
154 pthread_mutex_lock(&mutex);
155 while (!tp->stop) {
156 do_work(5, &tp->progress);
157 }
158 pthread_mutex_unlock(&mutex);
159
160 DPRINTF(stdout, "#EVENT %f Thread TL Stopped\n",
161 seconds_read() - base_time);
162 return NULL;
163 }
164
thread_sample(void * arg)165 void *thread_sample(void *arg)
166 {
167 char buffer[1024];
168 struct timespec ts;
169 double period = 250;
170 double newtime;
171 size_t size;
172 int i;
173 int rc;
174
175 test_set_priority(pthread_self(), SCHED_FIFO, 5);
176 DPRINTF(stderr, "Thread Sampler: started \n");
177 DPRINTF(stdout, "# COLUMNS %d Time TL TP ", 2 + cpus);
178 for (i = 0; i < (cpus - 1); i++)
179 DPRINTF(stdout, "TF%d ", i);
180 DPRINTF(stdout, "\n");
181 ts.tv_sec = 0;
182 ts.tv_nsec = period * 1000 * 1000;
183 while (!ts_stop) {
184 newtime = seconds_read();
185 size = snprintf(buffer, 1023, "%f ", newtime - base_time);
186 for (i = 0; i < cpus + 1; i++)
187 size +=
188 snprintf(buffer + size, 1023 - size, "%u ",
189 tp[i].progress);
190 DPRINTF(stdout, "%s \n", buffer);
191 rc = nanosleep(&ts, NULL);
192 if (rc < 0)
193 EPRINTF("UNRESOLVED: Thread %s %d: nanosleep returned "
194 "%d %s", tp->name, tp->index, rc, strerror(rc));
195 }
196 return NULL;
197 }
198
thread_tb(void * arg)199 void *thread_tb(void *arg)
200 {
201 struct timespec boost_time;
202 double seconds, t0, t1;
203 int rc;
204
205 test_set_priority(pthread_self(), SCHED_FIFO, 4);
206
207 DPRINTF(stdout, "#EVENT %f TB Starts\n", seconds_read() - base_time);
208
209 boost_time.tv_sec = time(NULL) + *((time_t *) arg);
210 boost_time.tv_nsec = 0;
211
212 t0 = seconds_read();
213 rc = pthread_mutex_timedlock(&mutex, &boost_time);
214 t1 = seconds_read();
215 seconds = t1 - t0;
216
217 DPRINTF(stdout, "#EVENT %f TB Waited for %.2f s\n",
218 t1 - base_time, seconds);
219
220 if (rc != ETIMEDOUT) {
221 EPRINTF("FAIL: Thread TB: lock returned %d %s, ", rc,
222 strerror(rc));
223 exit(FAIL);
224 }
225 DPRINTF(stderr, "Thread TB: DONE. lock returned %d %s, "
226 "slept %f \n", rc, strerror(rc), seconds)
227
228 return NULL;
229 }
230
main(void)231 int main(void)
232 {
233 cpus = sysconf(_SC_NPROCESSORS_ONLN);
234 pthread_mutexattr_t mutex_attr;
235 pthread_attr_t threadattr;
236 pthread_t threads[cpus - 1], threadsample, threadtp, threadtl, threadtb;
237
238 int multiplier = 1;
239 int i;
240 int rc;
241
242 test_set_priority(pthread_self(), SCHED_FIFO, 6);
243 base_time = seconds_read();
244
245 /* Initialize a mutex with PTHREAD_PRIO_INHERIT protocol */
246 mutex_attr_init(&mutex_attr);
247 mutex_init(&mutex, &mutex_attr);
248
249 /* Initialize thread attr */
250 threadattr_init(&threadattr);
251
252 /* Start the sample thread */
253 DPRINTF(stderr, "Main Thread: start sample thread \n");
254 rc = pthread_create(&threadsample, &threadattr, thread_sample, NULL);
255 if (rc != 0) {
256 EPRINTF("UNRESOLVED: pthread_create: %d %s", rc, strerror(rc));
257 exit(UNRESOLVED);
258 }
259
260 /* Start the TF threads */
261 DPRINTF(stderr, "Main Thread: start %d TF thread\n", cpus - 1);
262 for (i = 0; i < cpus - 1; i++) {
263 rc = pthread_create(&threads[i], &threadattr, thread_fn,
264 &tp[i + 2]);
265 if (rc != 0) {
266 EPRINTF("UNRESOLVED: pthread_create: %d %s",
267 rc, strerror(rc));
268 exit(UNRESOLVED);
269 }
270 }
271 sleep(base_time + multiplier * 10 - seconds_read());
272
273 /* Start TP thread */
274
275 DPRINTF(stderr, "Main Thread: start TP thread\n");
276 rc = pthread_create(&threadtp, &threadattr, thread_fn, &tp[1]);
277 if (rc != 0) {
278 EPRINTF("UNRESOLVED: pthread_create: %d %s", rc, strerror(rc));
279 exit(UNRESOLVED);
280 }
281 sleep(base_time + multiplier * 20 - seconds_read());
282
283 /* Start TL thread */
284 DPRINTF(stderr, "Main Thread: start TL thread\n");
285 rc = pthread_create(&threadtl, &threadattr, thread_tl, &tp[0]);
286 if (rc != 0) {
287 EPRINTF("UNRESOLVED: pthread_create: %d %s", rc, strerror(rc));
288 exit(UNRESOLVED);
289 }
290 sleep(base_time + multiplier * 30 - seconds_read());
291
292 /* Start TB thread (boosting thread) */
293 DPRINTF(stderr, "Main Thread: start TB thread\n");
294 time_t timeout = multiplier * 20;
295 rc = pthread_create(&threadtb, &threadattr, thread_tb, &timeout);
296 if (rc != 0) {
297 EPRINTF("UNRESOLVED: pthread_create: %d %s", rc, strerror(rc));
298 exit(UNRESOLVED);
299 }
300 sleep(base_time + multiplier * 60 - seconds_read());
301
302 /* Stop TL thread */
303
304 DPRINTF(stderr, "Main Thread: stop TL thread\n");
305 tp[0].stop = 1;
306 sleep(base_time + multiplier * 70 - seconds_read());
307
308 /* Stop TP thread */
309 DPRINTF(stderr, "Main Thread: stop TP thread\n");
310 tp[1].stop = 1;
311 sleep(base_time + multiplier * 80 - seconds_read());
312
313 /* Stop TF threads */
314 DPRINTF(stderr, "Main Thread: stop TF threads\n");
315 for (i = 2; i < cpus - 1; i++) {
316 tp[i].stop = 1;
317 }
318
319 /* Stop sampler */
320 ts_stop = 1;
321 DPRINTF(stderr, "Main Thread: stop sampler thread \n");
322 return 0;
323 }
324