xref: /dpdk/app/test/test_spinlock.c (revision b6a7e6852e9ab82ae0e05e2d2a0b83abca17de3b)
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