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