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