Home | History | Annotate | Download | only in hrtimer-prio
      1 /******************************************************************************
      2  *
      3  *   Copyright  International Business Machines  Corp., 2007, 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  *     hrtimer-prio.c
     21  *
     22  * DESCRIPTION
     23  *     Test the latency of hrtimers under rt load.
     24  *     The busy_threads should run at a priority higher than the system
     25  *     softirq_hrtimer, but lower than the timer_thread.  The timer_thread
     26  *     measure the time it takes to return from a nanosleep call.  If the
     27  *     lower priority threads can increase the latency of the higher
     28  *     priority thread, it is considered a failure.
     29  *
     30  * USAGE:
     31  *     Use run_auto.sh script in current directory to build and run test.
     32  *
     33  * AUTHOR
     34  *      Darren Hart <dvhltc (at) us.ibm.com>
     35  *
     36  * HISTORY
     37  *      2007-Aug-08:      Initial version by Darren Hart <dvhltc (at) us.ibm.com>
     38  *
     39  *      This line has to be added to avoid a stupid CVS problem
     40  *****************************************************************************/
     41 
     42 #include <stdio.h>
     43 #include <stdlib.h>
     44 #include <math.h>
     45 #include <librttest.h>
     46 #include <libstats.h>
     47 
     48 #define DEF_MED_PRIO 60		// (softirqd-hrtimer,98)
     49 #define DEF_ITERATIONS 10000
     50 #define HIST_BUCKETS 100
     51 #define DEF_BUSY_TIME 10	// Duration of busy work in milliseconds
     52 #define DEF_SLEEP_TIME 10000	// Duration of nanosleep in nanoseconds
     53 #define DEF_CRITERIA 10		// maximum timer latency in microseconds
     54 
     55 static int med_prio = DEF_MED_PRIO;
     56 static int high_prio;
     57 static int busy_time = DEF_BUSY_TIME;
     58 static int iterations = DEF_ITERATIONS;
     59 static int busy_threads;
     60 
     61 static stats_container_t dat;
     62 static stats_record_t rec;
     63 static atomic_t busy_threads_started;
     64 static unsigned long min_delta;
     65 static unsigned long max_delta;
     66 
     67 void usage(void)
     68 {
     69 	rt_help();
     70 	printf("hrtimer-prio specific options:\n");
     71 	printf("  -t#	   #:busy work time in ms, defaults to %d ms\n",
     72 	       DEF_BUSY_TIME);
     73 	printf("  -i#	   #:number of iterations, defaults to %d\n",
     74 	       DEF_ITERATIONS);
     75 	printf("  -n#	   #:number of busy threads, defaults to NR_CPUS*2\n");
     76 	printf
     77 	    ("  -f#	   #:rt fifo priority of busy threads (1,98), defaults to %d\n",
     78 	     DEF_MED_PRIO);
     79 	printf
     80 	    ("  -m#	   #:maximum timer latency in microseconds, defaults to %d\n",
     81 	     DEF_CRITERIA);
     82 }
     83 
     84 int parse_args(int c, char *v)
     85 {
     86 
     87 	int handled = 1;
     88 	switch (c) {
     89 	case 'h':
     90 		usage();
     91 		exit(0);
     92 	case 't':
     93 		busy_time = atoi(v);
     94 		break;
     95 	case 'n':
     96 		busy_threads = atoi(v);
     97 		break;
     98 	case 'f':
     99 		med_prio = MIN(atoi(v), 98);
    100 		break;
    101 	case 'i':
    102 		iterations = atoi(v);
    103 		if (iterations < 100) {
    104 			fprintf(stderr,
    105 				"Number of iterations cannot be less than 100.\n");
    106 			exit(1);
    107 		}
    108 		break;
    109 	default:
    110 		handled = 0;
    111 		break;
    112 	}
    113 	return handled;
    114 }
    115 
    116 void *busy_thread(void *thread)
    117 {
    118 	atomic_inc(&busy_threads_started);
    119 	while (1) {
    120 		busy_work_ms(busy_time);
    121 		sched_yield();
    122 	}
    123 	return NULL;
    124 }
    125 
    126 void *timer_thread(void *thread)
    127 {
    128 	int i;
    129 	nsec_t start, end;
    130 	unsigned long delta_us;
    131 	while (atomic_get(&busy_threads_started) < busy_threads) {
    132 		rt_nanosleep(10000);
    133 	}
    134 	printf("All Busy Threads started, commencing test\n");	// FIXME: use debug infrastructure
    135 	max_delta = 0;
    136 	for (i = 0; i < iterations; i++) {
    137 		start = rt_gettime();
    138 		rt_nanosleep(DEF_SLEEP_TIME);
    139 		end = rt_gettime();
    140 		delta_us =
    141 		    ((unsigned long)(end - start) - DEF_SLEEP_TIME) / NS_PER_US;
    142 		rec.x = i;
    143 		rec.y = delta_us;
    144 		stats_container_append(&dat, rec);
    145 		max_delta = MAX(max_delta, delta_us);
    146 		min_delta = (i == 0) ? delta_us : MIN(min_delta, delta_us);
    147 	}
    148 	return NULL;
    149 }
    150 
    151 int main(int argc, char *argv[])
    152 {
    153 	int ret = 1;
    154 	int b;
    155 	float avg_delta;
    156 	int t_id;
    157 	setup();
    158 	busy_threads = 2 * sysconf(_SC_NPROCESSORS_ONLN);	// default busy_threads
    159 	pass_criteria = DEF_CRITERIA;
    160 	rt_init("f:i:jhn:t:", parse_args, argc, argv);
    161 	high_prio = med_prio + 1;
    162 
    163 	// Set main()'s prio to one above the timer_thread so it is sure to not
    164 	// be starved
    165 	if (set_priority(high_prio + 1) < 0) {
    166 		printf("Failed to set main()'s priority to %d\n",
    167 		       high_prio + 1);
    168 		exit(1);
    169 	}
    170 
    171 	printf("\n-------------------------------------------\n");
    172 	printf("High Resolution Timer Priority (Starvation)\n");
    173 	printf("-------------------------------------------\n\n");
    174 	printf("Running %d iterations\n", iterations);
    175 	printf("Running with %d busy threads\n", busy_threads);
    176 	printf("Busy thread work time: %d\n", busy_time);
    177 	printf("Busy thread priority: %d\n", med_prio);
    178 	printf("Timer thread priority: %d\n", high_prio);
    179 
    180 	stats_container_t hist;
    181 	stats_quantiles_t quantiles;
    182 	if (stats_container_init(&dat, iterations)) {
    183 		printf("Cannot init stat containers for dat\n");
    184 		exit(1);
    185 	}
    186 	if (stats_container_init(&hist, HIST_BUCKETS)) {
    187 		printf("Cannot init stat containers for hist\n");
    188 		exit(1);
    189 	}
    190 	if (stats_quantiles_init(&quantiles, (int)log10(iterations))) {
    191 		printf("Cannot init stat quantiles\n");
    192 		exit(1);
    193 	}
    194 
    195 	t_id = create_fifo_thread(timer_thread, NULL, high_prio);
    196 	if (t_id == -1) {
    197 		printf("Failed to create timer thread\n");
    198 		exit(1);
    199 	}
    200 	for (b = 0; b < busy_threads; b++) {
    201 		if (create_fifo_thread(busy_thread, NULL, med_prio) < 0) {
    202 			printf("Failed to create a busy thread\n");
    203 			exit(1);
    204 		}
    205 	}
    206 	join_thread(t_id);
    207 
    208 	avg_delta = stats_avg(&dat);
    209 	stats_hist(&hist, &dat);
    210 	stats_container_save("samples",
    211 			     "High Resolution Timer Latency Scatter Plot",
    212 			     "Iteration", "Latency (us)", &dat, "points");
    213 	stats_container_save("hist", "High Resolution Timer Latency Histogram",
    214 			     "Latency (us)", "Samples", &hist, "steps");
    215 
    216 	if (max_delta <= pass_criteria)
    217 		ret = 0;
    218 
    219 	printf("Minimum: %ld us\n", min_delta);
    220 	printf("Maximum: %ld us\n", max_delta);
    221 	printf("Average: %f us\n", avg_delta);
    222 	printf("Standard Deviation: %f\n", stats_stddev(&dat));
    223 	printf("Quantiles:\n");
    224 	stats_quantiles_calc(&dat, &quantiles);
    225 	stats_quantiles_print(&quantiles);
    226 	printf("\nCriteria: Maximum wakeup latency < %lu us\n",
    227 	       (unsigned long)pass_criteria);
    228 	printf("Result: %s\n", ret ? "FAIL" : "PASS");
    229 
    230 	return ret;
    231 }
    232