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, ¶m) != 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, ¶m) != 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