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