14418919fSjohnjiang /* SPDX-License-Identifier: BSD-3-Clause
24418919fSjohnjiang  * Copyright(c) 2019 Intel Corporation
34418919fSjohnjiang  */
44418919fSjohnjiang 
54418919fSjohnjiang #include <stdio.h>
64418919fSjohnjiang #include <string.h>
74418919fSjohnjiang 
84418919fSjohnjiang #include <rte_eal.h>
94418919fSjohnjiang #include <rte_lcore.h>
104418919fSjohnjiang #include <rte_debug.h>
114418919fSjohnjiang #include <rte_memzone.h>
124418919fSjohnjiang #include <rte_atomic.h>
134418919fSjohnjiang #include <rte_timer.h>
144418919fSjohnjiang #include <rte_cycles.h>
154418919fSjohnjiang #include <rte_mempool.h>
164418919fSjohnjiang #include <rte_random.h>
174418919fSjohnjiang 
184418919fSjohnjiang #include "test.h"
194418919fSjohnjiang #include "process.h"
204418919fSjohnjiang 
214418919fSjohnjiang #define NUM_TIMERS		(1 << 20) /* ~1M timers */
224418919fSjohnjiang #define NUM_LCORES_NEEDED	3
234418919fSjohnjiang #define TEST_INFO_MZ_NAME	"test_timer_info_mz"
244418919fSjohnjiang #define MSECPERSEC		1E3
254418919fSjohnjiang 
26*2d9fd380Sjfb8856606 #define launch_proc(ARGV) process_dup(ARGV, RTE_DIM(ARGV), __func__)
274418919fSjohnjiang 
284418919fSjohnjiang struct test_info {
29*2d9fd380Sjfb8856606 	unsigned int main_lcore;
304418919fSjohnjiang 	unsigned int mgr_lcore;
314418919fSjohnjiang 	unsigned int sec_lcore;
324418919fSjohnjiang 	uint32_t timer_data_id;
334418919fSjohnjiang 	volatile int expected_count;
344418919fSjohnjiang 	volatile int expired_count;
354418919fSjohnjiang 	struct rte_mempool *tim_mempool;
364418919fSjohnjiang 	struct rte_timer *expired_timers[NUM_TIMERS];
374418919fSjohnjiang 	int expired_timers_idx;
384418919fSjohnjiang 	volatile int exit_flag;
394418919fSjohnjiang };
404418919fSjohnjiang 
414418919fSjohnjiang static int
timer_secondary_spawn_wait(unsigned int lcore)424418919fSjohnjiang timer_secondary_spawn_wait(unsigned int lcore)
434418919fSjohnjiang {
444418919fSjohnjiang 	char coremask[10];
454418919fSjohnjiang #ifdef RTE_EXEC_ENV_LINUXAPP
464418919fSjohnjiang 	char tmp[PATH_MAX] = {0};
474418919fSjohnjiang 	char prefix[PATH_MAX] = {0};
484418919fSjohnjiang 
494418919fSjohnjiang 	get_current_prefix(tmp, sizeof(tmp));
504418919fSjohnjiang 
514418919fSjohnjiang 	snprintf(prefix, sizeof(prefix), "--file-prefix=%s", tmp);
524418919fSjohnjiang #else
534418919fSjohnjiang 	const char *prefix = "";
544418919fSjohnjiang #endif
554418919fSjohnjiang 	char const *argv[] = {
564418919fSjohnjiang 		prgname,
574418919fSjohnjiang 		"-c", coremask,
584418919fSjohnjiang 		"--proc-type=secondary",
594418919fSjohnjiang 		prefix
604418919fSjohnjiang 	};
614418919fSjohnjiang 
624418919fSjohnjiang 	snprintf(coremask, sizeof(coremask), "%x", (1 << lcore));
634418919fSjohnjiang 
644418919fSjohnjiang 	return launch_proc(argv);
654418919fSjohnjiang }
664418919fSjohnjiang 
674418919fSjohnjiang static void
handle_expired_timer(struct rte_timer * tim)684418919fSjohnjiang handle_expired_timer(struct rte_timer *tim)
694418919fSjohnjiang {
704418919fSjohnjiang 	struct test_info *test_info = tim->arg;
714418919fSjohnjiang 
724418919fSjohnjiang 	test_info->expired_count++;
734418919fSjohnjiang 	test_info->expired_timers[test_info->expired_timers_idx++] = tim;
744418919fSjohnjiang }
754418919fSjohnjiang 
764418919fSjohnjiang static int
timer_manage_loop(void * arg)774418919fSjohnjiang timer_manage_loop(void *arg)
784418919fSjohnjiang {
794418919fSjohnjiang #define TICK_MSECS 1
804418919fSjohnjiang 	uint64_t tick_cycles = TICK_MSECS * rte_get_timer_hz() / MSECPERSEC;
814418919fSjohnjiang 	uint64_t prev_tsc = 0, cur_tsc, diff_tsc;
824418919fSjohnjiang 	struct test_info *test_info = arg;
834418919fSjohnjiang 
844418919fSjohnjiang 	while (!test_info->exit_flag) {
854418919fSjohnjiang 		cur_tsc = rte_rdtsc();
864418919fSjohnjiang 		diff_tsc = cur_tsc - prev_tsc;
874418919fSjohnjiang 
884418919fSjohnjiang 		if (diff_tsc > tick_cycles) {
894418919fSjohnjiang 			/* Scan timer list for expired timers */
904418919fSjohnjiang 			rte_timer_alt_manage(test_info->timer_data_id,
914418919fSjohnjiang 					     NULL,
924418919fSjohnjiang 					     0,
934418919fSjohnjiang 					     handle_expired_timer);
944418919fSjohnjiang 
954418919fSjohnjiang 			/* Return expired timer objects back to mempool */
964418919fSjohnjiang 			rte_mempool_put_bulk(test_info->tim_mempool,
974418919fSjohnjiang 					     (void **)test_info->expired_timers,
984418919fSjohnjiang 					     test_info->expired_timers_idx);
994418919fSjohnjiang 
1004418919fSjohnjiang 			test_info->expired_timers_idx = 0;
1014418919fSjohnjiang 
1024418919fSjohnjiang 			prev_tsc = cur_tsc;
1034418919fSjohnjiang 		}
1044418919fSjohnjiang 
1054418919fSjohnjiang 		rte_pause();
1064418919fSjohnjiang 	}
1074418919fSjohnjiang 
1084418919fSjohnjiang 	return 0;
1094418919fSjohnjiang }
1104418919fSjohnjiang 
1114418919fSjohnjiang int
test_timer_secondary(void)1124418919fSjohnjiang test_timer_secondary(void)
1134418919fSjohnjiang {
1144418919fSjohnjiang 	int proc_type = rte_eal_process_type();
1154418919fSjohnjiang 	const struct rte_memzone *mz;
1164418919fSjohnjiang 	struct test_info *test_info;
1174418919fSjohnjiang 	int ret;
1184418919fSjohnjiang 
1194418919fSjohnjiang 	if (proc_type == RTE_PROC_PRIMARY) {
1204418919fSjohnjiang 		if (rte_lcore_count() < NUM_LCORES_NEEDED) {
1214418919fSjohnjiang 			printf("Not enough cores for test_timer_secondary, expecting at least %u\n",
1224418919fSjohnjiang 			       NUM_LCORES_NEEDED);
1234418919fSjohnjiang 			return TEST_SKIPPED;
1244418919fSjohnjiang 		}
1254418919fSjohnjiang 
1264418919fSjohnjiang 		mz = rte_memzone_reserve(TEST_INFO_MZ_NAME, sizeof(*test_info),
1274418919fSjohnjiang 					 SOCKET_ID_ANY, 0);
1284418919fSjohnjiang 		test_info = mz->addr;
1294418919fSjohnjiang 		TEST_ASSERT_NOT_NULL(test_info, "Couldn't allocate memory for "
1304418919fSjohnjiang 				     "test data");
1314418919fSjohnjiang 
1324418919fSjohnjiang 		test_info->tim_mempool = rte_mempool_create("test_timer_mp",
1334418919fSjohnjiang 				NUM_TIMERS, sizeof(struct rte_timer), 0, 0,
1344418919fSjohnjiang 				NULL, NULL, NULL, NULL, rte_socket_id(), 0);
1354418919fSjohnjiang 
1364418919fSjohnjiang 		ret = rte_timer_data_alloc(&test_info->timer_data_id);
1374418919fSjohnjiang 		TEST_ASSERT_SUCCESS(ret, "Failed to allocate timer data "
1384418919fSjohnjiang 				    "instance");
1394418919fSjohnjiang 
140*2d9fd380Sjfb8856606 		unsigned int *main_lcorep = &test_info->main_lcore;
1414418919fSjohnjiang 		unsigned int *mgr_lcorep = &test_info->mgr_lcore;
1424418919fSjohnjiang 		unsigned int *sec_lcorep = &test_info->sec_lcore;
1434418919fSjohnjiang 
144*2d9fd380Sjfb8856606 		*main_lcorep = rte_get_main_lcore();
145*2d9fd380Sjfb8856606 		*mgr_lcorep = rte_get_next_lcore(*main_lcorep, 1, 1);
1464418919fSjohnjiang 		*sec_lcorep = rte_get_next_lcore(*mgr_lcorep, 1, 1);
1474418919fSjohnjiang 
1484418919fSjohnjiang 		ret = rte_eal_remote_launch(timer_manage_loop,
1494418919fSjohnjiang 					    (void *)test_info,
1504418919fSjohnjiang 					    *mgr_lcorep);
1514418919fSjohnjiang 		TEST_ASSERT_SUCCESS(ret, "Failed to launch timer manage loop");
1524418919fSjohnjiang 
1534418919fSjohnjiang 		ret = timer_secondary_spawn_wait(*sec_lcorep);
1544418919fSjohnjiang 		TEST_ASSERT_SUCCESS(ret, "Secondary process execution failed");
1554418919fSjohnjiang 
1564418919fSjohnjiang 		rte_delay_ms(2000);
1574418919fSjohnjiang 
1584418919fSjohnjiang 		test_info->exit_flag = 1;
1594418919fSjohnjiang 		rte_eal_wait_lcore(*mgr_lcorep);
1604418919fSjohnjiang 
1614418919fSjohnjiang #ifdef RTE_LIBRTE_TIMER_DEBUG
1624418919fSjohnjiang 		rte_timer_alt_dump_stats(test_info->timer_data_id, stdout);
1634418919fSjohnjiang #endif
1644418919fSjohnjiang 
1654418919fSjohnjiang 		return test_info->expected_count == test_info->expired_count ?
1664418919fSjohnjiang 			TEST_SUCCESS : TEST_FAILED;
1674418919fSjohnjiang 
1684418919fSjohnjiang 	} else if (proc_type == RTE_PROC_SECONDARY) {
1694418919fSjohnjiang 		uint64_t ticks, timeout_ms;
1704418919fSjohnjiang 		struct rte_timer *tim;
1714418919fSjohnjiang 		int i;
1724418919fSjohnjiang 
1734418919fSjohnjiang 		mz = rte_memzone_lookup(TEST_INFO_MZ_NAME);
1744418919fSjohnjiang 		test_info = mz->addr;
1754418919fSjohnjiang 		TEST_ASSERT_NOT_NULL(test_info, "Couldn't lookup memzone for "
1764418919fSjohnjiang 				     "test info");
1774418919fSjohnjiang 
1784418919fSjohnjiang 		for (i = 0; i < NUM_TIMERS; i++) {
1794418919fSjohnjiang 			rte_mempool_get(test_info->tim_mempool, (void **)&tim);
1804418919fSjohnjiang 
1814418919fSjohnjiang 			rte_timer_init(tim);
1824418919fSjohnjiang 
1834418919fSjohnjiang 			/* generate timeouts between 10 and 160 ms */
1844418919fSjohnjiang 			timeout_ms = ((rte_rand() & 0xF) + 1) * 10;
1854418919fSjohnjiang 			ticks = timeout_ms * rte_get_timer_hz() / MSECPERSEC;
1864418919fSjohnjiang 
1874418919fSjohnjiang 			ret = rte_timer_alt_reset(test_info->timer_data_id,
1884418919fSjohnjiang 						  tim, ticks, SINGLE,
1894418919fSjohnjiang 						  test_info->mgr_lcore, NULL,
1904418919fSjohnjiang 						  test_info);
1914418919fSjohnjiang 			if (ret < 0)
1924418919fSjohnjiang 				return TEST_FAILED;
1934418919fSjohnjiang 
1944418919fSjohnjiang 			test_info->expected_count++;
1954418919fSjohnjiang 
1964418919fSjohnjiang 			/* randomly leave timer running or stop it */
1974418919fSjohnjiang 			if (rte_rand() & 1)
1984418919fSjohnjiang 				continue;
1994418919fSjohnjiang 
2004418919fSjohnjiang 			ret = rte_timer_alt_stop(test_info->timer_data_id,
2014418919fSjohnjiang 						 tim);
2024418919fSjohnjiang 			if (ret == 0) {
2034418919fSjohnjiang 				test_info->expected_count--;
2044418919fSjohnjiang 				rte_mempool_put(test_info->tim_mempool,
2054418919fSjohnjiang 						(void *)tim);
2064418919fSjohnjiang 			}
2074418919fSjohnjiang 
2084418919fSjohnjiang 		}
2094418919fSjohnjiang 
2104418919fSjohnjiang 		return TEST_SUCCESS;
2114418919fSjohnjiang 	}
2124418919fSjohnjiang 
2134418919fSjohnjiang 	return TEST_FAILED;
2144418919fSjohnjiang }
2154418919fSjohnjiang 
2164418919fSjohnjiang REGISTER_TEST_COMMAND(timer_secondary_autotest, test_timer_secondary);
217