1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2021 Microsoft Corporation 3 */ 4 5 #include <stdio.h> 6 #include <stdint.h> 7 #include <inttypes.h> 8 #include <unistd.h> 9 #include <sys/queue.h> 10 #include <string.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_pflock.h> 17 #include <rte_eal.h> 18 #include <rte_lcore.h> 19 #include <rte_cycles.h> 20 21 #include "test.h" 22 23 /* 24 * phase-fair lock test 25 * ==================== 26 * Provides UT for phase-fair lock API. 27 * Main concern is on functional testing, but also provides some 28 * performance measurements. 29 * Obviously for proper testing need to be executed with more than one lcore. 30 */ 31 32 static rte_pflock_t sl; 33 static rte_pflock_t sl_tab[RTE_MAX_LCORE]; 34 static uint32_t synchro; 35 36 static int 37 test_pflock_per_core(__rte_unused void *arg) 38 { 39 rte_pflock_write_lock(&sl); 40 printf("Global write lock taken on core %u\n", rte_lcore_id()); 41 rte_pflock_write_unlock(&sl); 42 43 rte_pflock_write_lock(&sl_tab[rte_lcore_id()]); 44 printf("Hello from core %u !\n", rte_lcore_id()); 45 rte_pflock_write_unlock(&sl_tab[rte_lcore_id()]); 46 47 rte_pflock_read_lock(&sl); 48 printf("Global read lock taken on core %u\n", rte_lcore_id()); 49 rte_delay_ms(100); 50 printf("Release global read lock on core %u\n", rte_lcore_id()); 51 rte_pflock_read_unlock(&sl); 52 53 return 0; 54 } 55 56 static rte_pflock_t lk = RTE_PFLOCK_INITIALIZER; 57 static uint64_t time_count[RTE_MAX_LCORE] = {0}; 58 59 #define MAX_LOOP 10000 60 61 static int 62 load_loop_fn(void *arg) 63 { 64 uint64_t time_diff = 0, begin; 65 uint64_t hz = rte_get_timer_hz(); 66 uint64_t lcount = 0; 67 const int use_lock = *(int *)arg; 68 const unsigned int lcore = rte_lcore_id(); 69 70 /* wait synchro for workers */ 71 if (lcore != rte_get_main_lcore()) 72 rte_wait_until_equal_32(&synchro, 1, __ATOMIC_RELAXED); 73 74 begin = rte_rdtsc_precise(); 75 while (lcount < MAX_LOOP) { 76 if (use_lock) 77 rte_pflock_write_lock(&lk); 78 lcount++; 79 if (use_lock) 80 rte_pflock_write_unlock(&lk); 81 82 if (use_lock) { 83 rte_pflock_read_lock(&lk); 84 rte_pflock_read_unlock(&lk); 85 } 86 } 87 88 time_diff = rte_rdtsc_precise() - begin; 89 time_count[lcore] = time_diff * 1000000 / hz; 90 return 0; 91 } 92 93 static int 94 test_pflock_perf(void) 95 { 96 unsigned int i; 97 int lock = 0; 98 uint64_t total = 0; 99 const unsigned int lcore = rte_lcore_id(); 100 101 printf("\nTest with no lock on single core...\n"); 102 __atomic_store_n(&synchro, 1, __ATOMIC_RELAXED); 103 load_loop_fn(&lock); 104 printf("Core [%u] Cost Time = %"PRIu64" us\n", 105 lcore, time_count[lcore]); 106 memset(time_count, 0, sizeof(time_count)); 107 108 printf("\nTest with phase-fair lock on single core...\n"); 109 lock = 1; 110 __atomic_store_n(&synchro, 1, __ATOMIC_RELAXED); 111 load_loop_fn(&lock); 112 printf("Core [%u] Cost Time = %"PRIu64" us\n", 113 lcore, time_count[lcore]); 114 memset(time_count, 0, sizeof(time_count)); 115 116 printf("\nPhase-fair test on %u cores...\n", rte_lcore_count()); 117 118 /* clear synchro and start workers */ 119 __atomic_store_n(&synchro, 0, __ATOMIC_RELAXED); 120 if (rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MAIN) < 0) 121 return -1; 122 123 /* start synchro and launch test on main */ 124 __atomic_store_n(&synchro, 1, __ATOMIC_RELAXED); 125 load_loop_fn(&lock); 126 127 rte_eal_mp_wait_lcore(); 128 129 RTE_LCORE_FOREACH(i) { 130 printf("Core [%u] cost time = %"PRIu64" us\n", 131 i, time_count[i]); 132 total += time_count[i]; 133 } 134 135 printf("Total cost time = %"PRIu64" us\n", total); 136 memset(time_count, 0, sizeof(time_count)); 137 138 return 0; 139 } 140 141 /* 142 * - There is a global pflock and a table of pflocks (one per lcore). 143 * 144 * - The test function takes all of these locks and launches the 145 * ``test_pflock_per_core()`` function on each core (except the main). 146 * 147 * - The function takes the global write lock, display something, 148 * then releases the global lock. 149 * - Then, it takes the per-lcore write lock, display something, and 150 * releases the per-core lock. 151 * - Finally, a read lock is taken during 100 ms, then released. 152 * 153 * - The main function unlocks the per-lcore locks sequentially and 154 * waits between each lock. This triggers the display of a message 155 * for each core, in the correct order. 156 * 157 * Then, it tries to take the global write lock and display the last 158 * message. The autotest script checks that the message order is correct. 159 */ 160 static int 161 test_pflock(void) 162 { 163 int i; 164 165 rte_pflock_init(&sl); 166 for (i = 0; i < RTE_MAX_LCORE; i++) 167 rte_pflock_init(&sl_tab[i]); 168 169 rte_pflock_write_lock(&sl); 170 171 RTE_LCORE_FOREACH_WORKER(i) { 172 rte_pflock_write_lock(&sl_tab[i]); 173 rte_eal_remote_launch(test_pflock_per_core, NULL, i); 174 } 175 176 rte_pflock_write_unlock(&sl); 177 178 RTE_LCORE_FOREACH_WORKER(i) { 179 rte_pflock_write_unlock(&sl_tab[i]); 180 rte_delay_ms(100); 181 } 182 183 rte_pflock_write_lock(&sl); 184 /* this message should be the last message of test */ 185 printf("Global write lock taken on main core %u\n", rte_lcore_id()); 186 rte_pflock_write_unlock(&sl); 187 188 rte_eal_mp_wait_lcore(); 189 190 if (test_pflock_perf() < 0) 191 return -1; 192 193 return 0; 194 } 195 196 REGISTER_TEST_COMMAND(pflock_autotest, test_pflock); 197