xref: /f-stack/dpdk/app/test/test_ticketlock.c (revision 2d9fd380)
14418919fSjohnjiang /* SPDX-License-Identifier: BSD-3-Clause
24418919fSjohnjiang  * Copyright(c) 2018-2019 Arm Limited
34418919fSjohnjiang  */
44418919fSjohnjiang 
54418919fSjohnjiang #include <inttypes.h>
64418919fSjohnjiang #include <stdint.h>
74418919fSjohnjiang #include <stdio.h>
84418919fSjohnjiang #include <string.h>
94418919fSjohnjiang #include <sys/queue.h>
104418919fSjohnjiang #include <unistd.h>
114418919fSjohnjiang 
124418919fSjohnjiang #include <rte_atomic.h>
134418919fSjohnjiang #include <rte_common.h>
144418919fSjohnjiang #include <rte_cycles.h>
154418919fSjohnjiang #include <rte_eal.h>
164418919fSjohnjiang #include <rte_launch.h>
174418919fSjohnjiang #include <rte_lcore.h>
184418919fSjohnjiang #include <rte_memory.h>
194418919fSjohnjiang #include <rte_per_lcore.h>
204418919fSjohnjiang #include <rte_ticketlock.h>
214418919fSjohnjiang 
224418919fSjohnjiang #include "test.h"
234418919fSjohnjiang 
244418919fSjohnjiang /*
254418919fSjohnjiang  * Ticketlock test
264418919fSjohnjiang  * =============
274418919fSjohnjiang  *
284418919fSjohnjiang  * - There is a global ticketlock and a table of ticketlocks (one per lcore).
294418919fSjohnjiang  *
304418919fSjohnjiang  * - The test function takes all of these locks and launches the
31*2d9fd380Sjfb8856606  *   ``test_ticketlock_per_core()`` function on each core (except the main).
324418919fSjohnjiang  *
334418919fSjohnjiang  *   - The function takes the global lock, display something, then releases
344418919fSjohnjiang  *     the global lock.
354418919fSjohnjiang  *   - The function takes the per-lcore lock, display something, then releases
364418919fSjohnjiang  *     the per-core lock.
374418919fSjohnjiang  *
384418919fSjohnjiang  * - The main function unlocks the per-lcore locks sequentially and
394418919fSjohnjiang  *   waits between each lock. This triggers the display of a message
404418919fSjohnjiang  *   for each core, in the correct order. The autotest script checks that
414418919fSjohnjiang  *   this order is correct.
424418919fSjohnjiang  *
434418919fSjohnjiang  * - A load test is carried out, with all cores attempting to lock a single lock
444418919fSjohnjiang  *   multiple times
454418919fSjohnjiang  */
464418919fSjohnjiang 
474418919fSjohnjiang static rte_ticketlock_t tl, tl_try;
484418919fSjohnjiang static rte_ticketlock_t tl_tab[RTE_MAX_LCORE];
494418919fSjohnjiang static rte_ticketlock_recursive_t tlr;
504418919fSjohnjiang static unsigned int count;
514418919fSjohnjiang 
524418919fSjohnjiang static rte_atomic32_t synchro;
534418919fSjohnjiang 
544418919fSjohnjiang static int
test_ticketlock_per_core(__rte_unused void * arg)55*2d9fd380Sjfb8856606 test_ticketlock_per_core(__rte_unused void *arg)
564418919fSjohnjiang {
574418919fSjohnjiang 	rte_ticketlock_lock(&tl);
584418919fSjohnjiang 	printf("Global lock taken on core %u\n", rte_lcore_id());
594418919fSjohnjiang 	rte_ticketlock_unlock(&tl);
604418919fSjohnjiang 
614418919fSjohnjiang 	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
624418919fSjohnjiang 	printf("Hello from core %u !\n", rte_lcore_id());
634418919fSjohnjiang 	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
644418919fSjohnjiang 
654418919fSjohnjiang 	return 0;
664418919fSjohnjiang }
674418919fSjohnjiang 
684418919fSjohnjiang static int
test_ticketlock_recursive_per_core(__rte_unused void * arg)69*2d9fd380Sjfb8856606 test_ticketlock_recursive_per_core(__rte_unused void *arg)
704418919fSjohnjiang {
714418919fSjohnjiang 	unsigned int id = rte_lcore_id();
724418919fSjohnjiang 
734418919fSjohnjiang 	rte_ticketlock_recursive_lock(&tlr);
744418919fSjohnjiang 	printf("Global recursive lock taken on core %u - count = %d\n",
754418919fSjohnjiang 	       id, tlr.count);
764418919fSjohnjiang 	rte_ticketlock_recursive_lock(&tlr);
774418919fSjohnjiang 	printf("Global recursive lock taken on core %u - count = %d\n",
784418919fSjohnjiang 	       id, tlr.count);
794418919fSjohnjiang 	rte_ticketlock_recursive_lock(&tlr);
804418919fSjohnjiang 	printf("Global recursive lock taken on core %u - count = %d\n",
814418919fSjohnjiang 	       id, tlr.count);
824418919fSjohnjiang 
834418919fSjohnjiang 	printf("Hello from within recursive locks from core %u !\n", id);
844418919fSjohnjiang 
854418919fSjohnjiang 	rte_ticketlock_recursive_unlock(&tlr);
864418919fSjohnjiang 	printf("Global recursive lock released on core %u - count = %d\n",
874418919fSjohnjiang 	       id, tlr.count);
884418919fSjohnjiang 	rte_ticketlock_recursive_unlock(&tlr);
894418919fSjohnjiang 	printf("Global recursive lock released on core %u - count = %d\n",
904418919fSjohnjiang 	       id, tlr.count);
914418919fSjohnjiang 	rte_ticketlock_recursive_unlock(&tlr);
924418919fSjohnjiang 	printf("Global recursive lock released on core %u - count = %d\n",
934418919fSjohnjiang 	       id, tlr.count);
944418919fSjohnjiang 
954418919fSjohnjiang 	return 0;
964418919fSjohnjiang }
974418919fSjohnjiang 
984418919fSjohnjiang static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
994418919fSjohnjiang static uint64_t lcount __rte_cache_aligned;
1004418919fSjohnjiang static uint64_t lcore_count[RTE_MAX_LCORE] __rte_cache_aligned;
1014418919fSjohnjiang static uint64_t time_cost[RTE_MAX_LCORE];
1024418919fSjohnjiang 
1034418919fSjohnjiang #define MAX_LOOP 10000
1044418919fSjohnjiang 
1054418919fSjohnjiang static int
load_loop_fn(void * func_param)1064418919fSjohnjiang load_loop_fn(void *func_param)
1074418919fSjohnjiang {
1084418919fSjohnjiang 	uint64_t time_diff = 0, begin;
1094418919fSjohnjiang 	uint64_t hz = rte_get_timer_hz();
1104418919fSjohnjiang 	const int use_lock = *(int *)func_param;
1114418919fSjohnjiang 	const unsigned int lcore = rte_lcore_id();
1124418919fSjohnjiang 
113*2d9fd380Sjfb8856606 	/* wait synchro for workers */
114*2d9fd380Sjfb8856606 	if (lcore != rte_get_main_lcore())
1154418919fSjohnjiang 		while (rte_atomic32_read(&synchro) == 0)
1164418919fSjohnjiang 			;
1174418919fSjohnjiang 
1184418919fSjohnjiang 	begin = rte_rdtsc_precise();
1194418919fSjohnjiang 	while (lcore_count[lcore] < MAX_LOOP) {
1204418919fSjohnjiang 		if (use_lock)
1214418919fSjohnjiang 			rte_ticketlock_lock(&lk);
1224418919fSjohnjiang 		lcore_count[lcore]++;
1234418919fSjohnjiang 		lcount++;
1244418919fSjohnjiang 		if (use_lock)
1254418919fSjohnjiang 			rte_ticketlock_unlock(&lk);
1264418919fSjohnjiang 	}
1274418919fSjohnjiang 	time_diff = rte_rdtsc_precise() - begin;
1284418919fSjohnjiang 	time_cost[lcore] = time_diff * 1000000 / hz;
1294418919fSjohnjiang 	return 0;
1304418919fSjohnjiang }
1314418919fSjohnjiang 
1324418919fSjohnjiang static int
test_ticketlock_perf(void)1334418919fSjohnjiang test_ticketlock_perf(void)
1344418919fSjohnjiang {
1354418919fSjohnjiang 	unsigned int i;
1364418919fSjohnjiang 	uint64_t tcount = 0;
1374418919fSjohnjiang 	uint64_t total_time = 0;
1384418919fSjohnjiang 	int lock = 0;
1394418919fSjohnjiang 	const unsigned int lcore = rte_lcore_id();
1404418919fSjohnjiang 
1414418919fSjohnjiang 	printf("\nTest with no lock on single core...\n");
1424418919fSjohnjiang 	load_loop_fn(&lock);
1434418919fSjohnjiang 	printf("Core [%u] cost time = %"PRIu64" us\n", lcore, time_cost[lcore]);
1444418919fSjohnjiang 	memset(lcore_count, 0, sizeof(lcore_count));
1454418919fSjohnjiang 	memset(time_cost, 0, sizeof(time_cost));
1464418919fSjohnjiang 
1474418919fSjohnjiang 	printf("\nTest with lock on single core...\n");
1484418919fSjohnjiang 	lock = 1;
1494418919fSjohnjiang 	load_loop_fn(&lock);
1504418919fSjohnjiang 	printf("Core [%u] cost time = %"PRIu64" us\n", lcore, time_cost[lcore]);
1514418919fSjohnjiang 	memset(lcore_count, 0, sizeof(lcore_count));
1524418919fSjohnjiang 	memset(time_cost, 0, sizeof(time_cost));
1534418919fSjohnjiang 
1544418919fSjohnjiang 	lcount = 0;
1554418919fSjohnjiang 	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
1564418919fSjohnjiang 
157*2d9fd380Sjfb8856606 	/* Clear synchro and start workers */
1584418919fSjohnjiang 	rte_atomic32_set(&synchro, 0);
159*2d9fd380Sjfb8856606 	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MAIN);
1604418919fSjohnjiang 
161*2d9fd380Sjfb8856606 	/* start synchro and launch test on main */
1624418919fSjohnjiang 	rte_atomic32_set(&synchro, 1);
1634418919fSjohnjiang 	load_loop_fn(&lock);
1644418919fSjohnjiang 
1654418919fSjohnjiang 	rte_eal_mp_wait_lcore();
1664418919fSjohnjiang 
1674418919fSjohnjiang 	RTE_LCORE_FOREACH(i) {
1684418919fSjohnjiang 		printf("Core [%u] cost time = %"PRIu64" us\n", i, time_cost[i]);
1694418919fSjohnjiang 		tcount += lcore_count[i];
1704418919fSjohnjiang 		total_time += time_cost[i];
1714418919fSjohnjiang 	}
1724418919fSjohnjiang 
1734418919fSjohnjiang 	if (tcount != lcount)
1744418919fSjohnjiang 		return -1;
1754418919fSjohnjiang 
1764418919fSjohnjiang 	printf("Total cost time = %"PRIu64" us\n", total_time);
1774418919fSjohnjiang 
1784418919fSjohnjiang 	return 0;
1794418919fSjohnjiang }
1804418919fSjohnjiang 
1814418919fSjohnjiang /*
1824418919fSjohnjiang  * Use rte_ticketlock_trylock() to trylock a ticketlock object,
1834418919fSjohnjiang  * If it could not lock the object successfully, it would
1844418919fSjohnjiang  * return immediately and the variable of "count" would be
1854418919fSjohnjiang  * increased by one per times. the value of "count" could be
1864418919fSjohnjiang  * checked as the result later.
1874418919fSjohnjiang  */
1884418919fSjohnjiang static int
test_ticketlock_try(__rte_unused void * arg)189*2d9fd380Sjfb8856606 test_ticketlock_try(__rte_unused void *arg)
1904418919fSjohnjiang {
1914418919fSjohnjiang 	if (rte_ticketlock_trylock(&tl_try) == 0) {
1924418919fSjohnjiang 		rte_ticketlock_lock(&tl);
1934418919fSjohnjiang 		count++;
1944418919fSjohnjiang 		rte_ticketlock_unlock(&tl);
1954418919fSjohnjiang 	}
1964418919fSjohnjiang 
1974418919fSjohnjiang 	return 0;
1984418919fSjohnjiang }
1994418919fSjohnjiang 
2004418919fSjohnjiang 
2014418919fSjohnjiang /*
2024418919fSjohnjiang  * Test rte_eal_get_lcore_state() in addition to ticketlocks
2034418919fSjohnjiang  * as we have "waiting" then "running" lcores.
2044418919fSjohnjiang  */
2054418919fSjohnjiang static int
test_ticketlock(void)2064418919fSjohnjiang test_ticketlock(void)
2074418919fSjohnjiang {
2084418919fSjohnjiang 	int ret = 0;
2094418919fSjohnjiang 	int i;
2104418919fSjohnjiang 
211*2d9fd380Sjfb8856606 	/* worker cores should be waiting: print it */
212*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(i) {
2134418919fSjohnjiang 		printf("lcore %d state: %d\n", i,
2144418919fSjohnjiang 		       (int) rte_eal_get_lcore_state(i));
2154418919fSjohnjiang 	}
2164418919fSjohnjiang 
2174418919fSjohnjiang 	rte_ticketlock_init(&tl);
2184418919fSjohnjiang 	rte_ticketlock_init(&tl_try);
2194418919fSjohnjiang 	rte_ticketlock_recursive_init(&tlr);
220*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(i) {
2214418919fSjohnjiang 		rte_ticketlock_init(&tl_tab[i]);
2224418919fSjohnjiang 	}
2234418919fSjohnjiang 
2244418919fSjohnjiang 	rte_ticketlock_lock(&tl);
2254418919fSjohnjiang 
226*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(i) {
2274418919fSjohnjiang 		rte_ticketlock_lock(&tl_tab[i]);
2284418919fSjohnjiang 		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
2294418919fSjohnjiang 	}
2304418919fSjohnjiang 
231*2d9fd380Sjfb8856606 	/* worker cores should be busy: print it */
232*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(i) {
2334418919fSjohnjiang 		printf("lcore %d state: %d\n", i,
2344418919fSjohnjiang 		       (int) rte_eal_get_lcore_state(i));
2354418919fSjohnjiang 	}
2364418919fSjohnjiang 	rte_ticketlock_unlock(&tl);
2374418919fSjohnjiang 
238*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(i) {
2394418919fSjohnjiang 		rte_ticketlock_unlock(&tl_tab[i]);
2404418919fSjohnjiang 		rte_delay_ms(10);
2414418919fSjohnjiang 	}
2424418919fSjohnjiang 
2434418919fSjohnjiang 	rte_eal_mp_wait_lcore();
2444418919fSjohnjiang 
2454418919fSjohnjiang 	rte_ticketlock_recursive_lock(&tlr);
2464418919fSjohnjiang 
2474418919fSjohnjiang 	/*
2484418919fSjohnjiang 	 * Try to acquire a lock that we already own
2494418919fSjohnjiang 	 */
2504418919fSjohnjiang 	if (!rte_ticketlock_recursive_trylock(&tlr)) {
2514418919fSjohnjiang 		printf("rte_ticketlock_recursive_trylock failed on a lock that "
2524418919fSjohnjiang 		       "we already own\n");
2534418919fSjohnjiang 		ret = -1;
2544418919fSjohnjiang 	} else
2554418919fSjohnjiang 		rte_ticketlock_recursive_unlock(&tlr);
2564418919fSjohnjiang 
257*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(i) {
2584418919fSjohnjiang 		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
2594418919fSjohnjiang 					NULL, i);
2604418919fSjohnjiang 	}
2614418919fSjohnjiang 	rte_ticketlock_recursive_unlock(&tlr);
2624418919fSjohnjiang 	rte_eal_mp_wait_lcore();
2634418919fSjohnjiang 
2644418919fSjohnjiang 	/*
2654418919fSjohnjiang 	 * Test if it could return immediately from try-locking a locked object.
2664418919fSjohnjiang 	 * Here it will lock the ticketlock object first, then launch all the
267*2d9fd380Sjfb8856606 	 * worker lcores to trylock the same ticketlock object.
268*2d9fd380Sjfb8856606 	 * All the worker lcores should give up try-locking a locked object and
2694418919fSjohnjiang 	 * return immediately, and then increase the "count" initialized with
2704418919fSjohnjiang 	 * zero by one per times.
2714418919fSjohnjiang 	 * We can check if the "count" is finally equal to the number of all
272*2d9fd380Sjfb8856606 	 * worker lcores to see if the behavior of try-locking a locked
2734418919fSjohnjiang 	 * ticketlock object is correct.
2744418919fSjohnjiang 	 */
2754418919fSjohnjiang 	if (rte_ticketlock_trylock(&tl_try) == 0)
2764418919fSjohnjiang 		return -1;
2774418919fSjohnjiang 
2784418919fSjohnjiang 	count = 0;
279*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(i) {
2804418919fSjohnjiang 		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
2814418919fSjohnjiang 	}
2824418919fSjohnjiang 	rte_eal_mp_wait_lcore();
2834418919fSjohnjiang 	rte_ticketlock_unlock(&tl_try);
2844418919fSjohnjiang 	if (rte_ticketlock_is_locked(&tl)) {
2854418919fSjohnjiang 		printf("ticketlock is locked but it should not be\n");
2864418919fSjohnjiang 		return -1;
2874418919fSjohnjiang 	}
2884418919fSjohnjiang 	rte_ticketlock_lock(&tl);
2894418919fSjohnjiang 	if (count != (rte_lcore_count() - 1))
2904418919fSjohnjiang 		ret = -1;
2914418919fSjohnjiang 
2924418919fSjohnjiang 	rte_ticketlock_unlock(&tl);
2934418919fSjohnjiang 
2944418919fSjohnjiang 	/*
2954418919fSjohnjiang 	 * Test if it can trylock recursively.
2964418919fSjohnjiang 	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
2974418919fSjohnjiang 	 * a ticketlock object recursively. Here it will try to lock a
2984418919fSjohnjiang 	 * ticketlock object twice.
2994418919fSjohnjiang 	 */
3004418919fSjohnjiang 	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
3014418919fSjohnjiang 		printf("It failed to do the first ticketlock_recursive_trylock "
3024418919fSjohnjiang 			   "but it should able to do\n");
3034418919fSjohnjiang 		return -1;
3044418919fSjohnjiang 	}
3054418919fSjohnjiang 	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
3064418919fSjohnjiang 		printf("It failed to do the second ticketlock_recursive_trylock "
3074418919fSjohnjiang 			   "but it should able to do\n");
3084418919fSjohnjiang 		return -1;
3094418919fSjohnjiang 	}
3104418919fSjohnjiang 	rte_ticketlock_recursive_unlock(&tlr);
3114418919fSjohnjiang 	rte_ticketlock_recursive_unlock(&tlr);
3124418919fSjohnjiang 
3134418919fSjohnjiang 	if (test_ticketlock_perf() < 0)
3144418919fSjohnjiang 		return -1;
3154418919fSjohnjiang 
3164418919fSjohnjiang 	return ret;
3174418919fSjohnjiang }
3184418919fSjohnjiang 
3194418919fSjohnjiang REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
320