Branch data Line data Source code
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_ATOMIC(rte_mcslock_t *) p_ml;
40 : : RTE_ATOMIC(rte_mcslock_t *) p_ml_try;
41 : : RTE_ATOMIC(rte_mcslock_t *) p_ml_perf;
42 : :
43 : : static unsigned int count;
44 : :
45 : : #define MAX_LOOP_BASE 1000000u
46 : : #define MAX_LOOP_MIN 10000u
47 : : static unsigned int max_loop;
48 : :
49 : : static RTE_ATOMIC(uint32_t) synchro;
50 : :
51 : : static int
52 : 1 : test_mcslock_per_core(__rte_unused void *arg)
53 : : {
54 : : /* Per core me node. */
55 : : rte_mcslock_t ml_me;
56 : :
57 : 1 : rte_mcslock_lock(&p_ml, &ml_me);
58 : : printf("MCS lock taken on core %u\n", rte_lcore_id());
59 : 1 : rte_mcslock_unlock(&p_ml, &ml_me);
60 : : printf("MCS lock released on core %u\n", rte_lcore_id());
61 : :
62 : 1 : return 0;
63 : : }
64 : :
65 : : static uint64_t time_count[RTE_MAX_LCORE] = {0};
66 : :
67 : : static int
68 : 4 : load_loop_fn(void *func_param)
69 : : {
70 : : uint64_t time_diff = 0, begin;
71 : : uint64_t hz = rte_get_timer_hz();
72 : 4 : volatile uint64_t lcount = 0;
73 : 4 : const int use_lock = *(int *)func_param;
74 : : const unsigned int lcore = rte_lcore_id();
75 : :
76 : : /**< Per core me node. */
77 : : rte_mcslock_t ml_perf_me;
78 : :
79 : : /* wait synchro */
80 : : rte_wait_until_equal_32((uint32_t *)(uintptr_t)&synchro, 1, rte_memory_order_relaxed);
81 : :
82 : : begin = rte_get_timer_cycles();
83 [ + + ]: 1999987 : while (lcount < max_loop) {
84 [ + + ]: 1999979 : if (use_lock)
85 : 1499908 : rte_mcslock_lock(&p_ml_perf, &ml_perf_me);
86 : :
87 : 2000000 : lcount++;
88 [ + + ]: 2000000 : if (use_lock)
89 : 1500000 : rte_mcslock_unlock(&p_ml_perf, &ml_perf_me);
90 : : }
91 : 4 : time_diff = rte_get_timer_cycles() - begin;
92 : 4 : time_count[lcore] = time_diff * 1000000 / hz;
93 : 4 : return 0;
94 : : }
95 : :
96 : : static int
97 : 1 : test_mcslock_perf(void)
98 : : {
99 : : unsigned int i;
100 : : uint64_t total = 0;
101 : 1 : int lock = 0;
102 : : const unsigned int lcore = rte_lcore_id();
103 : :
104 : : printf("\nTest with no lock on single core...\n");
105 : 1 : rte_atomic_store_explicit(&synchro, 1, rte_memory_order_relaxed);
106 : 1 : load_loop_fn(&lock);
107 : 1 : 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 : 1 : rte_atomic_store_explicit(&synchro, 1, rte_memory_order_relaxed);
113 : 1 : lock = 1;
114 : 1 : load_loop_fn(&lock);
115 : 1 : printf("Core [%u] Cost Time = %"PRIu64" us\n",
116 : : lcore, time_count[lcore]);
117 : : memset(time_count, 0, sizeof(time_count));
118 : :
119 : 1 : printf("\nTest with lock on %u cores...\n", (rte_lcore_count()));
120 : :
121 : 1 : rte_atomic_store_explicit(&synchro, 0, rte_memory_order_relaxed);
122 : 1 : rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MAIN);
123 : :
124 : : /* start synchro and launch test on main */
125 : 1 : rte_atomic_store_explicit(&synchro, 1, rte_memory_order_relaxed);
126 : 1 : load_loop_fn(&lock);
127 : :
128 : 1 : rte_eal_mp_wait_lcore();
129 : :
130 [ + + ]: 3 : RTE_LCORE_FOREACH(i) {
131 : 2 : printf("Core [%u] Cost Time = %"PRIu64" us\n",
132 : : i, time_count[i]);
133 : 2 : total += time_count[i];
134 : : }
135 : :
136 : : printf("Total Cost Time = %"PRIu64" us\n", total);
137 : :
138 : 1 : 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 : 1 : 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 [ + - ]: 1 : if (rte_mcslock_trylock(&p_ml_try, &ml_try_me) == 0) {
157 : 1 : rte_mcslock_lock(&p_ml, &ml_me);
158 : 1 : count++;
159 : 1 : rte_mcslock_unlock(&p_ml, &ml_me);
160 : : }
161 : :
162 : 1 : 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 : 1 : 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 : 1 : max_loop = test_scale_iterations(MAX_LOOP_BASE, MAX_LOOP_MIN);
181 : :
182 : : /*
183 : : * Test mcs lock & unlock on each core
184 : : */
185 : :
186 : : /* worker cores should be waiting: print it */
187 [ + + ]: 2 : RTE_LCORE_FOREACH_WORKER(i) {
188 : 1 : printf("lcore %d state: %d\n", i,
189 : 1 : (int) rte_eal_get_lcore_state(i));
190 : : }
191 : :
192 : 1 : rte_mcslock_lock(&p_ml, &ml_me);
193 : :
194 [ + + ]: 2 : RTE_LCORE_FOREACH_WORKER(i) {
195 : 1 : rte_eal_remote_launch(test_mcslock_per_core, NULL, i);
196 : : }
197 : :
198 : : /* worker cores should be busy: print it */
199 [ + + ]: 2 : RTE_LCORE_FOREACH_WORKER(i) {
200 : 1 : printf("lcore %d state: %d\n", i,
201 : 1 : (int) rte_eal_get_lcore_state(i));
202 : : }
203 : :
204 : 1 : rte_mcslock_unlock(&p_ml, &ml_me);
205 : :
206 : 1 : rte_eal_mp_wait_lcore();
207 : :
208 : : /*
209 : : * Test if it could return immediately from try-locking a locked object.
210 : : * Here it will lock the mcs lock object first, then launch all the
211 : : * worker lcores to trylock the same mcs lock object.
212 : : * All the worker lcores should give up try-locking a locked object and
213 : : * return immediately, and then increase the "count" initialized with
214 : : * zero by one per times.
215 : : * We can check if the "count" is finally equal to the number of all
216 : : * worker lcores to see if the behavior of try-locking a locked
217 : : * mcslock object is correct.
218 : : */
219 [ + - ]: 1 : if (rte_mcslock_trylock(&p_ml_try, &ml_try_me) == 0)
220 : : return -1;
221 : :
222 : 1 : count = 0;
223 [ + + ]: 2 : RTE_LCORE_FOREACH_WORKER(i) {
224 : 1 : rte_eal_remote_launch(test_mcslock_try, NULL, i);
225 : : }
226 : 1 : rte_eal_mp_wait_lcore();
227 : 1 : rte_mcslock_unlock(&p_ml_try, &ml_try_me);
228 : :
229 : : /* Test is_locked API */
230 [ - + ]: 1 : if (rte_mcslock_is_locked(p_ml)) {
231 : : printf("mcslock is locked but it should not be\n");
232 : 0 : return -1;
233 : : }
234 : :
235 : : /* Counting the locked times in each core */
236 : 1 : rte_mcslock_lock(&p_ml, &ml_me);
237 [ - + ]: 1 : if (count != (rte_lcore_count() - 1))
238 : : ret = -1;
239 : 1 : rte_mcslock_unlock(&p_ml, &ml_me);
240 : :
241 : : /* mcs lock perf test */
242 [ - + ]: 1 : if (test_mcslock_perf() < 0)
243 : 0 : return -1;
244 : :
245 : : return ret;
246 : : }
247 : :
248 : 276 : REGISTER_FAST_TEST(mcslock_autotest, NOHUGE_SKIP, ASAN_OK, test_mcslock);
|