f7d26a17 |
1 | /* |
2 | * test-read-lock-speed.c |
3343768e |
3 | * |
6087c592 |
4 | * Compare speed of : |
1b485a14 |
5 | * - spin lock / spin unlock |
fe207500 |
6 | * - rwlock read lock |
6087c592 |
7 | * - using a sequence read lock (uncontended) |
f7d26a17 |
8 | * - preempt disable/enable (RCU) |
9 | * |
10 | * Copyright 2009 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> |
11 | * Distributed under GPLv2 |
3343768e |
12 | */ |
13 | |
3343768e |
14 | #include <linux/jiffies.h> |
15 | #include <linux/compiler.h> |
16 | #include <linux/init.h> |
17 | #include <linux/module.h> |
6087c592 |
18 | #include <linux/math64.h> |
8593c315 |
19 | #include <linux/spinlock.h> |
20 | #include <linux/seqlock.h> |
6087c592 |
21 | #include <linux/cpumask.h> |
3343768e |
22 | #include <asm/timex.h> |
23 | #include <asm/system.h> |
24 | |
25 | #define NR_LOOPS 20000 |
26 | |
6087c592 |
27 | #ifndef CONFIG_PREEMPT |
28289207 |
28 | #error "Your kernel should be built with preemption enabled" |
6087c592 |
29 | #endif |
30 | |
31 | #ifdef CONFIG_DEBUG_PREEMPT |
32 | #error "Please disable CONFIG_DEBUG_PREEMPT" |
33 | #endif |
34 | |
35 | #ifdef CONFIG_DEBUG_SPINLOCK |
36 | #error "Please disable CONFIG_DEBUG_SPINLOCK" |
37 | #endif |
38 | |
39 | #ifdef CONFIG_LOCKDEP |
40 | #error "Please disable CONFIG_LOCKDEP" |
41 | #endif |
42 | |
3343768e |
43 | int test_val; |
44 | |
45 | static void do_testbaseline(void) |
46 | { |
6087c592 |
47 | unsigned long flags; |
3343768e |
48 | unsigned int i; |
49 | cycles_t time1, time2, time; |
6087c592 |
50 | u32 rem; |
3343768e |
51 | |
52 | local_irq_save(flags); |
53 | preempt_disable(); |
54 | time1 = get_cycles(); |
55 | for (i = 0; i < NR_LOOPS; i++) { |
56 | asm volatile (""); |
57 | } |
58 | time2 = get_cycles(); |
59 | local_irq_restore(flags); |
60 | preempt_enable(); |
61 | time = time2 - time1; |
62 | |
63 | printk(KERN_ALERT "test results: time for baseline\n"); |
64 | printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS); |
65 | printk(KERN_ALERT "total time: %llu\n", time); |
6087c592 |
66 | time = div_u64_rem(time, NR_LOOPS, &rem); |
3343768e |
67 | printk(KERN_ALERT "-> baseline takes %llu cycles\n", time); |
68 | printk(KERN_ALERT "test end\n"); |
69 | } |
70 | |
71 | static void do_test_spinlock(void) |
72 | { |
8593c315 |
73 | static DEFINE_SPINLOCK(mylock); |
6087c592 |
74 | unsigned long flags; |
3343768e |
75 | unsigned int i; |
76 | cycles_t time1, time2, time; |
6087c592 |
77 | u32 rem; |
3343768e |
78 | |
79 | preempt_disable(); |
8593c315 |
80 | spin_lock_irqsave(&mylock, flags); |
3343768e |
81 | time1 = get_cycles(); |
82 | for (i = 0; i < NR_LOOPS; i++) { |
0c12e051 |
83 | spin_unlock(&mylock); |
84 | spin_lock(&mylock); |
3343768e |
85 | } |
86 | time2 = get_cycles(); |
8593c315 |
87 | spin_unlock_irqrestore(&mylock, flags); |
3343768e |
88 | preempt_enable(); |
89 | time = time2 - time1; |
90 | |
91 | printk(KERN_ALERT "test results: time for spinlock\n"); |
92 | printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS); |
93 | printk(KERN_ALERT "total time: %llu\n", time); |
6087c592 |
94 | time = div_u64_rem(time, NR_LOOPS, &rem); |
3343768e |
95 | printk(KERN_ALERT "-> spinlock takes %llu cycles\n", time); |
96 | printk(KERN_ALERT "test end\n"); |
97 | } |
98 | |
6bc6dad3 |
99 | static void do_test_read_rwlock(void) |
100 | { |
101 | static DEFINE_RWLOCK(mylock); |
102 | unsigned long flags; |
103 | unsigned int i; |
104 | cycles_t time1, time2, time; |
105 | u32 rem; |
106 | |
107 | preempt_disable(); |
108 | local_irq_save(flags); |
109 | read_lock(&mylock); |
110 | time1 = get_cycles(); |
111 | for (i = 0; i < NR_LOOPS; i++) { |
112 | read_unlock(&mylock); |
113 | read_lock(&mylock); |
114 | } |
115 | time2 = get_cycles(); |
116 | read_unlock(&mylock); |
117 | local_irq_restore(flags); |
118 | preempt_enable(); |
119 | time = time2 - time1; |
120 | |
121 | printk(KERN_ALERT "test results: time for read rwlock\n"); |
122 | printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS); |
123 | printk(KERN_ALERT "total time: %llu\n", time); |
124 | time = div_u64_rem(time, NR_LOOPS, &rem); |
125 | printk(KERN_ALERT "-> read rwlock takes %llu cycles\n", time); |
126 | printk(KERN_ALERT "test end\n"); |
127 | } |
128 | |
3343768e |
129 | static void do_test_seqlock(void) |
130 | { |
131 | static seqlock_t test_lock; |
8593c315 |
132 | unsigned long seq; |
6087c592 |
133 | unsigned long flags; |
3343768e |
134 | unsigned int i; |
135 | cycles_t time1, time2, time; |
6087c592 |
136 | u32 rem; |
3343768e |
137 | |
472e3e15 |
138 | local_irq_save(flags); |
3343768e |
139 | time1 = get_cycles(); |
140 | for (i = 0; i < NR_LOOPS; i++) { |
141 | do { |
472e3e15 |
142 | seq = read_seqbegin(&test_lock); |
143 | } while (read_seqretry(&test_lock, seq)); |
3343768e |
144 | } |
145 | time2 = get_cycles(); |
3343768e |
146 | time = time2 - time1; |
472e3e15 |
147 | local_irq_restore(flags); |
3343768e |
148 | |
149 | printk(KERN_ALERT "test results: time for seqlock\n"); |
150 | printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS); |
151 | printk(KERN_ALERT "total time: %llu\n", time); |
6087c592 |
152 | time = div_u64_rem(time, NR_LOOPS, &rem); |
3343768e |
153 | printk(KERN_ALERT "-> seqlock takes %llu cycles\n", time); |
154 | printk(KERN_ALERT "test end\n"); |
155 | } |
156 | |
157 | /* |
6087c592 |
158 | * Note : This test _should_ trigger lockdep errors due to preemption |
159 | * disabling/enabling within irq off section. Given we are only interested in |
160 | * having the most precise measurement for preemption disable/enable, we don't |
161 | * care about this. |
3343768e |
162 | */ |
163 | static void do_test_preempt(void) |
164 | { |
6087c592 |
165 | unsigned long flags; |
3343768e |
166 | unsigned int i; |
167 | cycles_t time1, time2, time; |
6087c592 |
168 | u32 rem; |
3343768e |
169 | |
170 | local_irq_save(flags); |
171 | preempt_disable(); |
172 | time1 = get_cycles(); |
173 | for (i = 0; i < NR_LOOPS; i++) { |
3343768e |
174 | preempt_disable(); |
54e7c224 |
175 | preempt_enable(); |
3343768e |
176 | } |
177 | time2 = get_cycles(); |
178 | preempt_enable(); |
179 | time = time2 - time1; |
180 | local_irq_restore(flags); |
181 | |
f7d26a17 |
182 | printk(KERN_ALERT |
183 | "test results: time for preempt disable/enable pairs\n"); |
3343768e |
184 | printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS); |
185 | printk(KERN_ALERT "total time: %llu\n", time); |
6087c592 |
186 | time = div_u64_rem(time, NR_LOOPS, &rem); |
3343768e |
187 | printk(KERN_ALERT "-> preempt disable/enable pair takes %llu cycles\n", |
188 | time); |
189 | printk(KERN_ALERT "test end\n"); |
190 | } |
191 | |
192 | static int ltt_test_init(void) |
193 | { |
194 | printk(KERN_ALERT "test init\n"); |
195 | |
6087c592 |
196 | printk(KERN_ALERT "Number of active CPUs : %d\n", num_online_cpus()); |
3343768e |
197 | do_testbaseline(); |
198 | do_test_spinlock(); |
6bc6dad3 |
199 | do_test_read_rwlock(); |
3343768e |
200 | do_test_seqlock(); |
201 | do_test_preempt(); |
202 | return -EAGAIN; /* Fail will directly unload the module */ |
203 | } |
204 | |
205 | static void ltt_test_exit(void) |
206 | { |
207 | printk(KERN_ALERT "test exit\n"); |
208 | } |
209 | |
210 | module_init(ltt_test_init) |
211 | module_exit(ltt_test_exit) |
212 | |
213 | MODULE_LICENSE("GPL"); |
214 | MODULE_AUTHOR("Mathieu Desnoyers"); |
f7d26a17 |
215 | MODULE_DESCRIPTION("Test read lock speed"); |