63787240 |
1 | /* test-tsc.c |
2 | * |
3 | * Test TSC synchronization |
4 | */ |
5 | |
6 | |
7 | #include <linux/module.h> |
8 | #include <linux/timer.h> |
9 | #include <asm/timex.h> |
10 | #include <linux/jiffies.h> |
11 | #include <linux/cpu.h> |
12 | #include <linux/kthread.h> |
13 | |
14 | #define MAX_CYCLES_DELTA 1000ULL |
15 | |
16 | static DEFINE_PER_CPU(cycles_t, tsc_count) = 0; |
17 | |
18 | static atomic_t wait_sync; |
19 | static atomic_t wait_end_sync; |
20 | |
21 | /* Mark it noinline so we make sure it is not unrolled. |
22 | * Wait until value is reached. */ |
23 | static noinline void tsc_barrier(int value) |
24 | { |
25 | sync_core(); |
26 | atomic_dec(&wait_sync); |
27 | do { |
28 | barrier(); |
29 | } while(unlikely(atomic_read(&wait_sync) > value)); |
30 | __get_cpu_var(tsc_count) = get_cycles_sync(); |
31 | } |
32 | |
33 | /* worker thread called on each CPU. |
34 | * First wait with interrupts enabled, then wait with interrupt disabled, |
35 | * for precision. We are already bound to one CPU. */ |
36 | static void test_sync(void *arg) |
37 | { |
38 | unsigned long flags; |
39 | |
40 | local_irq_save(flags); |
41 | tsc_barrier(2); /* Make sure the instructions are in I-CACHE */ |
42 | tsc_barrier(0); |
43 | atomic_dec(&wait_end_sync); |
44 | do { |
45 | barrier(); |
46 | } while(unlikely(atomic_read(&wait_end_sync))); |
47 | local_irq_restore(flags); |
48 | } |
49 | |
50 | /* Do loops (making sure no unexpected event changes the timing), keep the |
51 | * best one. The result of each loop is the highest tsc delta between the |
52 | * master CPU and the slaves. */ |
53 | static int test_synchronization(void) |
54 | { |
55 | int cpu, master; |
56 | cycles_t max_diff = 0, diff, best_loop, worse_loop = 0; |
57 | int i; |
58 | |
59 | preempt_disable(); |
60 | master = smp_processor_id(); |
61 | for_each_online_cpu(cpu) { |
62 | if (master == cpu) |
63 | continue; |
64 | best_loop = ULLONG_MAX; |
65 | for (i = 0; i < 10; i++) { |
66 | /* Each CPU (master and slave) must decrement the |
67 | * wait_sync value twice (one for priming in cache) */ |
68 | atomic_set(&wait_sync, 4); |
69 | atomic_set(&wait_end_sync, 2); |
70 | smp_call_function_single(cpu, test_sync, NULL, 1, 0); |
71 | test_sync(NULL); |
72 | diff = abs(per_cpu(tsc_count, cpu) |
73 | - per_cpu(tsc_count, master)); |
74 | best_loop = min(best_loop, diff); |
75 | worse_loop = max(worse_loop, diff); |
76 | } |
77 | max_diff = max(best_loop, max_diff); |
78 | } |
79 | preempt_enable(); |
80 | if (max_diff >= MAX_CYCLES_DELTA) { |
81 | printk("Synchronization tsc : %llu cycles delta is over " |
82 | "threshold %llu\n", max_diff, MAX_CYCLES_DELTA); |
83 | printk("Synchronization tsc (worse loop) : %llu cycles delta\n", |
84 | worse_loop); |
85 | } |
86 | return max_diff < MAX_CYCLES_DELTA; |
87 | } |
88 | |
89 | static int __init test_init(void) |
90 | { |
91 | int ret; |
92 | |
93 | ret = test_synchronization(); |
94 | return 0; |
95 | } |
96 | |
97 | static void __exit test_exit(void) |
98 | { |
99 | } |
100 | |
101 | module_init(test_init); |
102 | module_exit(test_exit); |
103 | |
104 | MODULE_LICENSE("GPL"); |
105 | MODULE_AUTHOR("Mathieu Desnoyers"); |
106 | MODULE_DESCRIPTION("sync tsc test"); |
107 | |