xref: /dpdk/app/test/test_lcores.c (revision 68a03efeed657e6e05f281479b33b51102797e15)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2020 Red Hat, Inc.
3  */
4 
5 #include <pthread.h>
6 #include <string.h>
7 
8 #include <rte_common.h>
9 #include <rte_errno.h>
10 #include <rte_lcore.h>
11 
12 #include "test.h"
13 
14 struct thread_context {
15 	enum { INIT, ERROR, DONE } state;
16 	bool lcore_id_any;
17 	pthread_t id;
18 	unsigned int *registered_count;
19 };
20 
21 static void *thread_loop(void *arg)
22 {
23 	struct thread_context *t = arg;
24 	unsigned int lcore_id;
25 
26 	lcore_id = rte_lcore_id();
27 	if (lcore_id != LCORE_ID_ANY) {
28 		printf("Error: incorrect lcore id for new thread %u\n", lcore_id);
29 		t->state = ERROR;
30 	}
31 	if (rte_thread_register() < 0)
32 		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 	if ((t->lcore_id_any && lcore_id != LCORE_ID_ANY) ||
36 			(!t->lcore_id_any && lcore_id == LCORE_ID_ANY)) {
37 		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 		t->state = ERROR;
40 	}
41 	/* Report register happened to the control thread. */
42 	__atomic_add_fetch(t->registered_count, 1, __ATOMIC_RELEASE);
43 
44 	/* Wait for release from the control thread. */
45 	while (__atomic_load_n(t->registered_count, __ATOMIC_ACQUIRE) != 0)
46 		;
47 	rte_thread_unregister();
48 	lcore_id = rte_lcore_id();
49 	if (lcore_id != LCORE_ID_ANY) {
50 		printf("Error: could not unregister new thread, %u still assigned\n",
51 			lcore_id);
52 		t->state = ERROR;
53 	}
54 
55 	if (t->state != ERROR)
56 		t->state = DONE;
57 
58 	return NULL;
59 }
60 
61 static int
62 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 	registered_count = 0;
73 
74 	/* Try to create as many threads as possible. */
75 	for (i = 0; i < RTE_MAX_LCORE - eal_threads_count; i++) {
76 		t = &thread_contexts[i];
77 		t->state = INIT;
78 		t->registered_count = &registered_count;
79 		t->lcore_id_any = false;
80 		if (pthread_create(&t->id, NULL, thread_loop, t) != 0)
81 			break;
82 		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 	while (__atomic_load_n(&registered_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 	if (eal_threads_count + non_eal_threads_count < RTE_MAX_LCORE)
94 		goto skip_lcore_any;
95 	t = &thread_contexts[non_eal_threads_count];
96 	t->state = INIT;
97 	t->registered_count = &registered_count;
98 	t->lcore_id_any = true;
99 	if (pthread_create(&t->id, NULL, thread_loop, t) == 0) {
100 		non_eal_threads_count++;
101 		printf("non-EAL threads count: %u\n", non_eal_threads_count);
102 		while (__atomic_load_n(&registered_count, __ATOMIC_ACQUIRE) !=
103 				non_eal_threads_count)
104 			;
105 	}
106 
107 skip_lcore_any:
108 	/* Release all threads, and check their states. */
109 	__atomic_store_n(&registered_count, 0, __ATOMIC_RELEASE);
110 	ret = 0;
111 	for (i = 0; i < non_eal_threads_count; i++) {
112 		t = &thread_contexts[i];
113 		pthread_join(t->id, NULL);
114 		if (t->state != DONE)
115 			ret = -1;
116 	}
117 
118 	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 limit_lcores_init(unsigned int lcore_id __rte_unused, void *arg)
129 {
130 	struct limit_lcore_context *l = arg;
131 
132 	l->init++;
133 	if (l->init > l->max)
134 		return -1;
135 	return 0;
136 }
137 
138 static void
139 limit_lcores_uninit(unsigned int lcore_id __rte_unused, void *arg)
140 {
141 	struct limit_lcore_context *l = arg;
142 
143 	l->uninit++;
144 }
145 
146 static int
147 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 	l.max = eal_threads_count - 1;
155 	handle = rte_lcore_callback_register("limit", limit_lcores_init,
156 		limit_lcores_uninit, &l);
157 	if (handle != NULL) {
158 		printf("Error: lcore callback register should have failed\n");
159 		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 	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 		goto error;
169 	}
170 	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 		goto error;
174 	}
175 
176 	/* Accept all lcore and unregister. */
177 	memset(&l, 0, sizeof(l));
178 	l.max = eal_threads_count;
179 	handle = rte_lcore_callback_register("limit", limit_lcores_init,
180 		limit_lcores_uninit, &l);
181 	if (handle == NULL) {
182 		printf("Error: lcore callback register failed\n");
183 		goto error;
184 	}
185 	if (l.uninit != 0) {
186 		printf("Error: lcore callback register succeeded but incorrect uninit calls, expected 0, got %u\n",
187 			l.uninit);
188 		goto error;
189 	}
190 	rte_lcore_callback_unregister(handle);
191 	handle = NULL;
192 	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 		goto error;
196 	}
197 	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 		goto error;
201 	}
202 
203 	return 0;
204 
205 error:
206 	if (handle != NULL)
207 		rte_lcore_callback_unregister(handle);
208 
209 	return -1;
210 }
211 
212 static int
213 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 	struct limit_lcore_context l[2] = {};
218 	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 	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 	l[0].max = UINT_MAX;
236 	handle[0] = rte_lcore_callback_register("no_limit", limit_lcores_init,
237 		limit_lcores_uninit, &l[0]);
238 	if (handle[0] == NULL) {
239 		printf("Error: lcore callback [0] register failed\n");
240 		goto error;
241 	}
242 	l[1].max = eal_threads_count + 1;
243 	handle[1] = rte_lcore_callback_register("limit", limit_lcores_init,
244 		limit_lcores_uninit, &l[1]);
245 	if (handle[1] == NULL) {
246 		printf("Error: lcore callback [1] register failed\n");
247 		goto error;
248 	}
249 	if (l[0].init != eal_threads_count || l[1].init != eal_threads_count) {
250 		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 		goto error;
254 	}
255 	if (l[0].uninit != 0 || l[1].uninit != 0) {
256 		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 		goto error;
259 	}
260 	/* First thread that expects a valid lcore id. */
261 	t = &thread_contexts[0];
262 	t->state = INIT;
263 	t->registered_count = &registered_count;
264 	t->lcore_id_any = false;
265 	if (pthread_create(&t->id, NULL, thread_loop, t) != 0)
266 		goto cleanup_threads;
267 	non_eal_threads_count++;
268 	while (__atomic_load_n(&registered_count, __ATOMIC_ACQUIRE) !=
269 			non_eal_threads_count)
270 		;
271 	if (l[0].init != eal_threads_count + 1 ||
272 			l[1].init != eal_threads_count + 1) {
273 		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 		goto cleanup_threads;
277 	}
278 	if (l[0].uninit != 0 || l[1].uninit != 0) {
279 		printf("Error: incorrect uninit calls, expected 0, 0, got %u, %u\n",
280 			l[0].uninit, l[1].uninit);
281 		goto cleanup_threads;
282 	}
283 	/* Second thread, that expects LCORE_ID_ANY because of init refusal. */
284 	t = &thread_contexts[1];
285 	t->state = INIT;
286 	t->registered_count = &registered_count;
287 	t->lcore_id_any = true;
288 	if (pthread_create(&t->id, NULL, thread_loop, t) != 0)
289 		goto cleanup_threads;
290 	non_eal_threads_count++;
291 	while (__atomic_load_n(&registered_count, __ATOMIC_ACQUIRE) !=
292 			non_eal_threads_count)
293 		;
294 	if (l[0].init != eal_threads_count + 2 ||
295 			l[1].init != eal_threads_count + 2) {
296 		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 		goto cleanup_threads;
300 	}
301 	if (l[0].uninit != 1 || l[1].uninit != 0) {
302 		printf("Error: incorrect uninit calls, expected 1, 0, got %u, %u\n",
303 			l[0].uninit, l[1].uninit);
304 		goto cleanup_threads;
305 	}
306 	rte_lcore_dump(stdout);
307 	/* Release all threads, and check their states. */
308 	__atomic_store_n(&registered_count, 0, __ATOMIC_RELEASE);
309 	ret = 0;
310 	for (i = 0; i < non_eal_threads_count; i++) {
311 		t = &thread_contexts[i];
312 		pthread_join(t->id, NULL);
313 		if (t->state != DONE)
314 			ret = -1;
315 	}
316 	if (ret < 0)
317 		goto error;
318 	rte_lcore_dump(stdout);
319 	if (l[0].uninit != 2 || l[1].uninit != 1) {
320 		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 		goto error;
323 	}
324 	rte_lcore_callback_unregister(handle[0]);
325 	rte_lcore_callback_unregister(handle[1]);
326 	return 0;
327 
328 cleanup_threads:
329 	/* Release all threads */
330 	__atomic_store_n(&registered_count, 0, __ATOMIC_RELEASE);
331 	for (i = 0; i < non_eal_threads_count; i++) {
332 		t = &thread_contexts[i];
333 		pthread_join(t->id, NULL);
334 	}
335 error:
336 	if (handle[1] != NULL)
337 		rte_lcore_callback_unregister(handle[1]);
338 	if (handle[0] != NULL)
339 		rte_lcore_callback_unregister(handle[0]);
340 	return -1;
341 }
342 
343 static int
344 test_lcores(void)
345 {
346 	unsigned int eal_threads_count = 0;
347 	unsigned int i;
348 
349 	for (i = 0; i < RTE_MAX_LCORE; i++) {
350 		if (!rte_lcore_has_role(i, ROLE_OFF))
351 			eal_threads_count++;
352 	}
353 	if (eal_threads_count == 0) {
354 		printf("Error: something is broken, no EAL thread detected.\n");
355 		return TEST_FAILED;
356 	}
357 	printf("EAL threads count: %u, RTE_MAX_LCORE=%u\n", eal_threads_count,
358 		RTE_MAX_LCORE);
359 	rte_lcore_dump(stdout);
360 
361 	if (test_non_eal_lcores(eal_threads_count) < 0)
362 		return TEST_FAILED;
363 
364 	if (test_lcores_callback(eal_threads_count) < 0)
365 		return TEST_FAILED;
366 
367 	if (test_non_eal_lcores_callback(eal_threads_count) < 0)
368 		return TEST_FAILED;
369 
370 	return TEST_SUCCESS;
371 }
372 
373 REGISTER_TEST_COMMAND(lcores_autotest, test_lcores);
374