Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2020 Red Hat, Inc.
3 : : */
4 : :
5 : : #include <string.h>
6 : :
7 : : #include <rte_common.h>
8 : : #include <rte_errno.h>
9 : : #include <rte_lcore.h>
10 : : #include <rte_thread.h>
11 : :
12 : : #include "test.h"
13 : :
14 : : struct thread_context {
15 : : enum { Thread_INIT, Thread_ERROR, Thread_DONE } state;
16 : : bool lcore_id_any;
17 : : rte_thread_t id;
18 : : unsigned int *registered_count;
19 : : };
20 : :
21 [ - + ]: 129 : static uint32_t thread_loop(void *arg)
22 : : {
23 : : struct thread_context *t = arg;
24 : : unsigned int lcore_id;
25 : :
26 : : lcore_id = rte_lcore_id();
27 [ - + ]: 129 : if (lcore_id != LCORE_ID_ANY) {
28 : : printf("Error: incorrect lcore id for new thread %u\n", lcore_id);
29 : 0 : t->state = Thread_ERROR;
30 : : }
31 [ + + ]: 129 : if (rte_thread_register() < 0)
32 : 2 : printf("Warning: could not register new thread (this might be expected during this test), reason %s\n",
33 : : rte_strerror(rte_errno));
34 : : lcore_id = rte_lcore_id();
35 [ + + + - : 129 : if ((t->lcore_id_any && lcore_id != LCORE_ID_ANY) ||
+ + ]
36 [ - + ]: 127 : (!t->lcore_id_any && lcore_id == LCORE_ID_ANY)) {
37 [ # # ]: 0 : printf("Error: could not register new thread, got %u while %sexpecting %u\n",
38 : : lcore_id, t->lcore_id_any ? "" : "not ", LCORE_ID_ANY);
39 : 0 : t->state = Thread_ERROR;
40 : : }
41 : : /* Report register happened to the control thread. */
42 : 129 : __atomic_fetch_add(t->registered_count, 1, __ATOMIC_RELEASE);
43 : :
44 : : /* Wait for release from the control thread. */
45 [ + + ]:18078278071 : while (__atomic_load_n(t->registered_count, __ATOMIC_ACQUIRE) != 0)
46 : : ;
47 : 129 : rte_thread_unregister();
48 : : lcore_id = rte_lcore_id();
49 [ - + ]: 129 : if (lcore_id != LCORE_ID_ANY) {
50 : : printf("Error: could not unregister new thread, %u still assigned\n",
51 : : lcore_id);
52 : 0 : t->state = Thread_ERROR;
53 : : }
54 : :
55 [ + - ]: 129 : if (t->state != Thread_ERROR)
56 : 129 : t->state = Thread_DONE;
57 : :
58 : 129 : return 0;
59 : : }
60 : :
61 : : static int
62 : 1 : test_non_eal_lcores(unsigned int eal_threads_count)
63 : : {
64 : : struct thread_context thread_contexts[RTE_MAX_LCORE];
65 : : unsigned int non_eal_threads_count;
66 : : unsigned int registered_count;
67 : : struct thread_context *t;
68 : : unsigned int i;
69 : : int ret;
70 : :
71 : : non_eal_threads_count = 0;
72 : 1 : registered_count = 0;
73 : :
74 : : /* Try to create as many threads as possible. */
75 [ + + ]: 127 : for (i = 0; i < RTE_MAX_LCORE - eal_threads_count; i++) {
76 : 126 : t = &thread_contexts[i];
77 : 126 : t->state = Thread_INIT;
78 : 126 : t->registered_count = ®istered_count;
79 : 126 : t->lcore_id_any = false;
80 [ + - ]: 126 : if (rte_thread_create(&t->id, NULL, thread_loop, t) != 0)
81 : : break;
82 : 126 : non_eal_threads_count++;
83 : : }
84 : : printf("non-EAL threads count: %u\n", non_eal_threads_count);
85 : : /* Wait all non-EAL threads to register. */
86 [ + + ]: 8789560 : while (__atomic_load_n(®istered_count, __ATOMIC_ACQUIRE) !=
87 : : non_eal_threads_count)
88 : : ;
89 : :
90 : : /* We managed to create the max number of threads, let's try to create
91 : : * one more. This will allow one more check.
92 : : */
93 [ - + ]: 1 : if (eal_threads_count + non_eal_threads_count < RTE_MAX_LCORE)
94 : 0 : goto skip_lcore_any;
95 : 1 : t = &thread_contexts[non_eal_threads_count];
96 : 1 : t->state = Thread_INIT;
97 : 1 : t->registered_count = ®istered_count;
98 : 1 : t->lcore_id_any = true;
99 [ + - ]: 1 : if (rte_thread_create(&t->id, NULL, thread_loop, t) == 0) {
100 : 1 : non_eal_threads_count++;
101 : : printf("non-EAL threads count: %u\n", non_eal_threads_count);
102 [ + - ]: 1 : while (__atomic_load_n(®istered_count, __ATOMIC_ACQUIRE) !=
103 : : non_eal_threads_count)
104 : : ;
105 : : }
106 : :
107 : 1 : skip_lcore_any:
108 : : /* Release all threads, and check their states. */
109 : 1 : __atomic_store_n(®istered_count, 0, __ATOMIC_RELEASE);
110 : : ret = 0;
111 [ + + ]: 128 : for (i = 0; i < non_eal_threads_count; i++) {
112 : 127 : t = &thread_contexts[i];
113 : 127 : rte_thread_join(t->id, NULL);
114 [ - + ]: 127 : if (t->state != Thread_DONE)
115 : : ret = -1;
116 : : }
117 : :
118 : 1 : return ret;
119 : : }
120 : :
121 : : struct limit_lcore_context {
122 : : unsigned int init;
123 : : unsigned int max;
124 : : unsigned int uninit;
125 : : };
126 : :
127 : : static int
128 : 12 : limit_lcores_init(unsigned int lcore_id __rte_unused, void *arg)
129 : : {
130 : : struct limit_lcore_context *l = arg;
131 : :
132 : 12 : l->init++;
133 [ + + ]: 12 : if (l->init > l->max)
134 : 2 : return -1;
135 : : return 0;
136 : : }
137 : :
138 : : static void
139 : 10 : limit_lcores_uninit(unsigned int lcore_id __rte_unused, void *arg)
140 : : {
141 : : struct limit_lcore_context *l = arg;
142 : :
143 : 10 : l->uninit++;
144 : 10 : }
145 : :
146 : : static int
147 : 1 : test_lcores_callback(unsigned int eal_threads_count)
148 : : {
149 : : struct limit_lcore_context l;
150 : : void *handle;
151 : :
152 : : /* Refuse last lcore => callback register error. */
153 : : memset(&l, 0, sizeof(l));
154 : 1 : l.max = eal_threads_count - 1;
155 : 1 : handle = rte_lcore_callback_register("limit", limit_lcores_init,
156 : : limit_lcores_uninit, &l);
157 [ - + ]: 1 : if (handle != NULL) {
158 : : printf("Error: lcore callback register should have failed\n");
159 : 0 : goto error;
160 : : }
161 : : /* Refusal happens at the n th call to the init callback.
162 : : * Besides, n - 1 were accepted, so we expect as many uninit calls when
163 : : * the rollback happens.
164 : : */
165 [ - + ]: 1 : if (l.init != eal_threads_count) {
166 : : printf("Error: lcore callback register failed but incorrect init calls, expected %u, got %u\n",
167 : : eal_threads_count, l.init);
168 : 0 : goto error;
169 : : }
170 [ - + ]: 1 : if (l.uninit != eal_threads_count - 1) {
171 : : printf("Error: lcore callback register failed but incorrect uninit calls, expected %u, got %u\n",
172 : : eal_threads_count - 1, l.uninit);
173 : 0 : goto error;
174 : : }
175 : :
176 : : /* Accept all lcore and unregister. */
177 : : memset(&l, 0, sizeof(l));
178 : 1 : l.max = eal_threads_count;
179 : 1 : handle = rte_lcore_callback_register("limit", limit_lcores_init,
180 : : limit_lcores_uninit, &l);
181 [ - + ]: 1 : if (handle == NULL) {
182 : : printf("Error: lcore callback register failed\n");
183 : 0 : goto error;
184 : : }
185 [ - + ]: 1 : if (l.uninit != 0) {
186 : : printf("Error: lcore callback register succeeded but incorrect uninit calls, expected 0, got %u\n",
187 : : l.uninit);
188 : 0 : goto error;
189 : : }
190 : 1 : rte_lcore_callback_unregister(handle);
191 : : handle = NULL;
192 [ - + ]: 1 : if (l.init != eal_threads_count) {
193 : : printf("Error: lcore callback unregister done but incorrect init calls, expected %u, got %u\n",
194 : : eal_threads_count, l.init);
195 : 0 : goto error;
196 : : }
197 [ - + ]: 1 : if (l.uninit != eal_threads_count) {
198 : : printf("Error: lcore callback unregister done but incorrect uninit calls, expected %u, got %u\n",
199 : : eal_threads_count, l.uninit);
200 : 0 : goto error;
201 : : }
202 : :
203 : : return 0;
204 : :
205 : 0 : error:
206 [ # # ]: 0 : if (handle != NULL)
207 : 0 : rte_lcore_callback_unregister(handle);
208 : :
209 : : return -1;
210 : : }
211 : :
212 : : static int
213 : 1 : test_non_eal_lcores_callback(unsigned int eal_threads_count)
214 : : {
215 : : struct thread_context thread_contexts[2];
216 : : unsigned int non_eal_threads_count = 0;
217 : 1 : struct limit_lcore_context l[2] = {};
218 : 1 : unsigned int registered_count = 0;
219 : : struct thread_context *t;
220 : : void *handle[2] = {};
221 : : unsigned int i;
222 : : int ret;
223 : :
224 : : /* This test requires two empty slots to be sure lcore init refusal is
225 : : * because of callback execution.
226 : : */
227 [ + - ]: 1 : if (eal_threads_count + 2 >= RTE_MAX_LCORE)
228 : : return 0;
229 : :
230 : : /* Register two callbacks:
231 : : * - first one accepts any lcore,
232 : : * - second one accepts all EAL lcore + one more for the first non-EAL
233 : : * thread, then refuses the next lcore.
234 : : */
235 : 1 : l[0].max = UINT_MAX;
236 : 1 : handle[0] = rte_lcore_callback_register("no_limit", limit_lcores_init,
237 : : limit_lcores_uninit, &l[0]);
238 [ - + ]: 1 : if (handle[0] == NULL) {
239 : : printf("Error: lcore callback [0] register failed\n");
240 : 0 : goto error;
241 : : }
242 : 1 : l[1].max = eal_threads_count + 1;
243 : 1 : handle[1] = rte_lcore_callback_register("limit", limit_lcores_init,
244 : : limit_lcores_uninit, &l[1]);
245 [ - + ]: 1 : if (handle[1] == NULL) {
246 : : printf("Error: lcore callback [1] register failed\n");
247 : 0 : goto error;
248 : : }
249 [ + - - + ]: 1 : if (l[0].init != eal_threads_count || l[1].init != eal_threads_count) {
250 : 0 : printf("Error: lcore callbacks register succeeded but incorrect init calls, expected %u, %u, got %u, %u\n",
251 : : eal_threads_count, eal_threads_count,
252 : : l[0].init, l[1].init);
253 : 0 : goto error;
254 : : }
255 [ + - - + ]: 1 : if (l[0].uninit != 0 || l[1].uninit != 0) {
256 : 0 : printf("Error: lcore callbacks register succeeded but incorrect uninit calls, expected 0, 1, got %u, %u\n",
257 : : l[0].uninit, l[1].uninit);
258 : 0 : goto error;
259 : : }
260 : : /* First thread that expects a valid lcore id. */
261 : : t = &thread_contexts[0];
262 : 1 : t->state = Thread_INIT;
263 : 1 : t->registered_count = ®istered_count;
264 : 1 : t->lcore_id_any = false;
265 [ - + ]: 1 : if (rte_thread_create(&t->id, NULL, thread_loop, t) != 0)
266 : 0 : goto cleanup_threads;
267 : : non_eal_threads_count++;
268 [ + + ]: 8497428 : while (__atomic_load_n(®istered_count, __ATOMIC_ACQUIRE) !=
269 : : non_eal_threads_count)
270 : : ;
271 [ + - ]: 1 : if (l[0].init != eal_threads_count + 1 ||
272 [ - + ]: 1 : l[1].init != eal_threads_count + 1) {
273 : 0 : printf("Error: incorrect init calls, expected %u, %u, got %u, %u\n",
274 : : eal_threads_count + 1, eal_threads_count + 1,
275 : : l[0].init, l[1].init);
276 : 0 : goto cleanup_threads;
277 : : }
278 [ + - - + ]: 1 : if (l[0].uninit != 0 || l[1].uninit != 0) {
279 : 0 : printf("Error: incorrect uninit calls, expected 0, 0, got %u, %u\n",
280 : : l[0].uninit, l[1].uninit);
281 : 0 : goto cleanup_threads;
282 : : }
283 : : /* Second thread, that expects LCORE_ID_ANY because of init refusal. */
284 : : t = &thread_contexts[1];
285 : 1 : t->state = Thread_INIT;
286 : 1 : t->registered_count = ®istered_count;
287 : 1 : t->lcore_id_any = true;
288 [ - + ]: 1 : if (rte_thread_create(&t->id, NULL, thread_loop, t) != 0)
289 : 0 : goto cleanup_threads;
290 : : non_eal_threads_count++;
291 [ + + ]: 8657772 : while (__atomic_load_n(®istered_count, __ATOMIC_ACQUIRE) !=
292 : : non_eal_threads_count)
293 : : ;
294 [ + - ]: 1 : if (l[0].init != eal_threads_count + 2 ||
295 [ - + ]: 1 : l[1].init != eal_threads_count + 2) {
296 : 0 : printf("Error: incorrect init calls, expected %u, %u, got %u, %u\n",
297 : : eal_threads_count + 2, eal_threads_count + 2,
298 : : l[0].init, l[1].init);
299 : 0 : goto cleanup_threads;
300 : : }
301 [ + - - + ]: 1 : if (l[0].uninit != 1 || l[1].uninit != 0) {
302 : 0 : printf("Error: incorrect uninit calls, expected 1, 0, got %u, %u\n",
303 : : l[0].uninit, l[1].uninit);
304 : 0 : goto cleanup_threads;
305 : : }
306 : 1 : rte_lcore_dump(stdout);
307 : : /* Release all threads, and check their states. */
308 : 1 : __atomic_store_n(®istered_count, 0, __ATOMIC_RELEASE);
309 : : ret = 0;
310 [ + + ]: 3 : for (i = 0; i < non_eal_threads_count; i++) {
311 : 2 : t = &thread_contexts[i];
312 : 2 : rte_thread_join(t->id, NULL);
313 [ - + ]: 2 : if (t->state != Thread_DONE)
314 : : ret = -1;
315 : : }
316 [ - + ]: 1 : if (ret < 0)
317 : 0 : goto error;
318 : 1 : rte_lcore_dump(stdout);
319 [ + - - + ]: 1 : if (l[0].uninit != 2 || l[1].uninit != 1) {
320 : 0 : printf("Error: threads reported having successfully registered and unregistered, but incorrect uninit calls, expected 2, 1, got %u, %u\n",
321 : : l[0].uninit, l[1].uninit);
322 : 0 : goto error;
323 : : }
324 : 1 : rte_lcore_callback_unregister(handle[0]);
325 : 1 : rte_lcore_callback_unregister(handle[1]);
326 : 1 : return 0;
327 : :
328 : 0 : cleanup_threads:
329 : : /* Release all threads */
330 : 0 : __atomic_store_n(®istered_count, 0, __ATOMIC_RELEASE);
331 [ # # ]: 0 : for (i = 0; i < non_eal_threads_count; i++) {
332 : 0 : t = &thread_contexts[i];
333 : 0 : rte_thread_join(t->id, NULL);
334 : : }
335 : 0 : error:
336 [ # # ]: 0 : if (handle[1] != NULL)
337 : 0 : rte_lcore_callback_unregister(handle[1]);
338 [ # # ]: 0 : if (handle[0] != NULL)
339 : 0 : rte_lcore_callback_unregister(handle[0]);
340 : : return -1;
341 : : }
342 : :
343 : 1 : static uint32_t ctrl_thread_loop(void *arg)
344 : : {
345 : : struct thread_context *t = arg;
346 : :
347 : : printf("Control thread running successfully\n");
348 : :
349 : : /* Set the thread state to DONE */
350 : 1 : t->state = Thread_DONE;
351 : :
352 : 1 : return 0;
353 : : }
354 : :
355 : : static int
356 : 1 : test_ctrl_thread(void)
357 : : {
358 : : struct thread_context ctrl_thread_context;
359 : : struct thread_context *t;
360 : :
361 : : /* Create one control thread */
362 : : t = &ctrl_thread_context;
363 : 1 : t->state = Thread_INIT;
364 [ + - ]: 1 : if (rte_thread_create_control(&t->id, "dpdk-test-ctrlt",
365 : : ctrl_thread_loop, t) != 0)
366 : : return -1;
367 : :
368 : : /* Wait till the control thread exits.
369 : : * This also acts as the barrier such that the memory operations
370 : : * in control thread are visible to this thread.
371 : : */
372 : 1 : rte_thread_join(t->id, NULL);
373 : :
374 : : /* Check if the control thread set the correct state */
375 [ - + ]: 1 : if (t->state != Thread_DONE)
376 : 0 : return -1;
377 : :
378 : : return 0;
379 : : }
380 : :
381 : : static int
382 : 1 : test_lcores(void)
383 : : {
384 : : unsigned int eal_threads_count = 0;
385 : : unsigned int i;
386 : :
387 [ + + ]: 129 : for (i = 0; i < RTE_MAX_LCORE; i++) {
388 [ + + ]: 128 : if (!rte_lcore_has_role(i, ROLE_OFF))
389 : 2 : eal_threads_count++;
390 : : }
391 [ - + ]: 1 : if (eal_threads_count == 0) {
392 : : printf("Error: something is broken, no EAL thread detected.\n");
393 : 0 : return TEST_FAILED;
394 : : }
395 : : printf("EAL threads count: %u, RTE_MAX_LCORE=%u\n", eal_threads_count,
396 : : RTE_MAX_LCORE);
397 : 1 : rte_lcore_dump(stdout);
398 : :
399 [ + - ]: 1 : if (test_non_eal_lcores(eal_threads_count) < 0)
400 : : return TEST_FAILED;
401 : :
402 [ + - ]: 1 : if (test_lcores_callback(eal_threads_count) < 0)
403 : : return TEST_FAILED;
404 : :
405 [ + - ]: 1 : if (test_non_eal_lcores_callback(eal_threads_count) < 0)
406 : : return TEST_FAILED;
407 : :
408 [ - + ]: 1 : if (test_ctrl_thread() < 0)
409 : 0 : return TEST_FAILED;
410 : :
411 : : return TEST_SUCCESS;
412 : : }
413 : :
414 : 235 : REGISTER_FAST_TEST(lcores_autotest, true, true, test_lcores);
|