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