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