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 <inttypes.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <sys/queue.h> 40 41 #include <rte_common.h> 42 #include <rte_memory.h> 43 #include <rte_memzone.h> 44 #include <rte_per_lcore.h> 45 #include <rte_launch.h> 46 #include <rte_eal.h> 47 #include <rte_per_lcore.h> 48 #include <rte_lcore.h> 49 #include <rte_cycles.h> 50 #include <rte_spinlock.h> 51 #include <rte_atomic.h> 52 53 #include "test.h" 54 55 /* 56 * Spinlock test 57 * ============= 58 * 59 * - There is a global spinlock and a table of spinlocks (one per lcore). 60 * 61 * - The test function takes all of these locks and launches the 62 * ``test_spinlock_per_core()`` function on each core (except the master). 63 * 64 * - The function takes the global lock, display something, then releases 65 * the global lock. 66 * - The function takes the per-lcore lock, display something, then releases 67 * the per-core lock. 68 * 69 * - The main function unlocks the per-lcore locks sequentially and 70 * waits between each lock. This triggers the display of a message 71 * for each core, in the correct order. The autotest script checks that 72 * this order is correct. 73 * 74 * - A load test is carried out, with all cores attempting to lock a single lock 75 * multiple times 76 */ 77 78 static rte_spinlock_t sl, sl_try; 79 static rte_spinlock_t sl_tab[RTE_MAX_LCORE]; 80 static rte_spinlock_recursive_t slr; 81 static unsigned count = 0; 82 83 static rte_atomic32_t synchro; 84 85 static int 86 test_spinlock_per_core(__attribute__((unused)) void *arg) 87 { 88 rte_spinlock_lock(&sl); 89 printf("Global lock taken on core %u\n", rte_lcore_id()); 90 rte_spinlock_unlock(&sl); 91 92 rte_spinlock_lock(&sl_tab[rte_lcore_id()]); 93 printf("Hello from core %u !\n", rte_lcore_id()); 94 rte_spinlock_unlock(&sl_tab[rte_lcore_id()]); 95 96 return 0; 97 } 98 99 static int 100 test_spinlock_recursive_per_core(__attribute__((unused)) void *arg) 101 { 102 unsigned id = rte_lcore_id(); 103 104 rte_spinlock_recursive_lock(&slr); 105 printf("Global recursive lock taken on core %u - count = %d\n", 106 id, slr.count); 107 rte_spinlock_recursive_lock(&slr); 108 printf("Global recursive lock taken on core %u - count = %d\n", 109 id, slr.count); 110 rte_spinlock_recursive_lock(&slr); 111 printf("Global recursive lock taken on core %u - count = %d\n", 112 id, slr.count); 113 114 printf("Hello from within recursive locks from core %u !\n", id); 115 116 rte_spinlock_recursive_unlock(&slr); 117 printf("Global recursive lock released on core %u - count = %d\n", 118 id, slr.count); 119 rte_spinlock_recursive_unlock(&slr); 120 printf("Global recursive lock released on core %u - count = %d\n", 121 id, slr.count); 122 rte_spinlock_recursive_unlock(&slr); 123 printf("Global recursive lock released on core %u - count = %d\n", 124 id, slr.count); 125 126 return 0; 127 } 128 129 static rte_spinlock_t lk = RTE_SPINLOCK_INITIALIZER; 130 static uint64_t lock_count[RTE_MAX_LCORE] = {0}; 131 132 #define TIME_MS 100 133 134 static int 135 load_loop_fn(void *func_param) 136 { 137 uint64_t time_diff = 0, begin; 138 uint64_t hz = rte_get_timer_hz(); 139 uint64_t lcount = 0; 140 const int use_lock = *(int*)func_param; 141 const unsigned lcore = rte_lcore_id(); 142 143 /* wait synchro for slaves */ 144 if (lcore != rte_get_master_lcore()) 145 while (rte_atomic32_read(&synchro) == 0); 146 147 begin = rte_get_timer_cycles(); 148 while (time_diff < hz * TIME_MS / 1000) { 149 if (use_lock) 150 rte_spinlock_lock(&lk); 151 lcount++; 152 if (use_lock) 153 rte_spinlock_unlock(&lk); 154 /* delay to make lock duty cycle slighlty realistic */ 155 rte_delay_us(1); 156 time_diff = rte_get_timer_cycles() - begin; 157 } 158 lock_count[lcore] = lcount; 159 return 0; 160 } 161 162 static int 163 test_spinlock_perf(void) 164 { 165 unsigned int i; 166 uint64_t total = 0; 167 int lock = 0; 168 const unsigned lcore = rte_lcore_id(); 169 170 printf("\nTest with no lock on single core...\n"); 171 load_loop_fn(&lock); 172 printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]); 173 memset(lock_count, 0, sizeof(lock_count)); 174 175 printf("\nTest with lock on single core...\n"); 176 lock = 1; 177 load_loop_fn(&lock); 178 printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]); 179 memset(lock_count, 0, sizeof(lock_count)); 180 181 printf("\nTest with lock on %u cores...\n", rte_lcore_count()); 182 183 /* Clear synchro and start slaves */ 184 rte_atomic32_set(&synchro, 0); 185 rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER); 186 187 /* start synchro and launch test on master */ 188 rte_atomic32_set(&synchro, 1); 189 load_loop_fn(&lock); 190 191 rte_eal_mp_wait_lcore(); 192 193 RTE_LCORE_FOREACH(i) { 194 printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]); 195 total += lock_count[i]; 196 } 197 198 printf("Total count = %"PRIu64"\n", total); 199 200 return 0; 201 } 202 203 /* 204 * Use rte_spinlock_trylock() to trylock a spinlock object, 205 * If it could not lock the object sucessfully, it would 206 * return immediately and the variable of "count" would be 207 * increased by one per times. the value of "count" could be 208 * checked as the result later. 209 */ 210 static int 211 test_spinlock_try(__attribute__((unused)) void *arg) 212 { 213 if (rte_spinlock_trylock(&sl_try) == 0) { 214 rte_spinlock_lock(&sl); 215 count ++; 216 rte_spinlock_unlock(&sl); 217 } 218 219 return 0; 220 } 221 222 223 /* 224 * Test rte_eal_get_lcore_state() in addition to spinlocks 225 * as we have "waiting" then "running" lcores. 226 */ 227 static int 228 test_spinlock(void) 229 { 230 int ret = 0; 231 int i; 232 233 /* slave cores should be waiting: print it */ 234 RTE_LCORE_FOREACH_SLAVE(i) { 235 printf("lcore %d state: %d\n", i, 236 (int) rte_eal_get_lcore_state(i)); 237 } 238 239 rte_spinlock_init(&sl); 240 rte_spinlock_init(&sl_try); 241 rte_spinlock_recursive_init(&slr); 242 for (i=0; i<RTE_MAX_LCORE; i++) 243 rte_spinlock_init(&sl_tab[i]); 244 245 rte_spinlock_lock(&sl); 246 247 RTE_LCORE_FOREACH_SLAVE(i) { 248 rte_spinlock_lock(&sl_tab[i]); 249 rte_eal_remote_launch(test_spinlock_per_core, NULL, i); 250 } 251 252 /* slave cores should be busy: print it */ 253 RTE_LCORE_FOREACH_SLAVE(i) { 254 printf("lcore %d state: %d\n", i, 255 (int) rte_eal_get_lcore_state(i)); 256 } 257 rte_spinlock_unlock(&sl); 258 259 RTE_LCORE_FOREACH_SLAVE(i) { 260 rte_spinlock_unlock(&sl_tab[i]); 261 rte_delay_ms(10); 262 } 263 264 rte_eal_mp_wait_lcore(); 265 266 rte_spinlock_recursive_lock(&slr); 267 268 /* 269 * Try to acquire a lock that we already own 270 */ 271 if(!rte_spinlock_recursive_trylock(&slr)) { 272 printf("rte_spinlock_recursive_trylock failed on a lock that " 273 "we already own\n"); 274 ret = -1; 275 } else 276 rte_spinlock_recursive_unlock(&slr); 277 278 RTE_LCORE_FOREACH_SLAVE(i) { 279 rte_eal_remote_launch(test_spinlock_recursive_per_core, NULL, i); 280 } 281 rte_spinlock_recursive_unlock(&slr); 282 rte_eal_mp_wait_lcore(); 283 284 /* 285 * Test if it could return immediately from try-locking a locked object. 286 * Here it will lock the spinlock object first, then launch all the slave 287 * lcores to trylock the same spinlock object. 288 * All the slave lcores should give up try-locking a locked object and 289 * return immediately, and then increase the "count" initialized with zero 290 * by one per times. 291 * We can check if the "count" is finally equal to the number of all slave 292 * lcores to see if the behavior of try-locking a locked spinlock object 293 * is correct. 294 */ 295 if (rte_spinlock_trylock(&sl_try) == 0) { 296 return -1; 297 } 298 count = 0; 299 RTE_LCORE_FOREACH_SLAVE(i) { 300 rte_eal_remote_launch(test_spinlock_try, NULL, i); 301 } 302 rte_eal_mp_wait_lcore(); 303 rte_spinlock_unlock(&sl_try); 304 if (rte_spinlock_is_locked(&sl)) { 305 printf("spinlock is locked but it should not be\n"); 306 return -1; 307 } 308 rte_spinlock_lock(&sl); 309 if (count != ( rte_lcore_count() - 1)) { 310 ret = -1; 311 } 312 rte_spinlock_unlock(&sl); 313 314 /* 315 * Test if it can trylock recursively. 316 * Use rte_spinlock_recursive_trylock() to check if it can lock a spinlock 317 * object recursively. Here it will try to lock a spinlock object twice. 318 */ 319 if (rte_spinlock_recursive_trylock(&slr) == 0) { 320 printf("It failed to do the first spinlock_recursive_trylock but it should able to do\n"); 321 return -1; 322 } 323 if (rte_spinlock_recursive_trylock(&slr) == 0) { 324 printf("It failed to do the second spinlock_recursive_trylock but it should able to do\n"); 325 return -1; 326 } 327 rte_spinlock_recursive_unlock(&slr); 328 rte_spinlock_recursive_unlock(&slr); 329 330 if (test_spinlock_perf() < 0) 331 return -1; 332 333 return ret; 334 } 335 336 REGISTER_TEST_COMMAND(spinlock_autotest, test_spinlock); 337