1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2019 Arm Limited 3 */ 4 5 #include <stdio.h> 6 #include <stdint.h> 7 #include <inttypes.h> 8 #include <string.h> 9 #include <unistd.h> 10 #include <sys/queue.h> 11 12 #include <rte_common.h> 13 #include <rte_memory.h> 14 #include <rte_per_lcore.h> 15 #include <rte_launch.h> 16 #include <rte_eal.h> 17 #include <rte_lcore.h> 18 #include <rte_cycles.h> 19 #include <rte_mcslock.h> 20 21 #include "test.h" 22 23 /* 24 * RTE MCS lock test 25 * ================= 26 * 27 * These tests are derived from spin lock test cases. 28 * 29 * - The functional test takes all of these locks and launches the 30 * ''test_mcslock_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 on each core. 34 * 35 * - A load test is carried out, with all cores attempting to lock a single 36 * lock multiple times. 37 */ 38 39 rte_mcslock_t *p_ml; 40 rte_mcslock_t *p_ml_try; 41 rte_mcslock_t *p_ml_perf; 42 43 static unsigned int count; 44 45 static uint32_t synchro; 46 47 static int 48 test_mcslock_per_core(__rte_unused void *arg) 49 { 50 /* Per core me node. */ 51 rte_mcslock_t ml_me; 52 53 rte_mcslock_lock(&p_ml, &ml_me); 54 printf("MCS lock taken on core %u\n", rte_lcore_id()); 55 rte_mcslock_unlock(&p_ml, &ml_me); 56 printf("MCS lock released on core %u\n", rte_lcore_id()); 57 58 return 0; 59 } 60 61 static uint64_t time_count[RTE_MAX_LCORE] = {0}; 62 63 #define MAX_LOOP 1000000 64 65 static int 66 load_loop_fn(void *func_param) 67 { 68 uint64_t time_diff = 0, begin; 69 uint64_t hz = rte_get_timer_hz(); 70 volatile uint64_t lcount = 0; 71 const int use_lock = *(int *)func_param; 72 const unsigned int lcore = rte_lcore_id(); 73 74 /**< Per core me node. */ 75 rte_mcslock_t ml_perf_me; 76 77 /* wait synchro */ 78 rte_wait_until_equal_32(&synchro, 1, __ATOMIC_RELAXED); 79 80 begin = rte_get_timer_cycles(); 81 while (lcount < MAX_LOOP) { 82 if (use_lock) 83 rte_mcslock_lock(&p_ml_perf, &ml_perf_me); 84 85 lcount++; 86 if (use_lock) 87 rte_mcslock_unlock(&p_ml_perf, &ml_perf_me); 88 } 89 time_diff = rte_get_timer_cycles() - begin; 90 time_count[lcore] = time_diff * 1000000 / hz; 91 return 0; 92 } 93 94 static int 95 test_mcslock_perf(void) 96 { 97 unsigned int i; 98 uint64_t total = 0; 99 int lock = 0; 100 const unsigned int lcore = rte_lcore_id(); 101 102 printf("\nTest with no lock on single core...\n"); 103 __atomic_store_n(&synchro, 1, __ATOMIC_RELAXED); 104 load_loop_fn(&lock); 105 printf("Core [%u] Cost Time = %"PRIu64" us\n", 106 lcore, time_count[lcore]); 107 memset(time_count, 0, sizeof(time_count)); 108 109 printf("\nTest with lock on single core...\n"); 110 __atomic_store_n(&synchro, 1, __ATOMIC_RELAXED); 111 lock = 1; 112 load_loop_fn(&lock); 113 printf("Core [%u] Cost Time = %"PRIu64" us\n", 114 lcore, time_count[lcore]); 115 memset(time_count, 0, sizeof(time_count)); 116 117 printf("\nTest with lock on %u cores...\n", (rte_lcore_count())); 118 119 __atomic_store_n(&synchro, 0, __ATOMIC_RELAXED); 120 rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MAIN); 121 122 /* start synchro and launch test on main */ 123 __atomic_store_n(&synchro, 1, __ATOMIC_RELAXED); 124 load_loop_fn(&lock); 125 126 rte_eal_mp_wait_lcore(); 127 128 RTE_LCORE_FOREACH(i) { 129 printf("Core [%u] Cost Time = %"PRIu64" us\n", 130 i, time_count[i]); 131 total += time_count[i]; 132 } 133 134 printf("Total Cost Time = %"PRIu64" us\n", total); 135 136 return 0; 137 } 138 139 /* 140 * Use rte_mcslock_trylock() to trylock a mcs lock object, 141 * If it could not lock the object successfully, it would 142 * return immediately. 143 */ 144 static int 145 test_mcslock_try(__rte_unused void *arg) 146 { 147 /**< Per core me node. */ 148 rte_mcslock_t ml_me; 149 rte_mcslock_t ml_try_me; 150 151 /* Locked ml_try in the main lcore, so it should fail 152 * when trying to lock it in the worker lcore. 153 */ 154 if (rte_mcslock_trylock(&p_ml_try, &ml_try_me) == 0) { 155 rte_mcslock_lock(&p_ml, &ml_me); 156 count++; 157 rte_mcslock_unlock(&p_ml, &ml_me); 158 } 159 160 return 0; 161 } 162 163 164 /* 165 * Test rte_eal_get_lcore_state() in addition to mcs locks 166 * as we have "waiting" then "running" lcores. 167 */ 168 static int 169 test_mcslock(void) 170 { 171 int ret = 0; 172 int i; 173 174 /* Define per core me node. */ 175 rte_mcslock_t ml_me; 176 rte_mcslock_t ml_try_me; 177 178 /* 179 * Test mcs lock & unlock on each core 180 */ 181 182 /* worker cores should be waiting: print it */ 183 RTE_LCORE_FOREACH_WORKER(i) { 184 printf("lcore %d state: %d\n", i, 185 (int) rte_eal_get_lcore_state(i)); 186 } 187 188 rte_mcslock_lock(&p_ml, &ml_me); 189 190 RTE_LCORE_FOREACH_WORKER(i) { 191 rte_eal_remote_launch(test_mcslock_per_core, NULL, i); 192 } 193 194 /* worker cores should be busy: print it */ 195 RTE_LCORE_FOREACH_WORKER(i) { 196 printf("lcore %d state: %d\n", i, 197 (int) rte_eal_get_lcore_state(i)); 198 } 199 200 rte_mcslock_unlock(&p_ml, &ml_me); 201 202 rte_eal_mp_wait_lcore(); 203 204 /* 205 * Test if it could return immediately from try-locking a locked object. 206 * Here it will lock the mcs lock object first, then launch all the 207 * worker lcores to trylock the same mcs lock object. 208 * All the worker lcores should give up try-locking a locked object and 209 * return immediately, and then increase the "count" initialized with 210 * zero by one per times. 211 * We can check if the "count" is finally equal to the number of all 212 * worker lcores to see if the behavior of try-locking a locked 213 * mcslock object is correct. 214 */ 215 if (rte_mcslock_trylock(&p_ml_try, &ml_try_me) == 0) 216 return -1; 217 218 count = 0; 219 RTE_LCORE_FOREACH_WORKER(i) { 220 rte_eal_remote_launch(test_mcslock_try, NULL, i); 221 } 222 rte_eal_mp_wait_lcore(); 223 rte_mcslock_unlock(&p_ml_try, &ml_try_me); 224 225 /* Test is_locked API */ 226 if (rte_mcslock_is_locked(p_ml)) { 227 printf("mcslock is locked but it should not be\n"); 228 return -1; 229 } 230 231 /* Counting the locked times in each core */ 232 rte_mcslock_lock(&p_ml, &ml_me); 233 if (count != (rte_lcore_count() - 1)) 234 ret = -1; 235 rte_mcslock_unlock(&p_ml, &ml_me); 236 237 /* mcs lock perf test */ 238 if (test_mcslock_perf() < 0) 239 return -1; 240 241 return ret; 242 } 243 244 REGISTER_TEST_COMMAND(mcslock_autotest, test_mcslock); 245