--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_memzone.h>
+#include <rte_atomic.h>
+#include <rte_timer.h>
+#include <rte_cycles.h>
+#include <rte_mempool.h>
+#include <rte_random.h>
+
+#include "test.h"
+#include "process.h"
+
+#define NUM_TIMERS (1 << 20) /* ~1M timers */
+#define NUM_LCORES_NEEDED 3
+#define TEST_INFO_MZ_NAME "test_timer_info_mz"
+#define MSECPERSEC 1E3
+
+#define launch_proc(ARGV) \
+ process_dup(ARGV, sizeof(ARGV)/(sizeof(ARGV[0])), __func__)
+
+struct test_info {
+ unsigned int mstr_lcore;
+ unsigned int mgr_lcore;
+ unsigned int sec_lcore;
+ uint32_t timer_data_id;
+ volatile int expected_count;
+ volatile int expired_count;
+ struct rte_mempool *tim_mempool;
+ struct rte_timer *expired_timers[NUM_TIMERS];
+ int expired_timers_idx;
+ volatile int exit_flag;
+};
+
+static int
+timer_secondary_spawn_wait(unsigned int lcore)
+{
+ char coremask[10];
+#ifdef RTE_EXEC_ENV_LINUXAPP
+ char tmp[PATH_MAX] = {0};
+ char prefix[PATH_MAX] = {0};
+
+ get_current_prefix(tmp, sizeof(tmp));
+
+ snprintf(prefix, sizeof(prefix), "--file-prefix=%s", tmp);
+#else
+ const char *prefix = "";
+#endif
+ char const *argv[] = {
+ prgname,
+ "-c", coremask,
+ "--proc-type=secondary",
+ prefix
+ };
+
+ snprintf(coremask, sizeof(coremask), "%x", (1 << lcore));
+
+ return launch_proc(argv);
+}
+
+static void
+handle_expired_timer(struct rte_timer *tim)
+{
+ struct test_info *test_info = tim->arg;
+
+ test_info->expired_count++;
+ test_info->expired_timers[test_info->expired_timers_idx++] = tim;
+}
+
+static int
+timer_manage_loop(void *arg)
+{
+#define TICK_MSECS 1
+ uint64_t tick_cycles = TICK_MSECS * rte_get_timer_hz() / MSECPERSEC;
+ uint64_t prev_tsc = 0, cur_tsc, diff_tsc;
+ struct test_info *test_info = arg;
+
+ while (!test_info->exit_flag) {
+ cur_tsc = rte_rdtsc();
+ diff_tsc = cur_tsc - prev_tsc;
+
+ if (diff_tsc > tick_cycles) {
+ /* Scan timer list for expired timers */
+ rte_timer_alt_manage(test_info->timer_data_id,
+ NULL,
+ 0,
+ handle_expired_timer);
+
+ /* Return expired timer objects back to mempool */
+ rte_mempool_put_bulk(test_info->tim_mempool,
+ (void **)test_info->expired_timers,
+ test_info->expired_timers_idx);
+
+ test_info->expired_timers_idx = 0;
+
+ prev_tsc = cur_tsc;
+ }
+
+ rte_pause();
+ }
+
+ return 0;
+}
+
+int
+test_timer_secondary(void)
+{
+ int proc_type = rte_eal_process_type();
+ const struct rte_memzone *mz;
+ struct test_info *test_info;
+ int ret;
+
+ if (proc_type == RTE_PROC_PRIMARY) {
+ mz = rte_memzone_reserve(TEST_INFO_MZ_NAME, sizeof(*test_info),
+ SOCKET_ID_ANY, 0);
+ test_info = mz->addr;
+ TEST_ASSERT_NOT_NULL(test_info, "Couldn't allocate memory for "
+ "test data");
+
+ TEST_ASSERT(rte_lcore_count() >= NUM_LCORES_NEEDED,
+ "at least %d lcores needed to run tests",
+ NUM_LCORES_NEEDED);
+
+ test_info->tim_mempool = rte_mempool_create("test_timer_mp",
+ NUM_TIMERS, sizeof(struct rte_timer), 0, 0,
+ NULL, NULL, NULL, NULL, rte_socket_id(), 0);
+
+ ret = rte_timer_data_alloc(&test_info->timer_data_id);
+ TEST_ASSERT_SUCCESS(ret, "Failed to allocate timer data "
+ "instance");
+
+ unsigned int *mstr_lcorep = &test_info->mstr_lcore;
+ unsigned int *mgr_lcorep = &test_info->mgr_lcore;
+ unsigned int *sec_lcorep = &test_info->sec_lcore;
+
+ *mstr_lcorep = rte_get_master_lcore();
+ *mgr_lcorep = rte_get_next_lcore(*mstr_lcorep, 1, 1);
+ *sec_lcorep = rte_get_next_lcore(*mgr_lcorep, 1, 1);
+
+ ret = rte_eal_remote_launch(timer_manage_loop,
+ (void *)test_info,
+ *mgr_lcorep);
+ TEST_ASSERT_SUCCESS(ret, "Failed to launch timer manage loop");
+
+ ret = timer_secondary_spawn_wait(*sec_lcorep);
+ TEST_ASSERT_SUCCESS(ret, "Secondary process execution failed");
+
+ rte_delay_ms(2000);
+
+ test_info->exit_flag = 1;
+ rte_eal_wait_lcore(*mgr_lcorep);
+
+#ifdef RTE_LIBRTE_TIMER_DEBUG
+ rte_timer_alt_dump_stats(test_info->timer_data_id, stdout);
+#endif
+
+ return test_info->expected_count == test_info->expired_count ?
+ TEST_SUCCESS : TEST_FAILED;
+
+ } else if (proc_type == RTE_PROC_SECONDARY) {
+ uint64_t ticks, timeout_ms;
+ struct rte_timer *tim;
+ int i;
+
+ mz = rte_memzone_lookup(TEST_INFO_MZ_NAME);
+ test_info = mz->addr;
+ TEST_ASSERT_NOT_NULL(test_info, "Couldn't lookup memzone for "
+ "test info");
+
+ for (i = 0; i < NUM_TIMERS; i++) {
+ rte_mempool_get(test_info->tim_mempool, (void **)&tim);
+
+ rte_timer_init(tim);
+
+ /* generate timeouts between 10 and 160 ms */
+ timeout_ms = ((rte_rand() & 0xF) + 1) * 10;
+ ticks = timeout_ms * rte_get_timer_hz() / MSECPERSEC;
+
+ ret = rte_timer_alt_reset(test_info->timer_data_id,
+ tim, ticks, SINGLE,
+ test_info->mgr_lcore, NULL,
+ test_info);
+ if (ret < 0)
+ return TEST_FAILED;
+
+ test_info->expected_count++;
+
+ /* randomly leave timer running or stop it */
+ if (rte_rand() & 1)
+ continue;
+
+ ret = rte_timer_alt_stop(test_info->timer_data_id,
+ tim);
+ if (ret == 0) {
+ test_info->expected_count--;
+ rte_mempool_put(test_info->tim_mempool,
+ (void *)tim);
+ }
+
+ }
+
+ return TEST_SUCCESS;
+ }
+
+ return TEST_FAILED;
+}
+
+REGISTER_TEST_COMMAND(timer_secondary_autotest, test_timer_secondary);