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