2 * Copyright (C) 2005, Ingo Molnar
4 * time-warp-test.c: check TSC synchronity on x86 CPUs. Also detects
5 * gettimeofday()-level time warps.
7 * Compile with: gcc -Wall -O2 -o time-warp-test time-warp-test.c -lrt
14 #include <linux/unistd.h>
20 #include <sys/types.h>
28 #include <sys/socket.h>
39 #if !TEST_TSC && !TEST_TOD && !TEST_CLOCK
40 # error this setting makes no sense ...
44 # define Printf(x...) printf(x)
46 # define Printf(x...) do { } while (0)
50 * Shared locks and variables between the test tasks:
58 SHARED_WORST_TOD
= 10,
59 SHARED_WORST_CLOCK
= 12,
60 SHARED_NR_TSC_LOOPS
= 14,
61 SHARED_NR_TSC_WARPS
= 16,
62 SHARED_NR_TOD_LOOPS
= 18,
63 SHARED_NR_TOD_WARPS
= 20,
64 SHARED_NR_CLOCK_LOOPS
= 22,
65 SHARED_NR_CLOCK_WARPS
= 24,
69 #define SHARED(x) (*(shared + SHARED_##x))
70 #define SHARED_LL(x) (*(long long *)(shared + SHARED_##x))
72 #define BUG_ON(c) assert(!(c))
74 typedef unsigned long long cycles_t
;
75 typedef unsigned long long usecs_t
;
76 typedef unsigned long long u64
;
79 #define DECLARE_ARGS(val, low, high) unsigned low, high
80 #define EAX_EDX_VAL(val, low, high) ((low) | ((u64)(high) << 32))
81 #define EAX_EDX_ARGS(val, low, high) "a" (low), "d" (high)
82 #define EAX_EDX_RET(val, low, high) "=a" (low), "=d" (high)
84 #define DECLARE_ARGS(val, low, high) unsigned long long val
85 #define EAX_EDX_VAL(val, low, high) (val)
86 #define EAX_EDX_ARGS(val, low, high) "A" (val)
87 #define EAX_EDX_RET(val, low, high) "=A" (val)
90 static inline unsigned long long __rdtscll(void)
92 DECLARE_ARGS(val
, low
, high
);
94 asm volatile("cpuid; rdtsc" : EAX_EDX_RET(val
, low
, high
));
96 return EAX_EDX_VAL(val
, low
, high
);
99 #define rdtscll(val) do { (val) = __rdtscll(); } while (0)
105 gettimeofday(&tv, NULL); \
106 (val) = tv.tv_sec * 1000000ULL + tv.tv_usec; \
109 #define rdclock(val) \
111 struct timespec ts; \
113 clock_gettime(CLOCK_MONOTONIC, &ts); \
114 (val) = ts.tv_sec * 1000000000ULL + ts.tv_nsec; \
117 static unsigned long *setup_shared_var(void)
119 char zerobuff
[4096] = { 0, };
123 fd
= creat(".tmp_mmap", 0700);
127 fd
= open(".tmp_mmap", O_RDWR
|O_CREAT
|O_TRUNC
, 00666);
129 ret
= write(fd
, zerobuff
, 4096);
132 buf
= (void *)mmap(0, 4096, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
133 BUG_ON(buf
== (void *)-1);
141 static inline void lock(unsigned long *flag
)
144 __asm__
__volatile__(
145 "1: lock; btsl $0,%0\n"
147 : "=g"(*flag
) : : "memory");
149 __asm__
__volatile__(
150 "1: lock; btsl $0,%0\n\t"
157 : "+m"(*flag
) : : "memory");
161 static inline void unlock(unsigned long *flag
)
164 __asm__
__volatile__(
166 : "=g"(*flag
) :: "memory");
167 __asm__
__volatile__("rep; nop");
169 __asm__
__volatile__("movl $0,%0; rep; nop" : "=g"(*flag
) :: "memory");
173 static void print_status(unsigned long *shared
)
175 const char progress
[] = "\\|/-";
177 static unsigned long long sum_tsc_loops
, sum_tod_loops
, sum_clock_loops
,
179 static unsigned int count1
, count2
;
180 static usecs_t prev_tod
;
193 if (abs(tod
- prev_tod
) < 100000ULL)
196 sum_tod
+= tod
- prev_tod
;
197 sum_tsc_loops
+= SHARED_LL(NR_TSC_LOOPS
);
198 sum_tod_loops
+= SHARED_LL(NR_TOD_LOOPS
);
199 sum_clock_loops
+= SHARED_LL(NR_CLOCK_LOOPS
);
200 SHARED_LL(NR_TSC_LOOPS
) = 0;
201 SHARED_LL(NR_TOD_LOOPS
) = 0;
202 SHARED_LL(NR_CLOCK_LOOPS
) = 0;
205 printf(" | TSC: %.2fus, fail:%ld",
206 (double)sum_tod
/(double)sum_tsc_loops
,
207 SHARED(NR_TSC_WARPS
));
210 printf(" | TOD: %.2fus, fail:%ld",
211 (double)sum_tod
/(double)sum_tod_loops
,
212 SHARED(NR_TOD_WARPS
));
215 printf(" | CLK: %.2fus, fail:%ld",
216 (double)sum_tod
/(double)sum_clock_loops
,
217 SHARED(NR_CLOCK_WARPS
));
221 printf(" %c\r", progress
[count2
& 3]);
225 static inline void test_TSC(unsigned long *shared
)
235 SHARED_LL(NR_TSC_LOOPS
)++;
236 unlock(&SHARED(LOCK
));
241 SHARED(NR_TSC_WARPS
)++;
242 if (delta
< SHARED_LL(WORST_TSC
)) {
243 SHARED_LL(WORST_TSC
) = delta
;
244 fprintf(stderr
, "\rnew TSC-warp maximum: %9Ld cycles, %016Lx -> %016Lx\n",
247 unlock(&SHARED(LOCK
));
249 if (!((unsigned long)t0
& 31))
250 asm volatile ("rep; nop");
254 static inline void test_TOD(unsigned long *shared
)
264 SHARED_LL(NR_TOD_LOOPS
)++;
265 unlock(&SHARED(LOCK
));
270 SHARED(NR_TOD_WARPS
)++;
271 if (delta
< SHARED_LL(WORST_TOD
)) {
272 SHARED_LL(WORST_TOD
) = delta
;
273 fprintf(stderr
, "\rnew TOD-warp maximum: %9Ld usecs, %016Lx -> %016Lx\n",
276 unlock(&SHARED(LOCK
));
281 static inline void test_CLOCK(unsigned long *shared
)
289 T0
= SHARED_LL(CLOCK
);
290 SHARED_LL(CLOCK
) = T1
;
291 SHARED_LL(NR_CLOCK_LOOPS
)++;
292 unlock(&SHARED(LOCK
));
297 SHARED(NR_CLOCK_WARPS
)++;
298 if (delta
< SHARED_LL(WORST_CLOCK
)) {
299 SHARED_LL(WORST_CLOCK
) = delta
;
300 fprintf(stderr
, "\rnew CLOCK-warp maximum: %9Ld nsecs, %016Lx -> %016Lx\n",
303 unlock(&SHARED(LOCK
));
308 int main(int argc
, char **argv
)
311 unsigned long *shared
;
312 unsigned long cpus
, tasks
;
314 cpus
= system("exit `grep ^processor /proc/cpuinfo | wc -l`");
315 cpus
= WEXITSTATUS(cpus
);
320 "usage: tsc-sync-test <threads>\n");
324 tasks
= atol(argv
[1]);
330 printf("%ld CPUs, running %ld parallel test-tasks.\n", cpus
, tasks
);
331 printf("checking for time-warps via:\n"
333 "- read time stamp counter (RDTSC) instruction (cycle resolution)\n"
336 "- gettimeofday (TOD) syscall (usec resolution)\n"
339 "- clock_gettime(CLOCK_MONOTONIC) syscall (nsec resolution)\n"
344 shared
= setup_shared_var();
346 for (i
= 1; i
< tasks
; i
++) {
355 for (i
= 0; i
< 10; i
++)
357 for (i
= 0; i
< 10; i
++)
359 for (i
= 0; i
< 10; i
++)
363 print_status(shared
);