1 /*- 2 * BSD LICENSE 3 * 4 * Copyright(c) 2010-2014 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 34 #include <stdio.h> 35 #include <stdint.h> 36 #include <unistd.h> 37 #include <sys/queue.h> 38 39 #include <rte_memory.h> 40 #include <rte_memzone.h> 41 #include <rte_per_lcore.h> 42 #include <rte_launch.h> 43 #include <rte_atomic.h> 44 #include <rte_tailq.h> 45 #include <rte_eal.h> 46 #include <rte_per_lcore.h> 47 #include <rte_lcore.h> 48 49 #include "test.h" 50 51 /* 52 * Atomic Variables 53 * ================ 54 * 55 * - The main test function performs three subtests. The first test 56 * checks that the usual inc/dec/add/sub functions are working 57 * correctly: 58 * 59 * - Initialize 16-bit, 32-bit and 64-bit atomic variables to specific 60 * values. 61 * 62 * - These variables are incremented and decremented on each core at 63 * the same time in ``test_atomic_usual()``. 64 * 65 * - The function checks that once all lcores finish their function, 66 * the value of the atomic variables are still the same. 67 * 68 * - The second test verifies the behavior of "test and set" functions. 69 * 70 * - Initialize 16-bit, 32-bit and 64-bit atomic variables to zero. 71 * 72 * - Invoke ``test_atomic_tas()`` on each lcore: before doing anything 73 * else. The cores are waiting a synchro using ``while 74 * (rte_atomic32_read(&val) == 0)`` which is triggered by the main test 75 * function. Then all cores do a 76 * ``rte_atomicXX_test_and_set()`` at the same time. If it is successful, 77 * it increments another atomic counter. 78 * 79 * - The main function checks that the atomic counter was incremented 80 * twice only (one for 16-bit, one for 32-bit and one for 64-bit values). 81 * 82 * - Test "add/sub and return" 83 * 84 * - Initialize 16-bit, 32-bit and 64-bit atomic variables to zero. 85 * 86 * - Invoke ``test_atomic_addsub_return()`` on each lcore. Before doing 87 * anything else, the cores are waiting a synchro. Each lcore does 88 * this operation several times:: 89 * 90 * tmp = rte_atomicXX_add_return(&a, 1); 91 * atomic_add(&count, tmp); 92 * tmp = rte_atomicXX_sub_return(&a, 1); 93 * atomic_sub(&count, tmp+1); 94 * 95 * - At the end of the test, the *count* value must be 0. 96 */ 97 98 #define NUM_ATOMIC_TYPES 3 99 100 #define N 10000 101 102 static rte_atomic16_t a16; 103 static rte_atomic32_t a32; 104 static rte_atomic64_t a64; 105 static rte_atomic64_t count; 106 static rte_atomic32_t synchro; 107 108 static int 109 test_atomic_usual(__attribute__((unused)) void *arg) 110 { 111 unsigned i; 112 113 while (rte_atomic32_read(&synchro) == 0) 114 ; 115 116 for (i = 0; i < N; i++) 117 rte_atomic16_inc(&a16); 118 for (i = 0; i < N; i++) 119 rte_atomic16_dec(&a16); 120 for (i = 0; i < (N / 5); i++) 121 rte_atomic16_add(&a16, 5); 122 for (i = 0; i < (N / 5); i++) 123 rte_atomic16_sub(&a16, 5); 124 125 for (i = 0; i < N; i++) 126 rte_atomic32_inc(&a32); 127 for (i = 0; i < N; i++) 128 rte_atomic32_dec(&a32); 129 for (i = 0; i < (N / 5); i++) 130 rte_atomic32_add(&a32, 5); 131 for (i = 0; i < (N / 5); i++) 132 rte_atomic32_sub(&a32, 5); 133 134 for (i = 0; i < N; i++) 135 rte_atomic64_inc(&a64); 136 for (i = 0; i < N; i++) 137 rte_atomic64_dec(&a64); 138 for (i = 0; i < (N / 5); i++) 139 rte_atomic64_add(&a64, 5); 140 for (i = 0; i < (N / 5); i++) 141 rte_atomic64_sub(&a64, 5); 142 143 return 0; 144 } 145 146 static int 147 test_atomic_tas(__attribute__((unused)) void *arg) 148 { 149 while (rte_atomic32_read(&synchro) == 0) 150 ; 151 152 if (rte_atomic16_test_and_set(&a16)) 153 rte_atomic64_inc(&count); 154 if (rte_atomic32_test_and_set(&a32)) 155 rte_atomic64_inc(&count); 156 if (rte_atomic64_test_and_set(&a64)) 157 rte_atomic64_inc(&count); 158 159 return 0; 160 } 161 162 static int 163 test_atomic_addsub_and_return(__attribute__((unused)) void *arg) 164 { 165 uint32_t tmp16; 166 uint32_t tmp32; 167 uint64_t tmp64; 168 unsigned i; 169 170 while (rte_atomic32_read(&synchro) == 0) 171 ; 172 173 for (i = 0; i < N; i++) { 174 tmp16 = rte_atomic16_add_return(&a16, 1); 175 rte_atomic64_add(&count, tmp16); 176 177 tmp16 = rte_atomic16_sub_return(&a16, 1); 178 rte_atomic64_sub(&count, tmp16+1); 179 180 tmp32 = rte_atomic32_add_return(&a32, 1); 181 rte_atomic64_add(&count, tmp32); 182 183 tmp32 = rte_atomic32_sub_return(&a32, 1); 184 rte_atomic64_sub(&count, tmp32+1); 185 186 tmp64 = rte_atomic64_add_return(&a64, 1); 187 rte_atomic64_add(&count, tmp64); 188 189 tmp64 = rte_atomic64_sub_return(&a64, 1); 190 rte_atomic64_sub(&count, tmp64+1); 191 } 192 193 return 0; 194 } 195 196 /* 197 * rte_atomic32_inc_and_test() would increase a 32 bits counter by one and then 198 * test if that counter is equal to 0. It would return true if the counter is 0 199 * and false if the counter is not 0. rte_atomic64_inc_and_test() could do the 200 * same thing but for a 64 bits counter. 201 * Here checks that if the 32/64 bits counter is equal to 0 after being atomically 202 * increased by one. If it is, increase the variable of "count" by one which would 203 * be checked as the result later. 204 * 205 */ 206 static int 207 test_atomic_inc_and_test(__attribute__((unused)) void *arg) 208 { 209 while (rte_atomic32_read(&synchro) == 0) 210 ; 211 212 if (rte_atomic16_inc_and_test(&a16)) { 213 rte_atomic64_inc(&count); 214 } 215 if (rte_atomic32_inc_and_test(&a32)) { 216 rte_atomic64_inc(&count); 217 } 218 if (rte_atomic64_inc_and_test(&a64)) { 219 rte_atomic64_inc(&count); 220 } 221 222 return 0; 223 } 224 225 /* 226 * rte_atomicXX_dec_and_test() should decrease a 32 bits counter by one and then 227 * test if that counter is equal to 0. It should return true if the counter is 0 228 * and false if the counter is not 0. 229 * This test checks if the counter is equal to 0 after being atomically 230 * decreased by one. If it is, increase the value of "count" by one which is to 231 * be checked as the result later. 232 */ 233 static int 234 test_atomic_dec_and_test(__attribute__((unused)) void *arg) 235 { 236 while (rte_atomic32_read(&synchro) == 0) 237 ; 238 239 if (rte_atomic16_dec_and_test(&a16)) 240 rte_atomic64_inc(&count); 241 242 if (rte_atomic32_dec_and_test(&a32)) 243 rte_atomic64_inc(&count); 244 245 if (rte_atomic64_dec_and_test(&a64)) 246 rte_atomic64_inc(&count); 247 248 return 0; 249 } 250 251 static int 252 test_atomic(void) 253 { 254 rte_atomic16_init(&a16); 255 rte_atomic32_init(&a32); 256 rte_atomic64_init(&a64); 257 rte_atomic64_init(&count); 258 rte_atomic32_init(&synchro); 259 260 rte_atomic16_set(&a16, 1UL << 10); 261 rte_atomic32_set(&a32, 1UL << 10); 262 rte_atomic64_set(&a64, 1ULL << 33); 263 264 printf("usual inc/dec/add/sub functions\n"); 265 266 rte_eal_mp_remote_launch(test_atomic_usual, NULL, SKIP_MASTER); 267 rte_atomic32_set(&synchro, 1); 268 rte_eal_mp_wait_lcore(); 269 rte_atomic32_set(&synchro, 0); 270 271 if (rte_atomic16_read(&a16) != 1UL << 10) { 272 printf("Atomic16 usual functions failed\n"); 273 return -1; 274 } 275 276 if (rte_atomic32_read(&a32) != 1UL << 10) { 277 printf("Atomic32 usual functions failed\n"); 278 return -1; 279 } 280 281 if (rte_atomic64_read(&a64) != 1ULL << 33) { 282 printf("Atomic64 usual functions failed\n"); 283 return -1; 284 } 285 286 printf("test and set\n"); 287 288 rte_atomic64_set(&a64, 0); 289 rte_atomic32_set(&a32, 0); 290 rte_atomic16_set(&a16, 0); 291 rte_atomic64_set(&count, 0); 292 rte_eal_mp_remote_launch(test_atomic_tas, NULL, SKIP_MASTER); 293 rte_atomic32_set(&synchro, 1); 294 rte_eal_mp_wait_lcore(); 295 rte_atomic32_set(&synchro, 0); 296 297 if (rte_atomic64_read(&count) != NUM_ATOMIC_TYPES) { 298 printf("Atomic test and set failed\n"); 299 return -1; 300 } 301 302 printf("add/sub and return\n"); 303 304 rte_atomic64_set(&a64, 0); 305 rte_atomic32_set(&a32, 0); 306 rte_atomic16_set(&a16, 0); 307 rte_atomic64_set(&count, 0); 308 rte_eal_mp_remote_launch(test_atomic_addsub_and_return, NULL, 309 SKIP_MASTER); 310 rte_atomic32_set(&synchro, 1); 311 rte_eal_mp_wait_lcore(); 312 rte_atomic32_set(&synchro, 0); 313 314 if (rte_atomic64_read(&count) != 0) { 315 printf("Atomic add/sub+return failed\n"); 316 return -1; 317 } 318 319 /* 320 * Set a64, a32 and a16 with the same value of minus "number of slave 321 * lcores", launch all slave lcores to atomically increase by one and 322 * test them respectively. 323 * Each lcore should have only one chance to increase a64 by one and 324 * then check if it is equal to 0, but there should be only one lcore 325 * that finds that it is 0. It is similar for a32 and a16. 326 * Then a variable of "count", initialized to zero, is increased by 327 * one if a64, a32 or a16 is 0 after being increased and tested 328 * atomically. 329 * We can check if "count" is finally equal to 3 to see if all slave 330 * lcores performed "atomic inc and test" right. 331 */ 332 printf("inc and test\n"); 333 334 rte_atomic64_clear(&a64); 335 rte_atomic32_clear(&a32); 336 rte_atomic16_clear(&a16); 337 rte_atomic32_clear(&synchro); 338 rte_atomic64_clear(&count); 339 340 rte_atomic64_set(&a64, (int64_t)(1 - (int64_t)rte_lcore_count())); 341 rte_atomic32_set(&a32, (int32_t)(1 - (int32_t)rte_lcore_count())); 342 rte_atomic16_set(&a16, (int16_t)(1 - (int16_t)rte_lcore_count())); 343 rte_eal_mp_remote_launch(test_atomic_inc_and_test, NULL, SKIP_MASTER); 344 rte_atomic32_set(&synchro, 1); 345 rte_eal_mp_wait_lcore(); 346 rte_atomic32_clear(&synchro); 347 348 if (rte_atomic64_read(&count) != NUM_ATOMIC_TYPES) { 349 printf("Atomic inc and test failed %d\n", (int)count.cnt); 350 return -1; 351 } 352 353 /* 354 * Same as above, but this time we set the values to "number of slave 355 * lcores", and decrement instead of increment. 356 */ 357 printf("dec and test\n"); 358 359 rte_atomic32_clear(&synchro); 360 rte_atomic64_clear(&count); 361 362 rte_atomic64_set(&a64, (int64_t)(rte_lcore_count() - 1)); 363 rte_atomic32_set(&a32, (int32_t)(rte_lcore_count() - 1)); 364 rte_atomic16_set(&a16, (int16_t)(rte_lcore_count() - 1)); 365 rte_eal_mp_remote_launch(test_atomic_dec_and_test, NULL, SKIP_MASTER); 366 rte_atomic32_set(&synchro, 1); 367 rte_eal_mp_wait_lcore(); 368 rte_atomic32_clear(&synchro); 369 370 if (rte_atomic64_read(&count) != NUM_ATOMIC_TYPES) { 371 printf("Atomic dec and test failed\n"); 372 return -1; 373 } 374 375 return 0; 376 } 377 378 static struct test_command atomic_cmd = { 379 .command = "atomic_autotest", 380 .callback = test_atomic, 381 }; 382 REGISTER_TEST_COMMAND(atomic_cmd); 383