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 RTE_ATOMIC(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((uint32_t *)(uintptr_t)&synchro, 1, 73 rte_memory_order_relaxed); 74 75 begin = rte_rdtsc_precise(); 76 while (lcount < MAX_LOOP) { 77 if (use_lock) 78 rte_pflock_write_lock(&lk); 79 lcount++; 80 if (use_lock) 81 rte_pflock_write_unlock(&lk); 82 83 if (use_lock) { 84 rte_pflock_read_lock(&lk); 85 rte_pflock_read_unlock(&lk); 86 } 87 } 88 89 time_diff = rte_rdtsc_precise() - begin; 90 time_count[lcore] = time_diff * 1000000 / hz; 91 return 0; 92 } 93 94 static int 95 test_pflock_perf(void) 96 { 97 unsigned int i; 98 int lock = 0; 99 uint64_t total = 0; 100 const unsigned int lcore = rte_lcore_id(); 101 102 printf("\nTest with no lock on single core...\n"); 103 rte_atomic_store_explicit(&synchro, 1, rte_memory_order_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 phase-fair lock on single core...\n"); 110 lock = 1; 111 rte_atomic_store_explicit(&synchro, 1, rte_memory_order_relaxed); 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("\nPhase-fair test on %u cores...\n", rte_lcore_count()); 118 119 /* clear synchro and start workers */ 120 rte_atomic_store_explicit(&synchro, 0, rte_memory_order_relaxed); 121 if (rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MAIN) < 0) 122 return -1; 123 124 /* start synchro and launch test on main */ 125 rte_atomic_store_explicit(&synchro, 1, rte_memory_order_relaxed); 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 memset(time_count, 0, sizeof(time_count)); 138 139 return 0; 140 } 141 142 /* 143 * - There is a global pflock and a table of pflocks (one per lcore). 144 * 145 * - The test function takes all of these locks and launches the 146 * ``test_pflock_per_core()`` function on each core (except the main). 147 * 148 * - The function takes the global write lock, display something, 149 * then releases the global lock. 150 * - Then, it takes the per-lcore write lock, display something, and 151 * releases the per-core lock. 152 * - Finally, a read lock is taken during 100 ms, then released. 153 * 154 * - The main function unlocks the per-lcore locks sequentially and 155 * waits between each lock. This triggers the display of a message 156 * for each core, in the correct order. 157 * 158 * Then, it tries to take the global write lock and display the last 159 * message. The autotest script checks that the message order is correct. 160 */ 161 static int 162 test_pflock(void) 163 { 164 int i; 165 166 rte_pflock_init(&sl); 167 for (i = 0; i < RTE_MAX_LCORE; i++) 168 rte_pflock_init(&sl_tab[i]); 169 170 rte_pflock_write_lock(&sl); 171 172 RTE_LCORE_FOREACH_WORKER(i) { 173 rte_pflock_write_lock(&sl_tab[i]); 174 rte_eal_remote_launch(test_pflock_per_core, NULL, i); 175 } 176 177 rte_pflock_write_unlock(&sl); 178 179 RTE_LCORE_FOREACH_WORKER(i) { 180 rte_pflock_write_unlock(&sl_tab[i]); 181 rte_delay_ms(100); 182 } 183 184 rte_pflock_write_lock(&sl); 185 /* this message should be the last message of test */ 186 printf("Global write lock taken on main core %u\n", rte_lcore_id()); 187 rte_pflock_write_unlock(&sl); 188 189 rte_eal_mp_wait_lcore(); 190 191 if (test_pflock_perf() < 0) 192 return -1; 193 194 return 0; 195 } 196 197 REGISTER_FAST_TEST(pflock_autotest, true, true, test_pflock); 198