1 /******************************************************************************/
2 /* */
3 /* Copyright (c) Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>, 2009 */
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 /* usage :
20 make
21 insmod test-cmpxchg-nolock.ko
22 insmod: error inserting 'test-cmpxchg-nolock.ko':
23 -1 Resource temporarily unavailable
24 dmesg (see dmesg output) */
25 /******************************************************************************/
26
27 /* test-cmpxchg-nolock.c
28 *
29 * Compare local cmpxchg with irq disable / enable.
30 */
31
32 #include <linux/jiffies.h>
33 #include <linux/compiler.h>
34 #include <linux/init.h>
35 #include <linux/module.h>
36 #include <linux/math64.h>
37 #include <asm/timex.h>
38 #include <asm/system.h>
39
40 #define NR_LOOPS 20000
41
42 int test_val;
43
do_testbaseline(void)44 static void do_testbaseline(void)
45 {
46 unsigned long flags;
47 unsigned int i;
48 cycles_t time1, time2, time;
49 u32 rem;
50
51 local_irq_save(flags);
52 preempt_disable();
53 time1 = get_cycles();
54 for (i = 0; i < NR_LOOPS; i++) {
55 asm volatile ("");
56 }
57 time2 = get_cycles();
58 local_irq_restore(flags);
59 preempt_enable();
60 time = time2 - time1;
61
62 printk(KERN_ALERT "test results: time for baseline\n");
63 printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS);
64 printk(KERN_ALERT "total time: %llu\n", time);
65 time = div_u64_rem(time, NR_LOOPS, &rem);
66 printk(KERN_ALERT "-> baseline takes %llu cycles\n", time);
67 printk(KERN_ALERT "test end\n");
68 }
69
do_test_sync_cmpxchg(void)70 static void do_test_sync_cmpxchg(void)
71 {
72 int ret;
73 unsigned long flags;
74 unsigned int i;
75 cycles_t time1, time2, time;
76 u32 rem;
77
78 local_irq_save(flags);
79 preempt_disable();
80 time1 = get_cycles();
81 for (i = 0; i < NR_LOOPS; i++) {
82 #ifdef CONFIG_X86_32
83 ret = sync_cmpxchg(&test_val, 0, 0);
84 #else
85 ret = cmpxchg(&test_val, 0, 0);
86 #endif
87 }
88 time2 = get_cycles();
89 local_irq_restore(flags);
90 preempt_enable();
91 time = time2 - time1;
92
93 printk(KERN_ALERT "test results: time for locked cmpxchg\n");
94 printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS);
95 printk(KERN_ALERT "total time: %llu\n", time);
96 time = div_u64_rem(time, NR_LOOPS, &rem);
97 printk(KERN_ALERT "-> locked cmpxchg takes %llu cycles\n", time);
98 printk(KERN_ALERT "test end\n");
99 }
100
do_test_cmpxchg(void)101 static void do_test_cmpxchg(void)
102 {
103 int ret;
104 unsigned long flags;
105 unsigned int i;
106 cycles_t time1, time2, time;
107 u32 rem;
108
109 local_irq_save(flags);
110 preempt_disable();
111 time1 = get_cycles();
112 for (i = 0; i < NR_LOOPS; i++) {
113 ret = cmpxchg_local(&test_val, 0, 0);
114 }
115 time2 = get_cycles();
116 local_irq_restore(flags);
117 preempt_enable();
118 time = time2 - time1;
119
120 printk(KERN_ALERT "test results: time for non locked cmpxchg\n");
121 printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS);
122 printk(KERN_ALERT "total time: %llu\n", time);
123 time = div_u64_rem(time, NR_LOOPS, &rem);
124 printk(KERN_ALERT "-> non locked cmpxchg takes %llu cycles\n", time);
125 printk(KERN_ALERT "test end\n");
126 }
127
do_test_sync_inc(void)128 static void do_test_sync_inc(void)
129 {
130 int ret;
131 unsigned long flags;
132 unsigned int i;
133 cycles_t time1, time2, time;
134 u32 rem;
135 atomic_t val;
136
137 local_irq_save(flags);
138 preempt_disable();
139 time1 = get_cycles();
140 for (i = 0; i < NR_LOOPS; i++) {
141 ret = atomic_add_return(10, &val);
142 }
143 time2 = get_cycles();
144 local_irq_restore(flags);
145 preempt_enable();
146 time = time2 - time1;
147
148 printk(KERN_ALERT "test results: time for locked add return\n");
149 printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS);
150 printk(KERN_ALERT "total time: %llu\n", time);
151 time = div_u64_rem(time, NR_LOOPS, &rem);
152 printk(KERN_ALERT "-> locked add return takes %llu cycles\n", time);
153 printk(KERN_ALERT "test end\n");
154 }
155
do_test_inc(void)156 static void do_test_inc(void)
157 {
158 int ret;
159 unsigned long flags;
160 unsigned int i;
161 cycles_t time1, time2, time;
162 u32 rem;
163 local_t loc_val;
164
165 local_irq_save(flags);
166 preempt_disable();
167 time1 = get_cycles();
168 for (i = 0; i < NR_LOOPS; i++) {
169 ret = local_add_return(10, &loc_val);
170 }
171 time2 = get_cycles();
172 local_irq_restore(flags);
173 preempt_enable();
174 time = time2 - time1;
175
176 printk(KERN_ALERT "test results: time for non locked add return\n");
177 printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS);
178 printk(KERN_ALERT "total time: %llu\n", time);
179 time = div_u64_rem(time, NR_LOOPS, &rem);
180 printk(KERN_ALERT "-> non locked add return takes %llu cycles\n", time);
181 printk(KERN_ALERT "test end\n");
182 }
183
184 /*
185 * This test will have a higher standard deviation due to incoming interrupts.
186 */
do_test_enable_int(void)187 static void do_test_enable_int(void)
188 {
189 unsigned long flags;
190 unsigned int i;
191 cycles_t time1, time2, time;
192 u32 rem;
193
194 local_irq_save(flags);
195 preempt_disable();
196 time1 = get_cycles();
197 for (i = 0; i < NR_LOOPS; i++) {
198 local_irq_restore(flags);
199 }
200 time2 = get_cycles();
201 local_irq_restore(flags);
202 preempt_enable();
203 time = time2 - time1;
204
205 printk(KERN_ALERT "test results: time for enabling interrupts (STI)\n");
206 printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS);
207 printk(KERN_ALERT "total time: %llu\n", time);
208 time = div_u64_rem(time, NR_LOOPS, &rem);
209 printk(KERN_ALERT "-> enabling interrupts (STI) takes %llu cycles\n",
210 time);
211 printk(KERN_ALERT "test end\n");
212 }
213
do_test_disable_int(void)214 static void do_test_disable_int(void)
215 {
216 unsigned long flags, flags2;
217 unsigned int i;
218 cycles_t time1, time2, time;
219 u32 rem;
220
221 local_irq_save(flags);
222 preempt_disable();
223 time1 = get_cycles();
224 for (i = 0; i < NR_LOOPS; i++) {
225 local_irq_save(flags2);
226 }
227 time2 = get_cycles();
228 local_irq_restore(flags);
229 preempt_enable();
230 time = time2 - time1;
231
232 printk(KERN_ALERT
233 "test results: time for disabling interrupts (CLI)\n");
234 printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS);
235 printk(KERN_ALERT "total time: %llu\n", time);
236 time = div_u64_rem(time, NR_LOOPS, &rem);
237 printk(KERN_ALERT "-> disabling interrupts (CLI) takes %llu cycles\n",
238 time);
239 printk(KERN_ALERT "test end\n");
240 }
241
do_test_int(void)242 static void do_test_int(void)
243 {
244 unsigned long flags;
245 unsigned int i;
246 cycles_t time1, time2, time;
247 u32 rem;
248
249 local_irq_save(flags);
250 preempt_disable();
251 time1 = get_cycles();
252 for (i = 0; i < NR_LOOPS; i++) {
253 local_irq_restore(flags);
254 local_irq_save(flags);
255 }
256 time2 = get_cycles();
257 local_irq_restore(flags);
258 preempt_enable();
259 time = time2 - time1;
260
261 printk(KERN_ALERT
262 "test results: time for disabling/enabling interrupts (STI/CLI)\n");
263 printk(KERN_ALERT "number of loops: %d\n", NR_LOOPS);
264 printk(KERN_ALERT "total time: %llu\n", time);
265 time = div_u64_rem(time, NR_LOOPS, &rem);
266 printk(KERN_ALERT
267 "-> enabling/disabling interrupts (STI/CLI) takes %llu cycles\n",
268 time);
269 printk(KERN_ALERT "test end\n");
270 }
271
ltt_test_init(void)272 static int ltt_test_init(void)
273 {
274 printk(KERN_ALERT "test init\n");
275
276 do_testbaseline();
277 do_test_sync_cmpxchg();
278 do_test_cmpxchg();
279 do_test_sync_inc();
280 do_test_inc();
281 do_test_enable_int();
282 do_test_disable_int();
283 do_test_int();
284 return -EAGAIN; /* Fail will directly unload the module */
285 }
286
ltt_test_exit(void)287 static void ltt_test_exit(void)
288 {
289 printk(KERN_ALERT "test exit\n");
290 }
291
292 module_init(ltt_test_init)
293 module_exit(ltt_test_exit)
294
295 MODULE_LICENSE("GPL");
296 MODULE_AUTHOR("Mathieu Desnoyers");
297 MODULE_DESCRIPTION("Cmpxchg vs int Test");
298