Home | History | Annotate | Download | only in pi-tests
      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