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