first public release
[dpdk.git] / app / test / test_atomic.c
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2012 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  * 
7  *   Redistribution and use in source and binary forms, with or without 
8  *   modification, are permitted provided that the following conditions 
9  *   are met:
10  * 
11  *     * Redistributions of source code must retain the above copyright 
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright 
14  *       notice, this list of conditions and the following disclaimer in 
15  *       the documentation and/or other materials provided with the 
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its 
18  *       contributors may be used to endorse or promote products derived 
19  *       from this software without specific prior written permission.
20  * 
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  * 
33  *  version: DPDK.L.1.2.3-3
34  */
35
36 #include <stdio.h>
37 #include <stdint.h>
38 #include <unistd.h>
39 #include <sys/queue.h>
40
41 #include <cmdline_parse.h>
42
43 #include <rte_memory.h>
44 #include <rte_memzone.h>
45 #include <rte_per_lcore.h>
46 #include <rte_launch.h>
47 #include <rte_atomic.h>
48 #include <rte_tailq.h>
49 #include <rte_eal.h>
50 #include <rte_per_lcore.h>
51 #include <rte_lcore.h>
52
53 #include "test.h"
54
55 /*
56  * Atomic Variables
57  * ================
58  *
59  * - The main test function performs three subtests. The first test
60  *   checks that the usual inc/dec/add/sub functions are working
61  *   correctly:
62  *
63  *   - Initialize 16-bit, 32-bit and 64-bit atomic variables to specific
64  *     values.
65  *
66  *   - These variables are incremented and decremented on each core at
67  *     the same time in ``test_atomic_usual()``.
68  *
69  *   - The function checks that once all lcores finish their function,
70  *     the value of the atomic variables are still the same.
71  *
72  * - The second test verifies the behavior of "test and set" functions.
73  *
74  *   - Initialize 16-bit, 32-bit and 64-bit atomic variables to zero.
75  *
76  *   - Invoke ``test_atomic_tas()`` on each lcore: before doing anything
77  *     else. The cores are waiting a synchro using ``while
78  *     (rte_atomic32_read(&val) == 0)`` which is triggered by the main test
79  *     function. Then all cores do a
80  *     ``rte_atomicXX_test_and_set()`` at the same time. If it is successful,
81  *     it increments another atomic counter.
82  *
83  *   - The main function checks that the atomic counter was incremented
84  *     twice only (one for 16-bit, one for 32-bit and one for 64-bit values).
85  *
86  * - Test "add/sub and return"
87  *
88  *   - Initialize 16-bit, 32-bit and 64-bit atomic variables to zero.
89  *
90  *   - Invoke ``test_atomic_addsub_return()`` on each lcore. Before doing
91  *     anything else, the cores are waiting a synchro. Each lcore does
92  *     this operation several times::
93  *
94  *       tmp = rte_atomicXX_add_return(&a, 1);
95  *       atomic_add(&count, tmp);
96  *       tmp = rte_atomicXX_sub_return(&a, 1);
97  *       atomic_sub(&count, tmp+1);
98  *
99  *   - At the end of the test, the *count* value must be 0.
100  */
101
102 #define NUM_ATOMIC_TYPES 3
103
104 #define N 10000
105
106 static rte_atomic16_t a16;
107 static rte_atomic32_t a32;
108 static rte_atomic64_t a64;
109 static rte_atomic32_t count;
110 static rte_atomic32_t synchro;
111
112 static int
113 test_atomic_usual(__attribute__((unused)) void *arg)
114 {
115         unsigned i;
116
117         while (rte_atomic32_read(&synchro) == 0)
118                 ;
119
120         for (i = 0; i < N; i++)
121                 rte_atomic16_inc(&a16);
122         for (i = 0; i < N; i++)
123                 rte_atomic16_dec(&a16);
124         for (i = 0; i < (N / 5); i++)
125                 rte_atomic16_add(&a16, 5);
126         for (i = 0; i < (N / 5); i++)
127                 rte_atomic16_sub(&a16, 5);
128
129         for (i = 0; i < N; i++)
130                 rte_atomic32_inc(&a32);
131         for (i = 0; i < N; i++)
132                 rte_atomic32_dec(&a32);
133         for (i = 0; i < (N / 5); i++)
134                 rte_atomic32_add(&a32, 5);
135         for (i = 0; i < (N / 5); i++)
136                 rte_atomic32_sub(&a32, 5);
137
138         for (i = 0; i < N; i++)
139                 rte_atomic64_inc(&a64);
140         for (i = 0; i < N; i++)
141                 rte_atomic64_dec(&a64);
142         for (i = 0; i < (N / 5); i++)
143                 rte_atomic64_add(&a64, 5);
144         for (i = 0; i < (N / 5); i++)
145                 rte_atomic64_sub(&a64, 5);
146
147         return 0;
148 }
149
150 static int
151 test_atomic_tas(__attribute__((unused)) void *arg)
152 {
153         while (rte_atomic32_read(&synchro) == 0)
154                 ;
155
156         if (rte_atomic16_test_and_set(&a16))
157                 rte_atomic32_inc(&count);
158         if (rte_atomic32_test_and_set(&a32))
159                 rte_atomic32_inc(&count);
160         if (rte_atomic64_test_and_set(&a64))
161                 rte_atomic32_inc(&count);
162
163         return 0;
164 }
165
166 static int
167 test_atomic_addsub_and_return(__attribute__((unused)) void *arg)
168 {
169         uint32_t tmp16;
170         uint32_t tmp32;
171         uint64_t tmp64;
172         unsigned i;
173
174         while (rte_atomic32_read(&synchro) == 0)
175                 ;
176
177         for (i = 0; i < N; i++) {
178                 tmp16 = rte_atomic16_add_return(&a16, 1);
179                 rte_atomic32_add(&count, tmp16);
180
181                 tmp16 = rte_atomic16_sub_return(&a16, 1);
182                 rte_atomic32_sub(&count, tmp16+1);
183
184                 tmp32 = rte_atomic32_add_return(&a32, 1);
185                 rte_atomic32_add(&count, tmp32);
186
187                 tmp32 = rte_atomic32_sub_return(&a32, 1);
188                 rte_atomic32_sub(&count, tmp32+1);
189
190                 tmp64 = rte_atomic64_add_return(&a64, 1);
191                 rte_atomic32_add(&count, tmp64);
192
193                 tmp64 = rte_atomic64_sub_return(&a64, 1);
194                 rte_atomic32_sub(&count, tmp64+1);
195         }
196
197         return 0;
198 }
199
200 /*
201  * rte_atomic32_inc_and_test() would increase a 32 bits counter by one and then
202  * test if that counter is equal to 0. It would return true if the counter is 0
203  * and false if the counter is not 0. rte_atomic64_inc_and_test() could do the
204  * same thing but for a 64 bits counter.
205  * Here checks that if the 32/64 bits counter is equal to 0 after being atomically
206  * increased by one. If it is, increase the variable of "count" by one which would
207  * be checked as the result later.
208  *
209  */
210 static int
211 test_atomic_inc_and_test(__attribute__((unused)) void *arg)
212 {
213         while (rte_atomic32_read(&synchro) == 0)
214                 ;
215
216         if (rte_atomic16_inc_and_test(&a16)) {
217                 rte_atomic32_inc(&count);
218         }
219         if (rte_atomic32_inc_and_test(&a32)) {
220                 rte_atomic32_inc(&count);
221         }
222         if (rte_atomic64_inc_and_test(&a64)) {
223                 rte_atomic32_inc(&count);
224         }
225
226         return 0;
227 }
228
229 /*
230  * rte_atomicXX_dec_and_test() should decrease a 32 bits counter by one and then
231  * test if that counter is equal to 0. It should return true if the counter is 0
232  * and false if the counter is not 0.
233  * This test checks if the counter is equal to 0 after being atomically
234  * decreased by one. If it is, increase the value of "count" by one which is to
235  * be checked as the result later.
236  */
237 static int
238 test_atomic_dec_and_test(__attribute__((unused)) void *arg)
239 {
240         while (rte_atomic32_read(&synchro) == 0)
241                 ;
242
243         if (rte_atomic16_dec_and_test(&a16))
244                 rte_atomic32_inc(&count);
245
246         if (rte_atomic32_dec_and_test(&a32))
247                 rte_atomic32_inc(&count);
248
249         if (rte_atomic64_dec_and_test(&a64))
250                 rte_atomic32_inc(&count);
251
252         return 0;
253 }
254
255 int
256 test_atomic(void)
257 {
258         rte_atomic16_init(&a16);
259         rte_atomic32_init(&a32);
260         rte_atomic64_init(&a64);
261         rte_atomic32_init(&count);
262         rte_atomic32_init(&synchro);
263
264         rte_atomic16_set(&a16, 1UL << 10);
265         rte_atomic32_set(&a32, 1UL << 10);
266         rte_atomic64_set(&a64, 1ULL << 33);
267
268         printf("usual inc/dec/add/sub functions\n");
269
270         rte_eal_mp_remote_launch(test_atomic_usual, NULL, SKIP_MASTER);
271         rte_atomic32_set(&synchro, 1);
272         rte_eal_mp_wait_lcore();
273         rte_atomic32_set(&synchro, 0);
274
275         if (rte_atomic16_read(&a16) != 1UL << 10) {
276                 printf("Atomic16 usual functions failed\n");
277                 return -1;
278         }
279
280         if (rte_atomic32_read(&a32) != 1UL << 10) {
281                 printf("Atomic32 usual functions failed\n");
282                 return -1;
283         }
284
285         if (rte_atomic64_read(&a64) != 1ULL << 33) {
286                 printf("Atomic64 usual functions failed\n");
287                 return -1;
288         }
289
290         printf("test and set\n");
291
292         rte_atomic64_set(&a64, 0);
293         rte_atomic32_set(&a32, 0);
294         rte_atomic16_set(&a16, 0);
295         rte_atomic32_set(&count, 0);
296         rte_eal_mp_remote_launch(test_atomic_tas, NULL, SKIP_MASTER);
297         rte_atomic32_set(&synchro, 1);
298         rte_eal_mp_wait_lcore();
299         rte_atomic32_set(&synchro, 0);
300
301         if (rte_atomic32_read(&count) != NUM_ATOMIC_TYPES) {
302                 printf("Atomic test and set failed\n");
303                 return -1;
304         }
305
306         printf("add/sub and return\n");
307
308         rte_atomic64_set(&a64, 0);
309         rte_atomic32_set(&a32, 0);
310         rte_atomic16_set(&a16, 0);
311         rte_atomic32_set(&count, 0);
312         rte_eal_mp_remote_launch(test_atomic_addsub_and_return, NULL,
313                                  SKIP_MASTER);
314         rte_atomic32_set(&synchro, 1);
315         rte_eal_mp_wait_lcore();
316         rte_atomic32_set(&synchro, 0);
317
318         if (rte_atomic32_read(&count) != 0) {
319                 printf("Atomic add/sub+return failed\n");
320                 return -1;
321         }
322
323         /*
324          * Set a64, a32 and a16 with the same value of minus "number of slave
325          * lcores", launch all slave lcores to atomically increase by one and
326          * test them respectively.
327          * Each lcore should have only one chance to increase a64 by one and
328          * then check if it is equal to 0, but there should be only one lcore
329          * that finds that it is 0. It is similar for a32 and a16.
330          * Then a variable of "count", initialized to zero, is increased by
331          * one if a64, a32 or a16 is 0 after being increased and tested
332          * atomically.
333          * We can check if "count" is finally equal to 3 to see if all slave
334          * lcores performed "atomic inc and test" right.
335          */
336         printf("inc and test\n");
337
338         rte_atomic64_clear(&a64);
339         rte_atomic32_clear(&a32);
340         rte_atomic16_clear(&a16);
341         rte_atomic32_clear(&synchro);
342         rte_atomic32_clear(&count);
343
344         rte_atomic64_set(&a64, (int64_t)(1 - (int64_t)rte_lcore_count()));
345         rte_atomic32_set(&a32, (int32_t)(1 - (int32_t)rte_lcore_count()));
346         rte_atomic16_set(&a16, (int16_t)(1 - (int16_t)rte_lcore_count()));
347         rte_eal_mp_remote_launch(test_atomic_inc_and_test, NULL, SKIP_MASTER);
348         rte_atomic32_set(&synchro, 1);
349         rte_eal_mp_wait_lcore();
350         rte_atomic32_clear(&synchro);
351
352         if (rte_atomic32_read(&count) != NUM_ATOMIC_TYPES) {
353                 printf("Atomic inc and test failed %d\n", count.cnt);
354                 return -1;
355         }
356
357         /*
358          * Same as above, but this time we set the values to "number of slave
359          * lcores", and decrement instead of increment.
360          */
361         printf("dec and test\n");
362
363         rte_atomic32_clear(&synchro);
364         rte_atomic32_clear(&count);
365
366         rte_atomic64_set(&a64, (int64_t)(rte_lcore_count() - 1));
367         rte_atomic32_set(&a32, (int32_t)(rte_lcore_count() - 1));
368         rte_atomic16_set(&a16, (int16_t)(rte_lcore_count() - 1));
369         rte_eal_mp_remote_launch(test_atomic_dec_and_test, NULL, SKIP_MASTER);
370         rte_atomic32_set(&synchro, 1);
371         rte_eal_mp_wait_lcore();
372         rte_atomic32_clear(&synchro);
373
374         if (rte_atomic32_read(&count) != NUM_ATOMIC_TYPES) {
375                 printf("Atomic dec and test failed\n");
376                 return -1;
377         }
378
379         return 0;
380 }
381