1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018-2019 Arm Limited 3 */ 4 5 #include <inttypes.h> 6 #include <stdint.h> 7 #include <stdio.h> 8 #include <string.h> 9 #include <sys/queue.h> 10 #include <unistd.h> 11 12 #include <rte_common.h> 13 #include <rte_cycles.h> 14 #include <rte_eal.h> 15 #include <rte_launch.h> 16 #include <rte_lcore.h> 17 #include <rte_memory.h> 18 #include <rte_per_lcore.h> 19 #include <rte_ticketlock.h> 20 21 #include "test.h" 22 23 /* 24 * Ticketlock test 25 * ============= 26 * 27 * - There is a global ticketlock and a table of ticketlocks (one per lcore). 28 * 29 * - The test function takes all of these locks and launches the 30 * ``test_ticketlock_per_core()`` function on each core (except the main). 31 * 32 * - The function takes the global lock, display something, then releases 33 * the global lock. 34 * - The function takes the per-lcore lock, display something, then releases 35 * the per-core lock. 36 * 37 * - The main function unlocks the per-lcore locks sequentially and 38 * waits between each lock. This triggers the display of a message 39 * for each core, in the correct order. The autotest script checks that 40 * this order is correct. 41 * 42 * - A load test is carried out, with all cores attempting to lock a single lock 43 * multiple times 44 */ 45 46 static rte_ticketlock_t tl, tl_try; 47 static rte_ticketlock_t tl_tab[RTE_MAX_LCORE]; 48 static rte_ticketlock_recursive_t tlr; 49 static unsigned int count; 50 51 static uint32_t synchro; 52 53 static int 54 test_ticketlock_per_core(__rte_unused void *arg) 55 { 56 rte_ticketlock_lock(&tl); 57 printf("Global lock taken on core %u\n", rte_lcore_id()); 58 rte_ticketlock_unlock(&tl); 59 60 rte_ticketlock_lock(&tl_tab[rte_lcore_id()]); 61 printf("Hello from core %u !\n", rte_lcore_id()); 62 rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]); 63 64 return 0; 65 } 66 67 static int 68 test_ticketlock_recursive_per_core(__rte_unused void *arg) 69 { 70 unsigned int id = rte_lcore_id(); 71 72 rte_ticketlock_recursive_lock(&tlr); 73 printf("Global recursive lock taken on core %u - count = %d\n", 74 id, tlr.count); 75 rte_ticketlock_recursive_lock(&tlr); 76 printf("Global recursive lock taken on core %u - count = %d\n", 77 id, tlr.count); 78 rte_ticketlock_recursive_lock(&tlr); 79 printf("Global recursive lock taken on core %u - count = %d\n", 80 id, tlr.count); 81 82 printf("Hello from within recursive locks from core %u !\n", id); 83 84 rte_ticketlock_recursive_unlock(&tlr); 85 printf("Global recursive lock released on core %u - count = %d\n", 86 id, tlr.count); 87 rte_ticketlock_recursive_unlock(&tlr); 88 printf("Global recursive lock released on core %u - count = %d\n", 89 id, tlr.count); 90 rte_ticketlock_recursive_unlock(&tlr); 91 printf("Global recursive lock released on core %u - count = %d\n", 92 id, tlr.count); 93 94 return 0; 95 } 96 97 static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER; 98 static uint64_t lcount __rte_cache_aligned; 99 static uint64_t lcore_count[RTE_MAX_LCORE] __rte_cache_aligned; 100 static uint64_t time_cost[RTE_MAX_LCORE]; 101 102 #define MAX_LOOP 10000 103 104 static int 105 load_loop_fn(void *func_param) 106 { 107 uint64_t time_diff = 0, begin; 108 uint64_t hz = rte_get_timer_hz(); 109 const int use_lock = *(int *)func_param; 110 const unsigned int lcore = rte_lcore_id(); 111 112 /* wait synchro for workers */ 113 if (lcore != rte_get_main_lcore()) 114 rte_wait_until_equal_32(&synchro, 1, __ATOMIC_RELAXED); 115 116 begin = rte_rdtsc_precise(); 117 while (lcore_count[lcore] < MAX_LOOP) { 118 if (use_lock) 119 rte_ticketlock_lock(&lk); 120 lcore_count[lcore]++; 121 lcount++; 122 if (use_lock) 123 rte_ticketlock_unlock(&lk); 124 } 125 time_diff = rte_rdtsc_precise() - begin; 126 time_cost[lcore] = time_diff * 1000000 / hz; 127 return 0; 128 } 129 130 static int 131 test_ticketlock_perf(void) 132 { 133 unsigned int i; 134 uint64_t tcount = 0; 135 uint64_t total_time = 0; 136 int lock = 0; 137 const unsigned int lcore = rte_lcore_id(); 138 139 printf("\nTest with no lock on single core...\n"); 140 load_loop_fn(&lock); 141 printf("Core [%u] cost time = %"PRIu64" us\n", lcore, time_cost[lcore]); 142 memset(lcore_count, 0, sizeof(lcore_count)); 143 memset(time_cost, 0, sizeof(time_cost)); 144 145 printf("\nTest with lock on single core...\n"); 146 lock = 1; 147 load_loop_fn(&lock); 148 printf("Core [%u] cost time = %"PRIu64" us\n", lcore, time_cost[lcore]); 149 memset(lcore_count, 0, sizeof(lcore_count)); 150 memset(time_cost, 0, sizeof(time_cost)); 151 152 lcount = 0; 153 printf("\nTest with lock on %u cores...\n", rte_lcore_count()); 154 155 /* Clear synchro and start workers */ 156 __atomic_store_n(&synchro, 0, __ATOMIC_RELAXED); 157 rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MAIN); 158 159 /* start synchro and launch test on main */ 160 __atomic_store_n(&synchro, 1, __ATOMIC_RELAXED); 161 load_loop_fn(&lock); 162 163 rte_eal_mp_wait_lcore(); 164 165 RTE_LCORE_FOREACH(i) { 166 printf("Core [%u] cost time = %"PRIu64" us\n", i, time_cost[i]); 167 tcount += lcore_count[i]; 168 total_time += time_cost[i]; 169 } 170 171 if (tcount != lcount) 172 return -1; 173 174 printf("Total cost time = %"PRIu64" us\n", total_time); 175 176 return 0; 177 } 178 179 /* 180 * Use rte_ticketlock_trylock() to trylock a ticketlock object, 181 * If it could not lock the object successfully, it would 182 * return immediately and the variable of "count" would be 183 * increased by one per times. the value of "count" could be 184 * checked as the result later. 185 */ 186 static int 187 test_ticketlock_try(__rte_unused void *arg) 188 { 189 if (rte_ticketlock_trylock(&tl_try) == 0) { 190 rte_ticketlock_lock(&tl); 191 count++; 192 rte_ticketlock_unlock(&tl); 193 } 194 195 return 0; 196 } 197 198 199 /* 200 * Test rte_eal_get_lcore_state() in addition to ticketlocks 201 * as we have "waiting" then "running" lcores. 202 */ 203 static int 204 test_ticketlock(void) 205 { 206 int ret = 0; 207 int i; 208 209 /* worker cores should be waiting: print it */ 210 RTE_LCORE_FOREACH_WORKER(i) { 211 printf("lcore %d state: %d\n", i, 212 (int) rte_eal_get_lcore_state(i)); 213 } 214 215 rte_ticketlock_init(&tl); 216 rte_ticketlock_init(&tl_try); 217 rte_ticketlock_recursive_init(&tlr); 218 RTE_LCORE_FOREACH_WORKER(i) { 219 rte_ticketlock_init(&tl_tab[i]); 220 } 221 222 rte_ticketlock_lock(&tl); 223 224 RTE_LCORE_FOREACH_WORKER(i) { 225 rte_ticketlock_lock(&tl_tab[i]); 226 rte_eal_remote_launch(test_ticketlock_per_core, NULL, i); 227 } 228 229 /* worker cores should be busy: print it */ 230 RTE_LCORE_FOREACH_WORKER(i) { 231 printf("lcore %d state: %d\n", i, 232 (int) rte_eal_get_lcore_state(i)); 233 } 234 rte_ticketlock_unlock(&tl); 235 236 RTE_LCORE_FOREACH_WORKER(i) { 237 rte_ticketlock_unlock(&tl_tab[i]); 238 rte_delay_ms(10); 239 } 240 241 rte_eal_mp_wait_lcore(); 242 243 rte_ticketlock_recursive_lock(&tlr); 244 245 /* 246 * Try to acquire a lock that we already own 247 */ 248 if (!rte_ticketlock_recursive_trylock(&tlr)) { 249 printf("rte_ticketlock_recursive_trylock failed on a lock that " 250 "we already own\n"); 251 ret = -1; 252 } else 253 rte_ticketlock_recursive_unlock(&tlr); 254 255 RTE_LCORE_FOREACH_WORKER(i) { 256 rte_eal_remote_launch(test_ticketlock_recursive_per_core, 257 NULL, i); 258 } 259 rte_ticketlock_recursive_unlock(&tlr); 260 rte_eal_mp_wait_lcore(); 261 262 /* 263 * Test if it could return immediately from try-locking a locked object. 264 * Here it will lock the ticketlock object first, then launch all the 265 * worker lcores to trylock the same ticketlock object. 266 * All the worker lcores should give up try-locking a locked object and 267 * return immediately, and then increase the "count" initialized with 268 * zero by one per times. 269 * We can check if the "count" is finally equal to the number of all 270 * worker lcores to see if the behavior of try-locking a locked 271 * ticketlock object is correct. 272 */ 273 if (rte_ticketlock_trylock(&tl_try) == 0) 274 return -1; 275 276 count = 0; 277 RTE_LCORE_FOREACH_WORKER(i) { 278 rte_eal_remote_launch(test_ticketlock_try, NULL, i); 279 } 280 rte_eal_mp_wait_lcore(); 281 rte_ticketlock_unlock(&tl_try); 282 if (rte_ticketlock_is_locked(&tl)) { 283 printf("ticketlock is locked but it should not be\n"); 284 return -1; 285 } 286 rte_ticketlock_lock(&tl); 287 if (count != (rte_lcore_count() - 1)) 288 ret = -1; 289 290 rte_ticketlock_unlock(&tl); 291 292 /* 293 * Test if it can trylock recursively. 294 * Use rte_ticketlock_recursive_trylock() to check if it can lock 295 * a ticketlock object recursively. Here it will try to lock a 296 * ticketlock object twice. 297 */ 298 if (rte_ticketlock_recursive_trylock(&tlr) == 0) { 299 printf("It failed to do the first ticketlock_recursive_trylock " 300 "but it should able to do\n"); 301 return -1; 302 } 303 if (rte_ticketlock_recursive_trylock(&tlr) == 0) { 304 printf("It failed to do the second ticketlock_recursive_trylock " 305 "but it should able to do\n"); 306 return -1; 307 } 308 rte_ticketlock_recursive_unlock(&tlr); 309 rte_ticketlock_recursive_unlock(&tlr); 310 311 if (test_ticketlock_perf() < 0) 312 return -1; 313 314 return ret; 315 } 316 317 REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock); 318