1/*
2 *  This program is free software; you can redistribute it and/or modify
3 *  it under the terms of the GNU General Public License version 2.
4 *
5 *  This program is distributed in the hope that it will be useful,
6 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
7 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
8 *  GNU General Public License for more details.
9 *
10 *
11 * Test that the running thread relinquish the processor until it again becomes
12 * the head of its thread list.
13 *
14 * Steps:
15 *  1. Set the policy to SCHED_FIFO.
16 *  2. Launch as many processes as number CPU minus 1. These processses set
17 *     their priority to the max and block all but 1 processor.
18 *  3. Launch a thread which increase a counter in a infinite loop.
19 *  4. Launch a thread which call sched_yield() and check that the counter has
20 *     changed since the call.
21 */
22#define LINUX
23
24#ifdef LINUX
25#define _GNU_SOURCE
26#endif
27
28#include <sched.h>
29#include <stdio.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <signal.h>
33#include <pthread.h>
34#include <errno.h>
35#include <sys/wait.h>
36#include "posixtest.h"
37
38#ifdef BSD
39# include <sys/types.h>
40# include <sys/param.h>
41# include <sys/sysctl.h>
42#endif
43
44#ifdef HPUX
45# include <sys/param.h>
46# include <sys/pstat.h>
47#endif
48
49
50#define LOOP 1000     /* Shall be >= 1 */
51
52
53volatile int nb_call = 0;
54
55/* Get the number of CPUs */
56int get_ncpu() {
57	int ncpu = -1;
58
59	/* This syscall is not POSIX but it should work on many system */
60#ifdef _SC_NPROCESSORS_ONLN
61	ncpu = sysconf(_SC_NPROCESSORS_ONLN);
62#else
63# ifdef BSD
64	int mib[2];
65	size_t len = sizeof(ncpu);
66	mib[0] = CTL_HW;
67	mib[1] = HW_NCPU;
68	sysctl(mib, 2, &ncpu, &len, NULL, 0);
69# else
70#  ifdef HPUX
71	struct pst_dynamic psd;
72	pstat_getdynamic(&psd, sizeof(psd), 1, 0);
73	ncpu = (int)psd.psd_proc_cnt;
74#  endif /* HPUX */
75# endif /* BSD */
76#endif /* _SC_NPROCESSORS_ONLN */
77
78	return ncpu;
79}
80
81#ifdef LINUX
82int set_process_affinity(int cpu)
83{
84	int retval = -1;
85	cpu_set_t cpu_mask;
86
87	CPU_ZERO(&cpu_mask);
88	if (cpu >= 0 && cpu <= CPU_SETSIZE) {
89		CPU_SET(cpu, &cpu_mask);
90	} else {
91		fprintf (stderr, "Wrong cpu id: %d\n", cpu);
92		return -1;
93	}
94
95//#ifndef P2_SCHED_SETAFFINITY
96	retval = sched_setaffinity(0, sizeof(cpu_mask), &cpu_mask);
97//#else
98//	retval = sched_setaffinity(0, &cpu_mask);
99//#endif
100	if (retval == -1)
101	perror("Error at sched_setaffinity()");
102
103        return retval;
104}
105
106int set_thread_affinity(int cpu)
107{
108	int retval = -1;
109	cpu_set_t cpu_mask;
110
111	CPU_ZERO(&cpu_mask);
112	if (cpu >= 0 && cpu <= CPU_SETSIZE) {
113		CPU_SET(cpu, &cpu_mask);
114	} else {
115		fprintf (stderr, "Wrong cpu id: %d\n", cpu);
116		return -1;
117	}
118//#ifndef P2_PTHREAD_SETAFFINITY
119	retval = pthread_setaffinity_np(pthread_self(),
120			sizeof(cpu_mask), &cpu_mask);
121//#else
122//	retval = pthread_setaffinity_np(pthread_self(), &cpu_mask);
123//#endif
124        if (retval != 0)
125	fprintf (stderr, "Error at pthread_setaffinity_np():\n");
126	return retval;
127}
128
129#endif
130
131void * runner(void * arg) {
132	int i=0, nc;
133	long result = 0;
134#ifdef LINUX
135        set_thread_affinity(*(int *)arg);
136        fprintf(stderr, "%ld bind to cpu: %d\n", pthread_self(), *(int*)arg);
137#endif
138
139	for(;i<LOOP;i++){
140		nc = nb_call;
141		sched_yield();
142
143		/* If the value of nb_call has not change since the last call
144		   of sched_yield, that means that the thread does not
145		   relinquish the processor */
146		if(nc == nb_call) {
147			result++;
148		}
149	}
150
151	pthread_exit((void*)(result));
152
153	return NULL;
154}
155
156void * busy_thread(void *arg){
157#ifdef LINUX
158        set_thread_affinity(*(int *)arg);
159        fprintf(stderr, "%ld bind to cpu: %d\n", pthread_self(), *(int*)arg);
160#endif
161        while(1){
162                nb_call++;
163		sched_yield();
164	}
165
166	return NULL;
167}
168
169
170void buzy_process(int cpu){
171        struct sched_param param;
172
173#ifdef LINUX
174        /* Bind to a processor */
175        set_process_affinity(cpu);
176        fprintf(stderr, "%d bind to cpu: %d\n", getpid(), cpu);
177#endif
178        param.sched_priority = sched_get_priority_max(SCHED_FIFO);
179        if(sched_setscheduler(getpid(), SCHED_FIFO, &param) != 0) {
180                perror("An error occurs when calling sched_setparam()");
181                return;
182        }
183
184        /* to avoid blocking */
185        alarm(2);
186        while(1);
187
188}
189
190
191int main() {
192	int i, ncpu;
193	int *child_pid;
194	pthread_t tid, tid_runner;
195	void *tmpresult;
196	long result;
197	pthread_attr_t attr;
198        struct sched_param param;
199        int thread_cpu;
200
201
202	ncpu = get_ncpu();
203	if(ncpu == -1) {
204		printf("Can not get the number of CPUs of your machines.\n");
205		return PTS_UNRESOLVED;
206	}
207
208	printf("System has %d processors\n", ncpu);
209
210        param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1;
211        if(sched_setscheduler(getpid(), SCHED_FIFO, &param) != 0) {
212		if(errno == EPERM){
213			printf("This process does not have the permission to set its own scheduling policy.\nTry to launch this test as root.\n");
214			return PTS_UNRESOLVED;
215		}
216		perror("An error occurs when calling sched_setscheduler()");
217                return PTS_UNRESOLVED;
218        }
219
220	child_pid = malloc((ncpu-1)*sizeof(int));
221
222	for(i=0; i<ncpu-1; i++) {
223		child_pid[i] = fork();
224		if(child_pid[i] == -1){
225			perror("An error occurs when calling fork()");
226			return PTS_UNRESOLVED;
227		} else if (child_pid[i] == 0){
228
229			buzy_process(i);
230
231			printf("This code should not be executed.\n");
232			return PTS_UNRESOLVED;
233		}
234	}
235
236
237	pthread_attr_init(&attr);
238        pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
239
240        thread_cpu = ncpu -1;
241	if(pthread_create(&tid, &attr, busy_thread, &thread_cpu) != 0) {
242		perror("An error occurs when calling pthread_create()");
243		return PTS_UNRESOLVED;
244	}
245
246	if(pthread_create(&tid_runner, &attr, runner, &thread_cpu) != 0) {
247		perror("An error occurs when calling pthread_create()");
248		return PTS_UNRESOLVED;
249	}
250
251	if(pthread_join(tid_runner, &tmpresult) != 0) {
252		perror("An error occurs when calling pthread_join()");
253		return PTS_UNRESOLVED;
254	}
255
256        for(i=0; i<ncpu-1; i++)
257                waitpid(child_pid[i], NULL, 0);
258
259        result = (long)tmpresult;
260	if(result){
261		printf("A thread does not relinquish the processor.\n");
262		return PTS_FAIL;
263	}
264
265	printf("Test PASSED\n");
266	return PTS_PASS;
267
268}
269