1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2019 Intel Corporation
3 */
4
5 #include <stdio.h>
6 #include <string.h>
7
8 #include <rte_eal.h>
9 #include <rte_lcore.h>
10 #include <rte_debug.h>
11 #include <rte_memzone.h>
12 #include <rte_atomic.h>
13 #include <rte_timer.h>
14 #include <rte_cycles.h>
15 #include <rte_mempool.h>
16 #include <rte_random.h>
17
18 #include "test.h"
19 #include "process.h"
20
21 #define NUM_TIMERS (1 << 20) /* ~1M timers */
22 #define NUM_LCORES_NEEDED 3
23 #define TEST_INFO_MZ_NAME "test_timer_info_mz"
24 #define MSECPERSEC 1E3
25
26 #define launch_proc(ARGV) process_dup(ARGV, RTE_DIM(ARGV), __func__)
27
28 struct test_info {
29 unsigned int main_lcore;
30 unsigned int mgr_lcore;
31 unsigned int sec_lcore;
32 uint32_t timer_data_id;
33 volatile int expected_count;
34 volatile int expired_count;
35 struct rte_mempool *tim_mempool;
36 struct rte_timer *expired_timers[NUM_TIMERS];
37 int expired_timers_idx;
38 volatile int exit_flag;
39 };
40
41 static int
timer_secondary_spawn_wait(unsigned int lcore)42 timer_secondary_spawn_wait(unsigned int lcore)
43 {
44 char coremask[10];
45 #ifdef RTE_EXEC_ENV_LINUXAPP
46 char tmp[PATH_MAX] = {0};
47 char prefix[PATH_MAX] = {0};
48
49 get_current_prefix(tmp, sizeof(tmp));
50
51 snprintf(prefix, sizeof(prefix), "--file-prefix=%s", tmp);
52 #else
53 const char *prefix = "";
54 #endif
55 char const *argv[] = {
56 prgname,
57 "-c", coremask,
58 "--proc-type=secondary",
59 prefix
60 };
61
62 snprintf(coremask, sizeof(coremask), "%x", (1 << lcore));
63
64 return launch_proc(argv);
65 }
66
67 static void
handle_expired_timer(struct rte_timer * tim)68 handle_expired_timer(struct rte_timer *tim)
69 {
70 struct test_info *test_info = tim->arg;
71
72 test_info->expired_count++;
73 test_info->expired_timers[test_info->expired_timers_idx++] = tim;
74 }
75
76 static int
timer_manage_loop(void * arg)77 timer_manage_loop(void *arg)
78 {
79 #define TICK_MSECS 1
80 uint64_t tick_cycles = TICK_MSECS * rte_get_timer_hz() / MSECPERSEC;
81 uint64_t prev_tsc = 0, cur_tsc, diff_tsc;
82 struct test_info *test_info = arg;
83
84 while (!test_info->exit_flag) {
85 cur_tsc = rte_rdtsc();
86 diff_tsc = cur_tsc - prev_tsc;
87
88 if (diff_tsc > tick_cycles) {
89 /* Scan timer list for expired timers */
90 rte_timer_alt_manage(test_info->timer_data_id,
91 NULL,
92 0,
93 handle_expired_timer);
94
95 /* Return expired timer objects back to mempool */
96 rte_mempool_put_bulk(test_info->tim_mempool,
97 (void **)test_info->expired_timers,
98 test_info->expired_timers_idx);
99
100 test_info->expired_timers_idx = 0;
101
102 prev_tsc = cur_tsc;
103 }
104
105 rte_pause();
106 }
107
108 return 0;
109 }
110
111 int
test_timer_secondary(void)112 test_timer_secondary(void)
113 {
114 int proc_type = rte_eal_process_type();
115 const struct rte_memzone *mz;
116 struct test_info *test_info;
117 int ret;
118
119 if (proc_type == RTE_PROC_PRIMARY) {
120 if (rte_lcore_count() < NUM_LCORES_NEEDED) {
121 printf("Not enough cores for test_timer_secondary, expecting at least %u\n",
122 NUM_LCORES_NEEDED);
123 return TEST_SKIPPED;
124 }
125
126 mz = rte_memzone_reserve(TEST_INFO_MZ_NAME, sizeof(*test_info),
127 SOCKET_ID_ANY, 0);
128 test_info = mz->addr;
129 TEST_ASSERT_NOT_NULL(test_info, "Couldn't allocate memory for "
130 "test data");
131
132 test_info->tim_mempool = rte_mempool_create("test_timer_mp",
133 NUM_TIMERS, sizeof(struct rte_timer), 0, 0,
134 NULL, NULL, NULL, NULL, rte_socket_id(), 0);
135
136 ret = rte_timer_data_alloc(&test_info->timer_data_id);
137 TEST_ASSERT_SUCCESS(ret, "Failed to allocate timer data "
138 "instance");
139
140 unsigned int *main_lcorep = &test_info->main_lcore;
141 unsigned int *mgr_lcorep = &test_info->mgr_lcore;
142 unsigned int *sec_lcorep = &test_info->sec_lcore;
143
144 *main_lcorep = rte_get_main_lcore();
145 *mgr_lcorep = rte_get_next_lcore(*main_lcorep, 1, 1);
146 *sec_lcorep = rte_get_next_lcore(*mgr_lcorep, 1, 1);
147
148 ret = rte_eal_remote_launch(timer_manage_loop,
149 (void *)test_info,
150 *mgr_lcorep);
151 TEST_ASSERT_SUCCESS(ret, "Failed to launch timer manage loop");
152
153 ret = timer_secondary_spawn_wait(*sec_lcorep);
154 TEST_ASSERT_SUCCESS(ret, "Secondary process execution failed");
155
156 rte_delay_ms(2000);
157
158 test_info->exit_flag = 1;
159 rte_eal_wait_lcore(*mgr_lcorep);
160
161 #ifdef RTE_LIBRTE_TIMER_DEBUG
162 rte_timer_alt_dump_stats(test_info->timer_data_id, stdout);
163 #endif
164
165 return test_info->expected_count == test_info->expired_count ?
166 TEST_SUCCESS : TEST_FAILED;
167
168 } else if (proc_type == RTE_PROC_SECONDARY) {
169 uint64_t ticks, timeout_ms;
170 struct rte_timer *tim;
171 int i;
172
173 mz = rte_memzone_lookup(TEST_INFO_MZ_NAME);
174 test_info = mz->addr;
175 TEST_ASSERT_NOT_NULL(test_info, "Couldn't lookup memzone for "
176 "test info");
177
178 for (i = 0; i < NUM_TIMERS; i++) {
179 rte_mempool_get(test_info->tim_mempool, (void **)&tim);
180
181 rte_timer_init(tim);
182
183 /* generate timeouts between 10 and 160 ms */
184 timeout_ms = ((rte_rand() & 0xF) + 1) * 10;
185 ticks = timeout_ms * rte_get_timer_hz() / MSECPERSEC;
186
187 ret = rte_timer_alt_reset(test_info->timer_data_id,
188 tim, ticks, SINGLE,
189 test_info->mgr_lcore, NULL,
190 test_info);
191 if (ret < 0)
192 return TEST_FAILED;
193
194 test_info->expected_count++;
195
196 /* randomly leave timer running or stop it */
197 if (rte_rand() & 1)
198 continue;
199
200 ret = rte_timer_alt_stop(test_info->timer_data_id,
201 tim);
202 if (ret == 0) {
203 test_info->expected_count--;
204 rte_mempool_put(test_info->tim_mempool,
205 (void *)tim);
206 }
207
208 }
209
210 return TEST_SUCCESS;
211 }
212
213 return TEST_FAILED;
214 }
215
216 REGISTER_TEST_COMMAND(timer_secondary_autotest, test_timer_secondary);
217