1 /****************************************************************************** 2 * 3 * Copyright International Business Machines Corp., 2006, 2008 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 * NAME 20 * testpi-7.c 21 * 22 * DESCRIPTION 23 * measure the latency involved with PI boosting. 24 * 25 * 26 * USAGE: 27 * Use run_auto.sh script in current directory to build and run test. 28 * 29 * AUTHOR 30 * Darren Hart <dvhltc (at) us.ibm.com> 31 * 32 * HISTORY 33 * 2006-May-3: Initial version by Darren Hart <dvhltc (at) us.ibm.com> 34 * 2006-May-4: Timing fixes reported by Vernon Mauery <vernux (at) us.ibm.com> 35 * 2006-May-4: Made the med prio threads RT by Darren Hart <dvhltc (at) us.ibm.com> 36 * 2006-May-5: Modified to use flagging by Vernon Mauery <vernux (at) us.ibm.com> 37 * 38 *****************************************************************************/ 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <math.h> 43 #include <librttest.h> 44 45 #define HIGH_PRIO 15 46 #define MED_PRIO 10 47 #define LOW_PRIO 5 48 49 #define ITERATIONS 100 50 51 #define MED_WORK_MS 20 52 #define NS_PER_MS 1000000 53 54 static int use_flag_mutex; 55 static int max_delay_us; 56 static int max_drop2grab_us; 57 58 static pthread_mutex_t pi_mutex; 59 60 // flagging details 61 typedef enum { 62 LOW_START_CYCLE = 1, // 1 63 MED_START_WORK, // 2 64 HIGH_GRAB_MUTEX, // 3 65 LOW_DROP_MUTEX, // 4 66 END_OF_CYCLE, // 5 67 END_OF_GAME // 6 68 } phase_t; 69 70 static volatile phase_t phase_flag = END_OF_CYCLE; 71 72 static pthread_mutex_t flag_mutex; 73 74 int med_threads = 0; 75 long iterations = ITERATIONS; 76 77 void usage(void) 78 { 79 rt_help(); 80 printf("testpi-7 specific options:\n"); 81 printf(" -i# #: number of iterations\n"); 82 printf(" -f #: Use flag mutex\n"); 83 printf(" -x# #:number of mid priority threads\n"); 84 } 85 86 int parse_args(int c, char *v) 87 { 88 int handled = 1; 89 switch (c) { 90 case 'f': 91 use_flag_mutex = 0; 92 break; 93 case 'h': 94 usage(); 95 exit(0); 96 case 'i': 97 iterations = atoi(v); 98 break; 99 case 'x': 100 med_threads = atoi(v); 101 break; 102 default: 103 handled = 0; 104 break; 105 } 106 return handled; 107 } 108 109 phase_t _read_flag(const char *s, int l) 110 { 111 phase_t ret; 112 if (use_flag_mutex) 113 pthread_mutex_lock(&flag_mutex); 114 ret = phase_flag; 115 debug(DBG_DEBUG, "%s:%d: read_flag = %s (%d)\n", s, l, 116 (ret == LOW_START_CYCLE ? "LOW_START_CYCLE" : ret == 117 MED_START_WORK ? "MED_START_WORK" : ret == 118 HIGH_GRAB_MUTEX ? "HIGH_GRAB_MUTEX" : ret == 119 LOW_DROP_MUTEX ? "LOW_DROP_MUTEX" : ret == 120 END_OF_CYCLE ? "END_OF_CYCLE" : "ERROR"), ret); 121 //debug(DBG_DEBUG, "%s:%d: read_flag = %d\n", s, l, ret); 122 if (use_flag_mutex) 123 pthread_mutex_unlock(&flag_mutex); 124 return ret; 125 } 126 127 void _write_flag(const char *s, int l, phase_t new_flag) 128 { 129 if (use_flag_mutex) 130 pthread_mutex_lock(&flag_mutex); 131 if (phase_flag != END_OF_GAME) { 132 if (new_flag != phase_flag && new_flag != (phase_flag + 1) 133 && !(new_flag == LOW_START_CYCLE 134 && phase_flag == END_OF_CYCLE)) 135 printf("YOU'RE HOSED: new_flag=%d, phase_flag=%d\n", 136 new_flag, phase_flag); 137 phase_flag = new_flag; 138 debug(DBG_DEBUG, "phase_flag: %s set it to %d\n", s, 139 phase_flag); 140 debug(DBG_DEBUG, "%s:%d: write_flag = %s (%d)\n", s, l, 141 (new_flag == 142 LOW_START_CYCLE ? "LOW_START_CYCLE" : new_flag == 143 MED_START_WORK ? "MED_START_WORK" : new_flag == 144 HIGH_GRAB_MUTEX ? "HIGH_GRAB_MUTEX" : new_flag == 145 LOW_DROP_MUTEX ? "LOW_DROP_MUTEX" : new_flag == 146 END_OF_CYCLE ? "END_OF_CYCLE" : "ERROR"), new_flag); 147 //debug(DBG_DEBUG, "%s:%d: write_flag = %d\n", s, l, new_flag); 148 } 149 if (use_flag_mutex) 150 pthread_mutex_unlock(&flag_mutex); 151 } 152 153 #define read_flag(A) _read_flag(__FUNCTION__,__LINE__) 154 #define write_flag(A) _write_flag(__FUNCTION__,__LINE__,A) 155 156 #define while_not_flag(A,B) while (read_flag() != (A) && !thread_quit(B)) 157 158 static nsec_t low_drop_time; 159 void *low_prio_rt_thread(void *arg) 160 { 161 struct thread *t = (struct thread *)arg; 162 while (!thread_quit(t)) { 163 while_not_flag(LOW_START_CYCLE, t) 164 rt_nanosleep(1 * NS_PER_MS); 165 debug(DBG_INFO, "low try mutex\n"); 166 pthread_mutex_lock(&pi_mutex); 167 debug(DBG_INFO, "low grab mutex\n"); 168 write_flag(MED_START_WORK); 169 rt_nanosleep(1 * NS_PER_MS); 170 while_not_flag(LOW_DROP_MUTEX, t) { 171 //printf("!"); fflush(NULL); 172 rt_nanosleep(1); 173 } 174 debug(DBG_INFO, "low drop mutex\n"); 175 low_drop_time = rt_gettime(); 176 pthread_mutex_unlock(&pi_mutex); 177 while_not_flag(END_OF_CYCLE, t) { 178 //printf("@"); fflush(NULL); 179 rt_nanosleep(1 * NS_PER_MS); 180 } 181 } 182 debug(DBG_INFO, "low prio thread finished (flags=%#x)\n", t->flags); 183 return NULL; 184 } 185 186 void *med_prio_thread(void *arg) 187 { 188 static atomic_t m_flag = { 0 }; 189 struct thread *t = (struct thread *)arg; 190 #define MP "\t\t\t" 191 while (!thread_quit(t)) { 192 int i_am_the_one; 193 phase_t f; 194 while_not_flag(MED_START_WORK, t) { 195 //printf("."); fflush(NULL); 196 rt_nanosleep(1 * NS_PER_MS); 197 } 198 if ((i_am_the_one = atomic_inc(&m_flag)) == 1) { 199 debug(DBG_INFO, MP "thread %d writing flag\n", t->id); 200 write_flag(HIGH_GRAB_MUTEX); 201 } 202 203 debug(DBG_DEBUG, MP "ready to start work\n"); 204 write_flag(HIGH_GRAB_MUTEX); 205 while (((f = read_flag()) == HIGH_GRAB_MUTEX 206 || f == LOW_DROP_MUTEX) && !thread_quit(t)) { 207 busy_work_ms(MED_WORK_MS); 208 //printf("-"); fflush(NULL); 209 } 210 debug(DBG_DEBUG, MP "done working -- time to sleep\n"); 211 if (i_am_the_one == 1) { 212 debug(DBG_INFO, MP "thread %d resetting m_flag\n", 213 t->id); 214 atomic_set(0, &m_flag); 215 } 216 } 217 debug(DBG_INFO, "med prio thread finished\n"); 218 return NULL; 219 #undef MP 220 } 221 222 void *high_prio_rt_thread(void *arg) 223 { 224 int delta_us; 225 int i; 226 nsec_t start, now; 227 struct thread *t = (struct thread *)arg; 228 long iterations = (long)t->arg; 229 230 #define HP "\t\t\t\t\t" 231 for (i = 0; i < iterations; i++) { 232 debug(DBG_INFO, "Staring iteration %d\n", i); 233 write_flag(LOW_START_CYCLE); 234 while_not_flag(HIGH_GRAB_MUTEX, t) { 235 //printf("a"); fflush(NULL); 236 rt_nanosleep(10 * NS_PER_MS); 237 } 238 debug(DBG_INFO, HP "high try mutex\n"); 239 write_flag(LOW_DROP_MUTEX); 240 start = rt_gettime(); 241 pthread_mutex_lock(&pi_mutex); 242 now = rt_gettime(); 243 debug(DBG_INFO, HP "high grab mutex\n"); 244 write_flag(END_OF_CYCLE); 245 debug(DBG_INFO, HP "high drop mutex\n"); 246 delta_us = (now - start) / NS_PER_US; 247 if (delta_us > max_delay_us) 248 max_delay_us = delta_us; 249 debug(DBG_WARN, "high prio delay time: %d us\n", delta_us); 250 delta_us = (now - low_drop_time) / NS_PER_US; 251 if (delta_us > max_drop2grab_us) 252 max_drop2grab_us = delta_us; 253 debug(DBG_WARN, "low drop to high grab time: %d us\n", 254 delta_us); 255 pthread_mutex_unlock(&pi_mutex); 256 rt_nanosleep(10 * NS_PER_MS); 257 } 258 all_threads_quit(); 259 write_flag(END_OF_GAME); 260 debug(DBG_INFO, HP "high prio done\n"); 261 #undef HP 262 return NULL; 263 } 264 265 int main(int argc, char *argv[]) 266 { 267 int i, numcpus; 268 setup(); 269 270 rt_init("hfi:x:", parse_args, argc, argv); 271 272 if (!med_threads) { 273 printf 274 ("This test requires that at least NRCPUS medium priority threads run\n"); 275 printf 276 ("If it is run bound to a single CPU, you can specify -x 1\n"); 277 printf("No User input , using default value for NRCPUS"); 278 numcpus = sysconf(_SC_NPROCESSORS_ONLN); 279 med_threads = numcpus; 280 } 281 printf(" flag mutex: %s\n", use_flag_mutex ? "enabled" : "disabled"); 282 printf(" iterations: %ld\n", iterations); 283 printf("med threads: %d\n", med_threads); 284 285 signal(SIGINT, cleanup); 286 signal(SIGQUIT, cleanup); 287 signal(SIGTERM, cleanup); 288 289 max_delay_us = 0; 290 max_drop2grab_us = 0; 291 292 init_pi_mutex(&pi_mutex); 293 294 create_fifo_thread(low_prio_rt_thread, NULL, LOW_PRIO); 295 create_fifo_thread(high_prio_rt_thread, (void *)iterations, HIGH_PRIO); 296 for (i = 0; i < med_threads; i++) { 297 create_fifo_thread(med_prio_thread, NULL, MED_PRIO); 298 } 299 300 while (phase_flag != END_OF_GAME) 301 usleep(100); 302 join_threads(); 303 cleanup(0); 304 305 printf("High priority lock aquisition maximum delay: %dus\n", 306 max_delay_us); 307 printf 308 ("Low priority lock drop to high priority acqusistion time: %dus\n", 309 max_drop2grab_us); 310 printf("\n"); 311 312 return 0; 313 } 314