Home | History | Annotate | Download | only in src
      1 #define _GNU_SOURCE
      2 
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <stdint.h>
      6 #include <stdarg.h>
      7 #include <string.h>
      8 #include <getopt.h>
      9 #include <pthread.h>
     10 #include <errno.h>
     11 #include "sched.h"
     12 
     13 
     14 #define MAX_CPUS		32
     15 #define	DEFAULT_THRESHOLD	500	/* default maximum TSC skew	*/
     16 
     17 
     18 char	*program;
     19 long	threshold	= DEFAULT_THRESHOLD;
     20 int	silent		= 0;
     21 int	verbose		= 0;
     22 
     23 
     24 struct option options[] = {
     25 	{ "cpus",	required_argument,	0, 	'c'	},
     26 	{ "help",	no_argument,		0, 	'h'	},
     27 	{ "silent",	no_argument,		0, 	's'	},
     28 	{ "threshold",	required_argument,	0, 	't'	},
     29 	{ "verbose",	no_argument,		0, 	'v'	},
     30 	{ 0,	0,	0,	0 }
     31 };
     32 
     33 
     34 void usage(void)
     35 {
     36 	printf("usage: %s [-hsv] [-c <cpu_set>] [-t threshold]\n", program);
     37 }
     38 
     39 
     40 void help(void)
     41 {
     42 	usage();
     43 	printf("check TSC synchronization between CPUs\n");
     44 	printf("  -c,--cpus        set of cpus to test (default: all)\n");
     45 	printf("  -h,--help        show this message\n");
     46 	printf("  -s,--silent      no output if test is successful\n");
     47 	printf("  -t,--threshold   TSC skew threshold (default: %d cycles)\n",
     48 		DEFAULT_THRESHOLD);
     49 	printf("  -v,--verbose     verbose output\n");
     50 }
     51 
     52 
     53 void error(int err, const char *fmt, ...)
     54 {
     55 	va_list	ap;
     56 
     57 	fprintf(stderr, "%s: ", program);
     58 	va_start(ap, fmt);
     59 	vfprintf(stderr, fmt, ap);
     60 	va_end(ap);
     61 
     62 	if (err)
     63 		fprintf(stderr, ": %s\n", strerror(err));
     64 	putc('\n', stderr);
     65 }
     66 
     67 
     68 /*
     69  * parse a string containing a comma separated list of ranges
     70  * of cpu numbers such as: "0,2,4-7" into a cpu_set_t
     71  */
     72 int parse_cpu_set(const char *s, cpu_set_t *cpus)
     73 {
     74 	CPU_ZERO(cpus);
     75 
     76 	while (*s) {
     77 		char	*next;
     78 		int	cpu;
     79 		int	start, end;
     80 
     81 		start = end = (int)strtol(s, &next, 0);
     82 		if (s == next)
     83 			break;
     84 		s = next;
     85 
     86 		if (*s == '-') {
     87 			++s;
     88 			end = (int)strtol(s, &next, 0);
     89 			if (s == next)
     90 				break;
     91 			s = next;
     92 		}
     93 
     94 		if (*s == ',')
     95 			++s;
     96 
     97 		if (start < 0 || start >= CPU_SETSIZE) {
     98 			error(0, "bad cpu number '%d' in cpu set", start);
     99 			return 1;
    100 		}
    101 
    102 		if (end < 0 || end >= CPU_SETSIZE) {
    103 			error(0, "bad cpu number '%d' in cpu set", end);
    104 			return 1;
    105 		}
    106 
    107 		if (end < start) {
    108 			error(0, "bad cpu range '%d-%d' in cpu set",
    109 				start, end);
    110 			return 1;
    111 		}
    112 
    113 		for (cpu = start; cpu <= end; ++cpu)
    114 			CPU_SET(cpu, cpus);
    115 
    116 	}
    117 
    118 	if (*s) {
    119 		error(0, "unexpected character '%c' in cpu set", *s);
    120 		return 1;
    121 	}
    122 
    123 	return 0;
    124 }
    125 
    126 
    127 #define	CACHE_LINE_SIZE	256
    128 typedef union state {
    129 	int	state;
    130 	char	pad[CACHE_LINE_SIZE];
    131 } state_t;
    132 
    133 #define barrier()	__asm__ __volatile__("" : : : "memory")
    134 
    135 static void inline set_state(state_t *s, int v)
    136 {
    137 	s->state = v;
    138 }
    139 
    140 static void inline wait_for_state(state_t *s, int v)
    141 {
    142 	while (s->state != v)
    143 		barrier();
    144 }
    145 
    146 #if defined(__x86_64__)
    147 static inline uint64_t rdtsc(void)
    148 {
    149 	uint32_t	tsc_lo, tsc_hi;
    150 
    151 	__asm__ __volatile__("rdtsc" : "=a" (tsc_lo), "=d" (tsc_hi));
    152 
    153 	return ((uint64_t)tsc_hi << 32) | tsc_lo;
    154 }
    155 #else
    156 static inline uint64_t rdtsc(void)
    157 {
    158 	uint64_t	tsc;
    159 
    160 	__asm__ __volatile__("rdtsc" : "=A" (tsc));
    161 
    162 	return tsc;
    163 }
    164 #endif
    165 
    166 #define	READY	1
    167 #define	DONE	2
    168 #define	ERROR	3
    169 
    170 state_t		master;
    171 state_t		slave;
    172 
    173 int64_t		slave_tsc;
    174 int		slave_cpu;
    175 
    176 
    177 int set_cpu_affinity(int cpu)
    178 {
    179 	cpu_set_t cpus;
    180 
    181 	CPU_ZERO(&cpus);
    182 	CPU_SET(cpu, &cpus);
    183 	if (sched_setaffinity(0, sizeof cpus, &cpus) < 0) {
    184 		error(errno, "sched_setaffinity() failed for CPU %d", cpu);
    185 		return -1;
    186 	}
    187 	return 0;
    188 }
    189 
    190 #define NUM_ITERS	10
    191 
    192 int64_t
    193 tsc_delta(int cpu_a, int cpu_b)
    194 {
    195 	uint64_t	best_t0	= 0;
    196 	uint64_t	best_t1	= ~0ULL;
    197 	uint64_t	best_tm	= 0;
    198 	int64_t		delta;
    199 	uint64_t	t0, t1, tm;
    200 	int		i;
    201 
    202 	if (verbose)
    203 		printf("CPU %d - CPU %d\n", cpu_a, cpu_b);
    204 
    205 	if (set_cpu_affinity(cpu_a) < 0)
    206 		return -1;
    207 
    208 	slave_cpu = cpu_b;
    209 
    210 	for (i = 0; i < NUM_ITERS; i++) {
    211 
    212 		set_state(&master, READY);
    213 
    214 		wait_for_state(&slave, READY);
    215 
    216 		t0 = rdtsc();
    217 		set_state(&master, DONE);
    218 		wait_for_state(&slave, DONE);
    219 		t1 = rdtsc();
    220 
    221 		if ((t1 - t0) < (best_t1 - best_t0)) {
    222 			best_t0 = t0;
    223 			best_t1 = t1;
    224 			best_tm = slave_tsc;
    225 		}
    226 		if (verbose)
    227 			printf("loop %2d: roundtrip = %5Ld\n", i, t1 - t0);
    228 	}
    229 
    230 	delta = (best_t0/2 + best_t1/2 + (best_t0 & best_t1 & 1)) - best_tm;
    231 
    232 	if (!silent)
    233 		printf("CPU %d - CPU %d = % 5Ld\n", cpu_a, cpu_b, delta);
    234 
    235 	return delta;
    236 }
    237 
    238 
    239 void *
    240 slave_thread(void *arg)
    241 {
    242 	int	current_cpu = -1;
    243 
    244 	for(;;) {
    245 
    246 		wait_for_state(&master, READY);
    247 
    248 		if (slave_cpu < 0) {
    249 			return NULL;
    250 		}
    251 
    252 		if (slave_cpu != current_cpu) {
    253 
    254 			if (set_cpu_affinity(slave_cpu) < 0) {
    255 				set_state(&slave, ERROR);
    256 				return NULL;
    257 			}
    258 
    259 			current_cpu = slave_cpu;
    260 		}
    261 
    262 		set_state(&slave, READY);
    263 
    264 		wait_for_state(&master, DONE);
    265 
    266 		slave_tsc = rdtsc();
    267 
    268 		set_state(&slave, DONE);
    269 	}
    270 	return NULL;
    271 }
    272 
    273 
    274 int
    275 check_tsc(cpu_set_t *cpus)
    276 {
    277 	int		cpu_a, cpu_b;
    278 	int64_t		delta;
    279 	int		err	= 0;
    280 	pthread_t	thread;
    281 
    282 	if ((err = pthread_create(&thread, NULL, slave_thread, NULL))) {
    283 		error(err, "pthread_create_failed");
    284 		return -1;
    285 	}
    286 
    287 
    288 	for (cpu_a = 0; cpu_a < MAX_CPUS; cpu_a++) {
    289 		if (!CPU_ISSET(cpu_a, cpus))
    290 			continue;
    291 
    292 		for (cpu_b = 0; cpu_b < MAX_CPUS; cpu_b++) {
    293 			if (!CPU_ISSET(cpu_b, cpus) || cpu_a == cpu_b)
    294 				continue;
    295 
    296 			delta = tsc_delta(cpu_a, cpu_b);
    297 
    298 			if (llabs(delta) > threshold) {
    299 				++err;
    300 			}
    301 		}
    302 	}
    303 
    304 	/*
    305 	 * tell the slave thread to exit
    306 	 */
    307 	slave_cpu = -1;
    308 	set_state(&master, READY);
    309 
    310 	pthread_join(thread, NULL);
    311 
    312 	return err;
    313 }
    314 
    315 
    316 int
    317 main(int argc, char *argv[])
    318 {
    319 	int		c;
    320 	cpu_set_t	cpus;
    321 	int		errs	= 0;
    322 	extern int	optind;
    323 	extern char	*optarg;
    324 
    325 	if ((program = strrchr(argv[0], '/')) != NULL)
    326 		++program;
    327 	else
    328 		program = argv[0];
    329 
    330 	/*
    331 	 * default to checking all cpus
    332 	 */
    333 	for (c = 0; c < MAX_CPUS; c++) {
    334 		CPU_SET(c, &cpus);
    335 	}
    336 
    337 	while ((c = getopt_long(argc, argv, "c:hst:v", options, NULL)) != EOF) {
    338 		switch (c) {
    339 			case 'c':
    340 				if (parse_cpu_set(optarg, &cpus) != 0)
    341 					++errs;
    342 				break;
    343 			case 'h':
    344 				help();
    345 				exit(0);
    346 			case 's':
    347 				++silent;
    348 				break;
    349 			case 't':
    350 				threshold = strtol(optarg, NULL, 0);
    351 				break;
    352 			case 'v':
    353 				++verbose;
    354 				break;
    355 			default:
    356 				++errs;
    357 				break;
    358 		}
    359 	}
    360 
    361 	if (errs || optind < argc) {
    362 		usage();
    363 		exit(1);
    364 	}
    365 
    366 	/*
    367 	 * limit the set of CPUs to the ones that are currently available
    368 	 * (Note that on some kernel versions sched_setaffinity() will fail
    369 	 * if you specify CPUs that are not currently online so we ignore
    370 	 * the return value and hope for the best)
    371 	 */
    372 	sched_setaffinity(0, sizeof cpus, &cpus);
    373 	if (sched_getaffinity(0, sizeof cpus, &cpus) < 0) {
    374 		error(errno, "sched_getaffinity() failed");
    375 		exit(1);
    376 	}
    377 
    378 	errs = check_tsc(&cpus);
    379 
    380 	if (!silent) {
    381 		printf("%s\n", errs ? "FAIL" : "PASS");
    382 	}
    383 
    384 	return errs ? EXIT_FAILURE : EXIT_SUCCESS;
    385 }
    386