1a9de470cSBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
2a9de470cSBruce Richardson * Copyright(c) 2010-2014 Intel Corporation
3a9de470cSBruce Richardson */
4a9de470cSBruce Richardson
5a9de470cSBruce Richardson #include <stdio.h>
6a9de470cSBruce Richardson #include <stdint.h>
7a9de470cSBruce Richardson #include <inttypes.h>
8a9de470cSBruce Richardson #include <string.h>
9a9de470cSBruce Richardson #include <unistd.h>
10a9de470cSBruce Richardson #include <sys/queue.h>
11a9de470cSBruce Richardson
12a9de470cSBruce Richardson #include <rte_common.h>
13a9de470cSBruce Richardson #include <rte_memory.h>
14a9de470cSBruce Richardson #include <rte_per_lcore.h>
15a9de470cSBruce Richardson #include <rte_launch.h>
16a9de470cSBruce Richardson #include <rte_eal.h>
17a9de470cSBruce Richardson #include <rte_lcore.h>
18a9de470cSBruce Richardson #include <rte_cycles.h>
19a9de470cSBruce Richardson #include <rte_spinlock.h>
20a9de470cSBruce Richardson
21a9de470cSBruce Richardson #include "test.h"
22a9de470cSBruce Richardson
23a9de470cSBruce Richardson /*
24a9de470cSBruce Richardson * Spinlock test
25a9de470cSBruce Richardson * =============
26a9de470cSBruce Richardson *
27a9de470cSBruce Richardson * - There is a global spinlock and a table of spinlocks (one per lcore).
28a9de470cSBruce Richardson *
29a9de470cSBruce Richardson * - The test function takes all of these locks and launches the
30cb056611SStephen Hemminger * ``test_spinlock_per_core()`` function on each core (except the main).
31a9de470cSBruce Richardson *
32a9de470cSBruce Richardson * - The function takes the global lock, display something, then releases
33a9de470cSBruce Richardson * the global lock.
34a9de470cSBruce Richardson * - The function takes the per-lcore lock, display something, then releases
35a9de470cSBruce Richardson * the per-core lock.
36a9de470cSBruce Richardson *
37a9de470cSBruce Richardson * - The main function unlocks the per-lcore locks sequentially and
38a9de470cSBruce Richardson * waits between each lock. This triggers the display of a message
39a9de470cSBruce Richardson * for each core, in the correct order. The autotest script checks that
40a9de470cSBruce Richardson * this order is correct.
41a9de470cSBruce Richardson *
42a9de470cSBruce Richardson * - A load test is carried out, with all cores attempting to lock a single lock
43a9de470cSBruce Richardson * multiple times
44a9de470cSBruce Richardson */
45a9de470cSBruce Richardson
46a9de470cSBruce Richardson static rte_spinlock_t sl, sl_try;
47a9de470cSBruce Richardson static rte_spinlock_t sl_tab[RTE_MAX_LCORE];
48a9de470cSBruce Richardson static rte_spinlock_recursive_t slr;
49a9de470cSBruce Richardson static unsigned count = 0;
50a9de470cSBruce Richardson
51*b6a7e685STyler Retzlaff static RTE_ATOMIC(uint32_t) synchro;
52a9de470cSBruce Richardson
53a9de470cSBruce Richardson static int
test_spinlock_per_core(__rte_unused void * arg)54f2fc83b4SThomas Monjalon test_spinlock_per_core(__rte_unused void *arg)
55a9de470cSBruce Richardson {
56a9de470cSBruce Richardson rte_spinlock_lock(&sl);
57a9de470cSBruce Richardson printf("Global lock taken on core %u\n", rte_lcore_id());
58a9de470cSBruce Richardson rte_spinlock_unlock(&sl);
59a9de470cSBruce Richardson
60a9de470cSBruce Richardson rte_spinlock_lock(&sl_tab[rte_lcore_id()]);
61a9de470cSBruce Richardson printf("Hello from core %u !\n", rte_lcore_id());
62a9de470cSBruce Richardson rte_spinlock_unlock(&sl_tab[rte_lcore_id()]);
63a9de470cSBruce Richardson
64a9de470cSBruce Richardson return 0;
65a9de470cSBruce Richardson }
66a9de470cSBruce Richardson
67a9de470cSBruce Richardson static int
test_spinlock_recursive_per_core(__rte_unused void * arg)68f2fc83b4SThomas Monjalon test_spinlock_recursive_per_core(__rte_unused void *arg)
69a9de470cSBruce Richardson {
70a9de470cSBruce Richardson unsigned id = rte_lcore_id();
71a9de470cSBruce Richardson
72a9de470cSBruce Richardson rte_spinlock_recursive_lock(&slr);
73a9de470cSBruce Richardson printf("Global recursive lock taken on core %u - count = %d\n",
74a9de470cSBruce Richardson id, slr.count);
75a9de470cSBruce Richardson rte_spinlock_recursive_lock(&slr);
76a9de470cSBruce Richardson printf("Global recursive lock taken on core %u - count = %d\n",
77a9de470cSBruce Richardson id, slr.count);
78a9de470cSBruce Richardson rte_spinlock_recursive_lock(&slr);
79a9de470cSBruce Richardson printf("Global recursive lock taken on core %u - count = %d\n",
80a9de470cSBruce Richardson id, slr.count);
81a9de470cSBruce Richardson
82a9de470cSBruce Richardson printf("Hello from within recursive locks from core %u !\n", id);
83a9de470cSBruce Richardson
84a9de470cSBruce Richardson rte_spinlock_recursive_unlock(&slr);
85a9de470cSBruce Richardson printf("Global recursive lock released on core %u - count = %d\n",
86a9de470cSBruce Richardson id, slr.count);
87a9de470cSBruce Richardson rte_spinlock_recursive_unlock(&slr);
88a9de470cSBruce Richardson printf("Global recursive lock released on core %u - count = %d\n",
89a9de470cSBruce Richardson id, slr.count);
90a9de470cSBruce Richardson rte_spinlock_recursive_unlock(&slr);
91a9de470cSBruce Richardson printf("Global recursive lock released on core %u - count = %d\n",
92a9de470cSBruce Richardson id, slr.count);
93a9de470cSBruce Richardson
94a9de470cSBruce Richardson return 0;
95a9de470cSBruce Richardson }
96a9de470cSBruce Richardson
97a9de470cSBruce Richardson static rte_spinlock_t lk = RTE_SPINLOCK_INITIALIZER;
98a52c5530SGavin Hu static uint64_t time_count[RTE_MAX_LCORE] = {0};
99a9de470cSBruce Richardson
100a52c5530SGavin Hu #define MAX_LOOP 10000
101a9de470cSBruce Richardson
102a9de470cSBruce Richardson static int
load_loop_fn(void * func_param)103a9de470cSBruce Richardson load_loop_fn(void *func_param)
104a9de470cSBruce Richardson {
105a9de470cSBruce Richardson uint64_t time_diff = 0, begin;
106a9de470cSBruce Richardson uint64_t hz = rte_get_timer_hz();
107a52c5530SGavin Hu volatile uint64_t lcount = 0;
108a9de470cSBruce Richardson const int use_lock = *(int*)func_param;
109a9de470cSBruce Richardson const unsigned lcore = rte_lcore_id();
110a9de470cSBruce Richardson
111cb056611SStephen Hemminger /* wait synchro for workers */
112cb056611SStephen Hemminger if (lcore != rte_get_main_lcore())
113*b6a7e685STyler Retzlaff rte_wait_until_equal_32((uint32_t *)(uintptr_t)&synchro, 1,
114*b6a7e685STyler Retzlaff rte_memory_order_relaxed);
115a9de470cSBruce Richardson
116a9de470cSBruce Richardson begin = rte_get_timer_cycles();
117a52c5530SGavin Hu while (lcount < MAX_LOOP) {
118a9de470cSBruce Richardson if (use_lock)
119a9de470cSBruce Richardson rte_spinlock_lock(&lk);
120a9de470cSBruce Richardson lcount++;
121a9de470cSBruce Richardson if (use_lock)
122a9de470cSBruce Richardson rte_spinlock_unlock(&lk);
123a9de470cSBruce Richardson }
124a52c5530SGavin Hu time_diff = rte_get_timer_cycles() - begin;
125a52c5530SGavin Hu time_count[lcore] = time_diff * 1000000 / hz;
126a9de470cSBruce Richardson return 0;
127a9de470cSBruce Richardson }
128a9de470cSBruce Richardson
129a9de470cSBruce Richardson static int
test_spinlock_perf(void)130a9de470cSBruce Richardson test_spinlock_perf(void)
131a9de470cSBruce Richardson {
132a9de470cSBruce Richardson unsigned int i;
133a9de470cSBruce Richardson uint64_t total = 0;
134a9de470cSBruce Richardson int lock = 0;
135a9de470cSBruce Richardson const unsigned lcore = rte_lcore_id();
136a9de470cSBruce Richardson
137a9de470cSBruce Richardson printf("\nTest with no lock on single core...\n");
138a9de470cSBruce Richardson load_loop_fn(&lock);
139a52c5530SGavin Hu printf("Core [%u] Cost Time = %"PRIu64" us\n", lcore,
140a52c5530SGavin Hu time_count[lcore]);
141a52c5530SGavin Hu memset(time_count, 0, sizeof(time_count));
142a9de470cSBruce Richardson
143a9de470cSBruce Richardson printf("\nTest with lock on single core...\n");
144a9de470cSBruce Richardson lock = 1;
145a9de470cSBruce Richardson load_loop_fn(&lock);
146a52c5530SGavin Hu printf("Core [%u] Cost Time = %"PRIu64" us\n", lcore,
147a52c5530SGavin Hu time_count[lcore]);
148a52c5530SGavin Hu memset(time_count, 0, sizeof(time_count));
149a9de470cSBruce Richardson
150a9de470cSBruce Richardson printf("\nTest with lock on %u cores...\n", rte_lcore_count());
151a9de470cSBruce Richardson
152cb056611SStephen Hemminger /* Clear synchro and start workers */
153*b6a7e685STyler Retzlaff rte_atomic_store_explicit(&synchro, 0, rte_memory_order_relaxed);
154cb056611SStephen Hemminger rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MAIN);
155a9de470cSBruce Richardson
156cb056611SStephen Hemminger /* start synchro and launch test on main */
157*b6a7e685STyler Retzlaff rte_atomic_store_explicit(&synchro, 1, rte_memory_order_relaxed);
158a9de470cSBruce Richardson load_loop_fn(&lock);
159a9de470cSBruce Richardson
160a9de470cSBruce Richardson rte_eal_mp_wait_lcore();
161a9de470cSBruce Richardson
162a9de470cSBruce Richardson RTE_LCORE_FOREACH(i) {
163a52c5530SGavin Hu printf("Core [%u] Cost Time = %"PRIu64" us\n", i,
164a52c5530SGavin Hu time_count[i]);
165a52c5530SGavin Hu total += time_count[i];
166a9de470cSBruce Richardson }
167a9de470cSBruce Richardson
168a52c5530SGavin Hu printf("Total Cost Time = %"PRIu64" us\n", total);
169a9de470cSBruce Richardson
170a9de470cSBruce Richardson return 0;
171a9de470cSBruce Richardson }
172a9de470cSBruce Richardson
173a9de470cSBruce Richardson /*
174a9de470cSBruce Richardson * Use rte_spinlock_trylock() to trylock a spinlock object,
175a9de470cSBruce Richardson * If it could not lock the object successfully, it would
176a9de470cSBruce Richardson * return immediately and the variable of "count" would be
177a9de470cSBruce Richardson * increased by one per times. the value of "count" could be
178a9de470cSBruce Richardson * checked as the result later.
179a9de470cSBruce Richardson */
180a9de470cSBruce Richardson static int
test_spinlock_try(__rte_unused void * arg)181f2fc83b4SThomas Monjalon test_spinlock_try(__rte_unused void *arg)
182a9de470cSBruce Richardson {
183a9de470cSBruce Richardson if (rte_spinlock_trylock(&sl_try) == 0) {
184a9de470cSBruce Richardson rte_spinlock_lock(&sl);
185a9de470cSBruce Richardson count ++;
186a9de470cSBruce Richardson rte_spinlock_unlock(&sl);
187a9de470cSBruce Richardson }
188a9de470cSBruce Richardson
189a9de470cSBruce Richardson return 0;
190a9de470cSBruce Richardson }
191a9de470cSBruce Richardson
192a9de470cSBruce Richardson
193a9de470cSBruce Richardson /*
194a9de470cSBruce Richardson * Test rte_eal_get_lcore_state() in addition to spinlocks
195a9de470cSBruce Richardson * as we have "waiting" then "running" lcores.
196a9de470cSBruce Richardson */
197a9de470cSBruce Richardson static int
test_spinlock(void)198a9de470cSBruce Richardson test_spinlock(void)
199a9de470cSBruce Richardson {
200a9de470cSBruce Richardson int ret = 0;
201a9de470cSBruce Richardson int i;
202a9de470cSBruce Richardson
203cb056611SStephen Hemminger /* worker cores should be waiting: print it */
204cb056611SStephen Hemminger RTE_LCORE_FOREACH_WORKER(i) {
205a9de470cSBruce Richardson printf("lcore %d state: %d\n", i,
206a9de470cSBruce Richardson (int) rte_eal_get_lcore_state(i));
207a9de470cSBruce Richardson }
208a9de470cSBruce Richardson
209a9de470cSBruce Richardson rte_spinlock_init(&sl);
210a9de470cSBruce Richardson rte_spinlock_init(&sl_try);
211a9de470cSBruce Richardson rte_spinlock_recursive_init(&slr);
212a9de470cSBruce Richardson for (i=0; i<RTE_MAX_LCORE; i++)
213a9de470cSBruce Richardson rte_spinlock_init(&sl_tab[i]);
214a9de470cSBruce Richardson
215a9de470cSBruce Richardson rte_spinlock_lock(&sl);
216a9de470cSBruce Richardson
217cb056611SStephen Hemminger RTE_LCORE_FOREACH_WORKER(i) {
218a9de470cSBruce Richardson rte_spinlock_lock(&sl_tab[i]);
219a9de470cSBruce Richardson rte_eal_remote_launch(test_spinlock_per_core, NULL, i);
220a9de470cSBruce Richardson }
221a9de470cSBruce Richardson
222cb056611SStephen Hemminger /* worker cores should be busy: print it */
223cb056611SStephen Hemminger RTE_LCORE_FOREACH_WORKER(i) {
224a9de470cSBruce Richardson printf("lcore %d state: %d\n", i,
225a9de470cSBruce Richardson (int) rte_eal_get_lcore_state(i));
226a9de470cSBruce Richardson }
227a9de470cSBruce Richardson rte_spinlock_unlock(&sl);
228a9de470cSBruce Richardson
229cb056611SStephen Hemminger RTE_LCORE_FOREACH_WORKER(i) {
230a9de470cSBruce Richardson rte_spinlock_unlock(&sl_tab[i]);
231a9de470cSBruce Richardson rte_delay_ms(10);
232a9de470cSBruce Richardson }
233a9de470cSBruce Richardson
234a9de470cSBruce Richardson rte_eal_mp_wait_lcore();
235a9de470cSBruce Richardson
236a9de470cSBruce Richardson rte_spinlock_recursive_lock(&slr);
237a9de470cSBruce Richardson
238a9de470cSBruce Richardson /*
239a9de470cSBruce Richardson * Try to acquire a lock that we already own
240a9de470cSBruce Richardson */
241a9de470cSBruce Richardson if(!rte_spinlock_recursive_trylock(&slr)) {
242a9de470cSBruce Richardson printf("rte_spinlock_recursive_trylock failed on a lock that "
243a9de470cSBruce Richardson "we already own\n");
244a9de470cSBruce Richardson ret = -1;
245a9de470cSBruce Richardson } else
246a9de470cSBruce Richardson rte_spinlock_recursive_unlock(&slr);
247a9de470cSBruce Richardson
248cb056611SStephen Hemminger RTE_LCORE_FOREACH_WORKER(i) {
249a9de470cSBruce Richardson rte_eal_remote_launch(test_spinlock_recursive_per_core, NULL, i);
250a9de470cSBruce Richardson }
251a9de470cSBruce Richardson rte_spinlock_recursive_unlock(&slr);
252a9de470cSBruce Richardson rte_eal_mp_wait_lcore();
253a9de470cSBruce Richardson
254a9de470cSBruce Richardson /*
255a9de470cSBruce Richardson * Test if it could return immediately from try-locking a locked object.
256cb056611SStephen Hemminger * Here it will lock the spinlock object first, then launch all the worker
257a9de470cSBruce Richardson * lcores to trylock the same spinlock object.
258cb056611SStephen Hemminger * All the worker lcores should give up try-locking a locked object and
259a9de470cSBruce Richardson * return immediately, and then increase the "count" initialized with zero
260a9de470cSBruce Richardson * by one per times.
261cb056611SStephen Hemminger * We can check if the "count" is finally equal to the number of all worker
262a9de470cSBruce Richardson * lcores to see if the behavior of try-locking a locked spinlock object
263a9de470cSBruce Richardson * is correct.
264a9de470cSBruce Richardson */
265a9de470cSBruce Richardson if (rte_spinlock_trylock(&sl_try) == 0) {
266a9de470cSBruce Richardson return -1;
267a9de470cSBruce Richardson }
268a9de470cSBruce Richardson count = 0;
269cb056611SStephen Hemminger RTE_LCORE_FOREACH_WORKER(i) {
270a9de470cSBruce Richardson rte_eal_remote_launch(test_spinlock_try, NULL, i);
271a9de470cSBruce Richardson }
272a9de470cSBruce Richardson rte_eal_mp_wait_lcore();
273a9de470cSBruce Richardson rte_spinlock_unlock(&sl_try);
274a9de470cSBruce Richardson if (rte_spinlock_is_locked(&sl)) {
275a9de470cSBruce Richardson printf("spinlock is locked but it should not be\n");
276a9de470cSBruce Richardson return -1;
277a9de470cSBruce Richardson }
278a9de470cSBruce Richardson rte_spinlock_lock(&sl);
279a9de470cSBruce Richardson if (count != ( rte_lcore_count() - 1)) {
280a9de470cSBruce Richardson ret = -1;
281a9de470cSBruce Richardson }
282a9de470cSBruce Richardson rte_spinlock_unlock(&sl);
283a9de470cSBruce Richardson
284a9de470cSBruce Richardson /*
285a9de470cSBruce Richardson * Test if it can trylock recursively.
286a9de470cSBruce Richardson * Use rte_spinlock_recursive_trylock() to check if it can lock a spinlock
287a9de470cSBruce Richardson * object recursively. Here it will try to lock a spinlock object twice.
288a9de470cSBruce Richardson */
289a9de470cSBruce Richardson if (rte_spinlock_recursive_trylock(&slr) == 0) {
290a9de470cSBruce Richardson printf("It failed to do the first spinlock_recursive_trylock but it should able to do\n");
291a9de470cSBruce Richardson return -1;
292a9de470cSBruce Richardson }
293a9de470cSBruce Richardson if (rte_spinlock_recursive_trylock(&slr) == 0) {
294a9de470cSBruce Richardson printf("It failed to do the second spinlock_recursive_trylock but it should able to do\n");
295a9de470cSBruce Richardson return -1;
296a9de470cSBruce Richardson }
297a9de470cSBruce Richardson rte_spinlock_recursive_unlock(&slr);
298a9de470cSBruce Richardson rte_spinlock_recursive_unlock(&slr);
299a9de470cSBruce Richardson
300a9de470cSBruce Richardson if (test_spinlock_perf() < 0)
301a9de470cSBruce Richardson return -1;
302a9de470cSBruce Richardson
303a9de470cSBruce Richardson return ret;
304a9de470cSBruce Richardson }
305a9de470cSBruce Richardson
306e0a8442cSBruce Richardson REGISTER_FAST_TEST(spinlock_autotest, true, true, test_spinlock);
307