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
test_pflock_per_core(__rte_unused void * arg)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
load_loop_fn(void * arg)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
test_pflock_perf(void)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
test_pflock(void)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