xref: /dpdk/app/test/test_rcu_qsbr.c (revision 7f12dfdc)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2018 Arm Limited
3  */
4 
5 #include <stdio.h>
6 #include <rte_pause.h>
7 #include <rte_rcu_qsbr.h>
8 #include <rte_hash.h>
9 #include <rte_hash_crc.h>
10 #include <rte_malloc.h>
11 #include <rte_cycles.h>
12 #include <unistd.h>
13 
14 #include "test.h"
15 
16 /* Check condition and return an error if true. */
17 #define TEST_RCU_QSBR_RETURN_IF_ERROR(cond, str, ...) do { \
18 	if (cond) { \
19 		printf("ERROR file %s, line %d: " str "\n", __FILE__, \
20 			__LINE__, ##__VA_ARGS__); \
21 		return -1; \
22 	} \
23 } while (0)
24 
25 /* Make sure that this has the same value as __RTE_QSBR_CNT_INIT */
26 #define TEST_RCU_QSBR_CNT_INIT 1
27 
28 uint16_t enabled_core_ids[RTE_MAX_LCORE];
29 unsigned int num_cores;
30 
31 static uint32_t *keys;
32 #define TOTAL_ENTRY (1024 * 8)
33 #define COUNTER_VALUE 4096
34 static uint32_t *hash_data[RTE_MAX_LCORE][TOTAL_ENTRY];
35 static uint8_t writer_done;
36 
37 static struct rte_rcu_qsbr *t[RTE_MAX_LCORE];
38 struct rte_hash *h[RTE_MAX_LCORE];
39 char hash_name[RTE_MAX_LCORE][8];
40 
41 struct test_rcu_thread_info {
42 	/* Index in RCU array */
43 	int ir;
44 	/* Index in hash array */
45 	int ih;
46 	/* lcore IDs registered on the RCU variable */
47 	uint16_t r_core_ids[2];
48 };
49 struct test_rcu_thread_info thread_info[RTE_MAX_LCORE/4];
50 
51 static int
52 alloc_rcu(void)
53 {
54 	int i;
55 	uint32_t sz;
56 
57 	sz = rte_rcu_qsbr_get_memsize(RTE_MAX_LCORE);
58 
59 	for (i = 0; i < RTE_MAX_LCORE; i++)
60 		t[i] = (struct rte_rcu_qsbr *)rte_zmalloc(NULL, sz,
61 						RTE_CACHE_LINE_SIZE);
62 
63 	return 0;
64 }
65 
66 static int
67 free_rcu(void)
68 {
69 	int i;
70 
71 	for (i = 0; i < RTE_MAX_LCORE; i++)
72 		rte_free(t[i]);
73 
74 	return 0;
75 }
76 
77 /*
78  * rte_rcu_qsbr_thread_register: Add a reader thread, to the list of threads
79  * reporting their quiescent state on a QS variable.
80  */
81 static int
82 test_rcu_qsbr_get_memsize(void)
83 {
84 	uint32_t sz;
85 
86 	printf("\nTest rte_rcu_qsbr_thread_register()\n");
87 
88 	sz = rte_rcu_qsbr_get_memsize(0);
89 	TEST_RCU_QSBR_RETURN_IF_ERROR((sz != 1), "Get Memsize for 0 threads");
90 
91 	sz = rte_rcu_qsbr_get_memsize(128);
92 	/* For 128 threads,
93 	 * for machines with cache line size of 64B - 8384
94 	 * for machines with cache line size of 128 - 16768
95 	 */
96 	if (RTE_CACHE_LINE_SIZE == 64)
97 		TEST_RCU_QSBR_RETURN_IF_ERROR((sz != 8384),
98 			"Get Memsize for 128 threads");
99 	else if (RTE_CACHE_LINE_SIZE == 128)
100 		TEST_RCU_QSBR_RETURN_IF_ERROR((sz != 16768),
101 			"Get Memsize for 128 threads");
102 
103 	return 0;
104 }
105 
106 /*
107  * rte_rcu_qsbr_init: Initialize a QSBR variable.
108  */
109 static int
110 test_rcu_qsbr_init(void)
111 {
112 	int r;
113 
114 	printf("\nTest rte_rcu_qsbr_init()\n");
115 
116 	r = rte_rcu_qsbr_init(NULL, RTE_MAX_LCORE);
117 	TEST_RCU_QSBR_RETURN_IF_ERROR((r != 1), "NULL variable");
118 
119 	return 0;
120 }
121 
122 /*
123  * rte_rcu_qsbr_thread_register: Add a reader thread, to the list of threads
124  * reporting their quiescent state on a QS variable.
125  */
126 static int
127 test_rcu_qsbr_thread_register(void)
128 {
129 	int ret;
130 
131 	printf("\nTest rte_rcu_qsbr_thread_register()\n");
132 
133 	ret = rte_rcu_qsbr_thread_register(NULL, enabled_core_ids[0]);
134 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "NULL variable check");
135 
136 	ret = rte_rcu_qsbr_thread_register(NULL, 100000);
137 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0),
138 		"NULL variable, invalid thread id");
139 
140 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
141 
142 	/* Register valid thread id */
143 	ret = rte_rcu_qsbr_thread_register(t[0], enabled_core_ids[0]);
144 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 1), "Valid thread id");
145 
146 	/* Re-registering should not return error */
147 	ret = rte_rcu_qsbr_thread_register(t[0], enabled_core_ids[0]);
148 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 1),
149 		"Already registered thread id");
150 
151 	/* Register valid thread id - max allowed thread id */
152 	ret = rte_rcu_qsbr_thread_register(t[0], RTE_MAX_LCORE - 1);
153 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 1), "Max thread id");
154 
155 	ret = rte_rcu_qsbr_thread_register(t[0], 100000);
156 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0),
157 		"NULL variable, invalid thread id");
158 
159 	return 0;
160 }
161 
162 /*
163  * rte_rcu_qsbr_thread_unregister: Remove a reader thread, from the list of
164  * threads reporting their quiescent state on a QS variable.
165  */
166 static int
167 test_rcu_qsbr_thread_unregister(void)
168 {
169 	unsigned int num_threads[3] = {1, RTE_MAX_LCORE, 1};
170 	unsigned int i, j;
171 	uint64_t token;
172 	int ret;
173 
174 	printf("\nTest rte_rcu_qsbr_thread_unregister()\n");
175 
176 	ret = rte_rcu_qsbr_thread_unregister(NULL, enabled_core_ids[0]);
177 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "NULL variable check");
178 
179 	ret = rte_rcu_qsbr_thread_unregister(NULL, 100000);
180 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0),
181 		"NULL variable, invalid thread id");
182 
183 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
184 
185 	rte_rcu_qsbr_thread_register(t[0], enabled_core_ids[0]);
186 
187 	ret = rte_rcu_qsbr_thread_unregister(t[0], 100000);
188 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0),
189 		"NULL variable, invalid thread id");
190 
191 	/* Find first disabled core */
192 	for (i = 0; i < RTE_MAX_LCORE; i++) {
193 		if (enabled_core_ids[i] == 0)
194 			break;
195 	}
196 	/* Test with disabled lcore */
197 	ret = rte_rcu_qsbr_thread_unregister(t[0], i);
198 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 1),
199 		"disabled thread id");
200 	/* Unregister already unregistered core */
201 	ret = rte_rcu_qsbr_thread_unregister(t[0], i);
202 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 1),
203 		"Already unregistered core");
204 
205 	/* Test with enabled lcore */
206 	ret = rte_rcu_qsbr_thread_unregister(t[0], enabled_core_ids[0]);
207 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 1),
208 		"enabled thread id");
209 	/* Unregister already unregistered core */
210 	ret = rte_rcu_qsbr_thread_unregister(t[0], enabled_core_ids[0]);
211 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 1),
212 		"Already unregistered core");
213 
214 	/*
215 	 * Test with different thread_ids:
216 	 * 1 - thread_id = 0
217 	 * 2 - All possible thread_ids, from 0 to RTE_MAX_LCORE
218 	 * 3 - thread_id = RTE_MAX_LCORE - 1
219 	 */
220 	for (j = 0; j < 3; j++) {
221 		rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
222 
223 		for (i = 0; i < num_threads[j]; i++)
224 			rte_rcu_qsbr_thread_register(t[0],
225 				(j == 2) ? (RTE_MAX_LCORE - 1) : i);
226 
227 		token = rte_rcu_qsbr_start(t[0]);
228 		TEST_RCU_QSBR_RETURN_IF_ERROR(
229 			(token != (TEST_RCU_QSBR_CNT_INIT + 1)), "QSBR Start");
230 		/* Update quiescent state counter */
231 		for (i = 0; i < num_threads[j]; i++) {
232 			/* Skip one update */
233 			if (i == (RTE_MAX_LCORE - 10))
234 				continue;
235 			rte_rcu_qsbr_quiescent(t[0],
236 				(j == 2) ? (RTE_MAX_LCORE - 1) : i);
237 		}
238 
239 		if (j == 1) {
240 			/* Validate the updates */
241 			ret = rte_rcu_qsbr_check(t[0], token, false);
242 			TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0),
243 						"Non-blocking QSBR check");
244 			/* Update the previously skipped thread */
245 			rte_rcu_qsbr_quiescent(t[0], RTE_MAX_LCORE - 10);
246 		}
247 
248 		/* Validate the updates */
249 		ret = rte_rcu_qsbr_check(t[0], token, false);
250 		TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0),
251 						"Non-blocking QSBR check");
252 
253 		for (i = 0; i < num_threads[j]; i++)
254 			rte_rcu_qsbr_thread_unregister(t[0],
255 				(j == 2) ? (RTE_MAX_LCORE - 1) : i);
256 
257 		/* Check with no thread registered */
258 		ret = rte_rcu_qsbr_check(t[0], token, true);
259 		TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0),
260 						"Blocking QSBR check");
261 	}
262 	return 0;
263 }
264 
265 /*
266  * rte_rcu_qsbr_start: Ask the worker threads to report the quiescent state
267  * status.
268  */
269 static int
270 test_rcu_qsbr_start(void)
271 {
272 	uint64_t token;
273 	int i;
274 
275 	printf("\nTest rte_rcu_qsbr_start()\n");
276 
277 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
278 
279 	for (i = 0; i < 3; i++)
280 		rte_rcu_qsbr_thread_register(t[0], enabled_core_ids[i]);
281 
282 	token = rte_rcu_qsbr_start(t[0]);
283 	TEST_RCU_QSBR_RETURN_IF_ERROR(
284 		(token != (TEST_RCU_QSBR_CNT_INIT + 1)), "QSBR Start");
285 	return 0;
286 }
287 
288 static int
289 test_rcu_qsbr_check_reader(void *arg)
290 {
291 	struct rte_rcu_qsbr *temp;
292 	uint8_t read_type = (uint8_t)((uintptr_t)arg);
293 
294 	temp = t[read_type];
295 
296 	/* Update quiescent state counter */
297 	rte_rcu_qsbr_quiescent(temp, enabled_core_ids[0]);
298 	rte_rcu_qsbr_quiescent(temp, enabled_core_ids[1]);
299 	rte_rcu_qsbr_thread_unregister(temp, enabled_core_ids[2]);
300 	rte_rcu_qsbr_quiescent(temp, enabled_core_ids[3]);
301 	return 0;
302 }
303 
304 /*
305  * rte_rcu_qsbr_check: Checks if all the worker threads have entered the queis-
306  * cent state 'n' number of times. 'n' is provided in rte_rcu_qsbr_start API.
307  */
308 static int
309 test_rcu_qsbr_check(void)
310 {
311 	int i, ret;
312 	uint64_t token;
313 
314 	printf("\nTest rte_rcu_qsbr_check()\n");
315 
316 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
317 
318 	token = rte_rcu_qsbr_start(t[0]);
319 	TEST_RCU_QSBR_RETURN_IF_ERROR(
320 		(token != (TEST_RCU_QSBR_CNT_INIT + 1)), "QSBR Start");
321 
322 
323 	ret = rte_rcu_qsbr_check(t[0], 0, false);
324 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Token = 0");
325 
326 	ret = rte_rcu_qsbr_check(t[0], token, true);
327 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Blocking QSBR check");
328 
329 	for (i = 0; i < 3; i++)
330 		rte_rcu_qsbr_thread_register(t[0], enabled_core_ids[i]);
331 
332 	ret = rte_rcu_qsbr_check(t[0], token, false);
333 	/* Threads are offline, hence this should pass */
334 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Non-blocking QSBR check");
335 
336 	token = rte_rcu_qsbr_start(t[0]);
337 	TEST_RCU_QSBR_RETURN_IF_ERROR(
338 		(token != (TEST_RCU_QSBR_CNT_INIT + 2)), "QSBR Start");
339 
340 	ret = rte_rcu_qsbr_check(t[0], token, false);
341 	/* Threads are offline, hence this should pass */
342 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Non-blocking QSBR check");
343 
344 	for (i = 0; i < 3; i++)
345 		rte_rcu_qsbr_thread_unregister(t[0], enabled_core_ids[i]);
346 
347 	ret = rte_rcu_qsbr_check(t[0], token, true);
348 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Blocking QSBR check");
349 
350 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
351 
352 	for (i = 0; i < 4; i++)
353 		rte_rcu_qsbr_thread_register(t[0], enabled_core_ids[i]);
354 
355 	token = rte_rcu_qsbr_start(t[0]);
356 	TEST_RCU_QSBR_RETURN_IF_ERROR(
357 		(token != (TEST_RCU_QSBR_CNT_INIT + 1)), "QSBR Start");
358 
359 	rte_eal_remote_launch(test_rcu_qsbr_check_reader, NULL,
360 							enabled_core_ids[0]);
361 
362 	rte_eal_mp_wait_lcore();
363 	ret = rte_rcu_qsbr_check(t[0], token, true);
364 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret != 1), "Blocking QSBR check");
365 
366 	return 0;
367 }
368 
369 static int
370 test_rcu_qsbr_synchronize_reader(void *arg)
371 {
372 	uint32_t lcore_id = rte_lcore_id();
373 	(void)arg;
374 
375 	/* Register and become online */
376 	rte_rcu_qsbr_thread_register(t[0], lcore_id);
377 	rte_rcu_qsbr_thread_online(t[0], lcore_id);
378 
379 	while (!writer_done)
380 		rte_rcu_qsbr_quiescent(t[0], lcore_id);
381 
382 	rte_rcu_qsbr_thread_offline(t[0], lcore_id);
383 	rte_rcu_qsbr_thread_unregister(t[0], lcore_id);
384 
385 	return 0;
386 }
387 
388 /*
389  * rte_rcu_qsbr_synchronize: Wait till all the reader threads have entered
390  * the queiscent state.
391  */
392 static int
393 test_rcu_qsbr_synchronize(void)
394 {
395 	unsigned int i;
396 
397 	printf("\nTest rte_rcu_qsbr_synchronize()\n");
398 
399 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
400 
401 	/* Test if the API returns when there are no threads reporting
402 	 * QS on the variable.
403 	 */
404 	rte_rcu_qsbr_synchronize(t[0], RTE_QSBR_THRID_INVALID);
405 
406 	/* Test if the API returns when there are threads registered
407 	 * but not online.
408 	 */
409 	for (i = 0; i < RTE_MAX_LCORE; i++)
410 		rte_rcu_qsbr_thread_register(t[0], i);
411 	rte_rcu_qsbr_synchronize(t[0], RTE_QSBR_THRID_INVALID);
412 
413 	/* Test if the API returns when the caller is also
414 	 * reporting the QS status.
415 	 */
416 	rte_rcu_qsbr_thread_online(t[0], 0);
417 	rte_rcu_qsbr_synchronize(t[0], 0);
418 	rte_rcu_qsbr_thread_offline(t[0], 0);
419 
420 	/* Check the other boundary */
421 	rte_rcu_qsbr_thread_online(t[0], RTE_MAX_LCORE - 1);
422 	rte_rcu_qsbr_synchronize(t[0], RTE_MAX_LCORE - 1);
423 	rte_rcu_qsbr_thread_offline(t[0], RTE_MAX_LCORE - 1);
424 
425 	/* Test if the API returns after unregisterng all the threads */
426 	for (i = 0; i < RTE_MAX_LCORE; i++)
427 		rte_rcu_qsbr_thread_unregister(t[0], i);
428 	rte_rcu_qsbr_synchronize(t[0], RTE_QSBR_THRID_INVALID);
429 
430 	/* Test if the API returns with the live threads */
431 	writer_done = 0;
432 	for (i = 0; i < num_cores; i++)
433 		rte_eal_remote_launch(test_rcu_qsbr_synchronize_reader,
434 			NULL, enabled_core_ids[i]);
435 	rte_rcu_qsbr_synchronize(t[0], RTE_QSBR_THRID_INVALID);
436 	rte_rcu_qsbr_synchronize(t[0], RTE_QSBR_THRID_INVALID);
437 	rte_rcu_qsbr_synchronize(t[0], RTE_QSBR_THRID_INVALID);
438 	rte_rcu_qsbr_synchronize(t[0], RTE_QSBR_THRID_INVALID);
439 	rte_rcu_qsbr_synchronize(t[0], RTE_QSBR_THRID_INVALID);
440 
441 	writer_done = 1;
442 	rte_eal_mp_wait_lcore();
443 
444 	return 0;
445 }
446 
447 /*
448  * rte_rcu_qsbr_thread_online: Add a registered reader thread, to
449  * the list of threads reporting their quiescent state on a QS variable.
450  */
451 static int
452 test_rcu_qsbr_thread_online(void)
453 {
454 	int i, ret;
455 	uint64_t token;
456 
457 	printf("Test rte_rcu_qsbr_thread_online()\n");
458 
459 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
460 
461 	/* Register 2 threads to validate that only the
462 	 * online thread is waited upon.
463 	 */
464 	rte_rcu_qsbr_thread_register(t[0], enabled_core_ids[0]);
465 	rte_rcu_qsbr_thread_register(t[0], enabled_core_ids[1]);
466 
467 	/* Use qsbr_start to verify that the thread_online API
468 	 * succeeded.
469 	 */
470 	token = rte_rcu_qsbr_start(t[0]);
471 
472 	/* Make the thread online */
473 	rte_rcu_qsbr_thread_online(t[0], enabled_core_ids[0]);
474 
475 	/* Check if the thread is online */
476 	ret = rte_rcu_qsbr_check(t[0], token, true);
477 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "thread online");
478 
479 	/* Check if the online thread, can report QS */
480 	token = rte_rcu_qsbr_start(t[0]);
481 	rte_rcu_qsbr_quiescent(t[0], enabled_core_ids[0]);
482 	ret = rte_rcu_qsbr_check(t[0], token, true);
483 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "thread update");
484 
485 	/* Make all the threads online */
486 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
487 	token = rte_rcu_qsbr_start(t[0]);
488 	for (i = 0; i < RTE_MAX_LCORE; i++) {
489 		rte_rcu_qsbr_thread_register(t[0], i);
490 		rte_rcu_qsbr_thread_online(t[0], i);
491 	}
492 	/* Check if all the threads are online */
493 	ret = rte_rcu_qsbr_check(t[0], token, true);
494 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "thread online");
495 	/* Check if all the online threads can report QS */
496 	token = rte_rcu_qsbr_start(t[0]);
497 	for (i = 0; i < RTE_MAX_LCORE; i++)
498 		rte_rcu_qsbr_quiescent(t[0], i);
499 	ret = rte_rcu_qsbr_check(t[0], token, true);
500 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "thread update");
501 
502 	return 0;
503 }
504 
505 /*
506  * rte_rcu_qsbr_thread_offline: Remove a registered reader thread, from
507  * the list of threads reporting their quiescent state on a QS variable.
508  */
509 static int
510 test_rcu_qsbr_thread_offline(void)
511 {
512 	int i, ret;
513 	uint64_t token;
514 
515 	printf("\nTest rte_rcu_qsbr_thread_offline()\n");
516 
517 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
518 
519 	rte_rcu_qsbr_thread_register(t[0], enabled_core_ids[0]);
520 
521 	/* Make the thread offline */
522 	rte_rcu_qsbr_thread_offline(t[0], enabled_core_ids[0]);
523 
524 	/* Use qsbr_start to verify that the thread_offline API
525 	 * succeeded.
526 	 */
527 	token = rte_rcu_qsbr_start(t[0]);
528 	/* Check if the thread is offline */
529 	ret = rte_rcu_qsbr_check(t[0], token, true);
530 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "thread offline");
531 
532 	/* Bring an offline thread online and check if it can
533 	 * report QS.
534 	 */
535 	rte_rcu_qsbr_thread_online(t[0], enabled_core_ids[0]);
536 	/* Check if the online thread, can report QS */
537 	token = rte_rcu_qsbr_start(t[0]);
538 	rte_rcu_qsbr_quiescent(t[0], enabled_core_ids[0]);
539 	ret = rte_rcu_qsbr_check(t[0], token, true);
540 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "offline to online");
541 
542 	/*
543 	 * Check a sequence of online/status/offline/status/online/status
544 	 */
545 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
546 	token = rte_rcu_qsbr_start(t[0]);
547 	/* Make the threads online */
548 	for (i = 0; i < RTE_MAX_LCORE; i++) {
549 		rte_rcu_qsbr_thread_register(t[0], i);
550 		rte_rcu_qsbr_thread_online(t[0], i);
551 	}
552 
553 	/* Check if all the threads are online */
554 	ret = rte_rcu_qsbr_check(t[0], token, true);
555 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "thread online");
556 
557 	/* Check if all the online threads can report QS */
558 	token = rte_rcu_qsbr_start(t[0]);
559 	for (i = 0; i < RTE_MAX_LCORE; i++)
560 		rte_rcu_qsbr_quiescent(t[0], i);
561 	ret = rte_rcu_qsbr_check(t[0], token, true);
562 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "report QS");
563 
564 	/* Make all the threads offline */
565 	for (i = 0; i < RTE_MAX_LCORE; i++)
566 		rte_rcu_qsbr_thread_offline(t[0], i);
567 	/* Make sure these threads are not being waited on */
568 	token = rte_rcu_qsbr_start(t[0]);
569 	ret = rte_rcu_qsbr_check(t[0], token, true);
570 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "offline QS");
571 
572 	/* Make the threads online */
573 	for (i = 0; i < RTE_MAX_LCORE; i++)
574 		rte_rcu_qsbr_thread_online(t[0], i);
575 	/* Check if all the online threads can report QS */
576 	token = rte_rcu_qsbr_start(t[0]);
577 	for (i = 0; i < RTE_MAX_LCORE; i++)
578 		rte_rcu_qsbr_quiescent(t[0], i);
579 	ret = rte_rcu_qsbr_check(t[0], token, true);
580 	TEST_RCU_QSBR_RETURN_IF_ERROR((ret == 0), "online again");
581 
582 	return 0;
583 }
584 
585 /*
586  * rte_rcu_qsbr_dump: Dump status of a single QS variable to a file
587  */
588 static int
589 test_rcu_qsbr_dump(void)
590 {
591 	int i;
592 
593 	printf("\nTest rte_rcu_qsbr_dump()\n");
594 
595 	/* Negative tests */
596 	rte_rcu_qsbr_dump(NULL, t[0]);
597 	rte_rcu_qsbr_dump(stdout, NULL);
598 	rte_rcu_qsbr_dump(NULL, NULL);
599 
600 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
601 	rte_rcu_qsbr_init(t[1], RTE_MAX_LCORE);
602 
603 	/* QS variable with 0 core mask */
604 	rte_rcu_qsbr_dump(stdout, t[0]);
605 
606 	rte_rcu_qsbr_thread_register(t[0], enabled_core_ids[0]);
607 
608 	for (i = 1; i < 3; i++)
609 		rte_rcu_qsbr_thread_register(t[1], enabled_core_ids[i]);
610 
611 	rte_rcu_qsbr_dump(stdout, t[0]);
612 	rte_rcu_qsbr_dump(stdout, t[1]);
613 	printf("\n");
614 	return 0;
615 }
616 
617 static int
618 test_rcu_qsbr_reader(void *arg)
619 {
620 	struct rte_rcu_qsbr *temp;
621 	struct rte_hash *hash = NULL;
622 	int i;
623 	uint32_t lcore_id = rte_lcore_id();
624 	struct test_rcu_thread_info *ti;
625 	uint32_t *pdata;
626 
627 	ti = (struct test_rcu_thread_info *)arg;
628 	temp = t[ti->ir];
629 	hash = h[ti->ih];
630 
631 	do {
632 		rte_rcu_qsbr_thread_register(temp, lcore_id);
633 		rte_rcu_qsbr_thread_online(temp, lcore_id);
634 		for (i = 0; i < TOTAL_ENTRY; i++) {
635 			rte_rcu_qsbr_lock(temp, lcore_id);
636 			if (rte_hash_lookup_data(hash, keys+i,
637 					(void **)&pdata) != -ENOENT) {
638 				pdata[lcore_id] = 0;
639 				while (pdata[lcore_id] < COUNTER_VALUE)
640 					pdata[lcore_id]++;
641 			}
642 			rte_rcu_qsbr_unlock(temp, lcore_id);
643 		}
644 		/* Update quiescent state counter */
645 		rte_rcu_qsbr_quiescent(temp, lcore_id);
646 		rte_rcu_qsbr_thread_offline(temp, lcore_id);
647 		rte_rcu_qsbr_thread_unregister(temp, lcore_id);
648 	} while (!writer_done);
649 
650 	return 0;
651 }
652 
653 static int
654 test_rcu_qsbr_writer(void *arg)
655 {
656 	uint64_t token;
657 	int32_t i, pos, del;
658 	uint32_t c;
659 	struct rte_rcu_qsbr *temp;
660 	struct rte_hash *hash = NULL;
661 	struct test_rcu_thread_info *ti;
662 
663 	ti = (struct test_rcu_thread_info *)arg;
664 	temp = t[ti->ir];
665 	hash = h[ti->ih];
666 
667 	/* Delete element from the shared data structure */
668 	del = rte_lcore_id() % TOTAL_ENTRY;
669 	pos = rte_hash_del_key(hash, keys + del);
670 	if (pos < 0) {
671 		printf("Delete key failed #%d\n", keys[del]);
672 		return -1;
673 	}
674 	/* Start the quiescent state query process */
675 	token = rte_rcu_qsbr_start(temp);
676 	/* Check the quiescent state status */
677 	rte_rcu_qsbr_check(temp, token, true);
678 	for (i = 0; i < 2; i++) {
679 		c = hash_data[ti->ih][del][ti->r_core_ids[i]];
680 		if (c != COUNTER_VALUE && c != 0) {
681 			printf("Reader lcore id %u did not complete = %u\t",
682 				rte_lcore_id(), c);
683 			return -1;
684 		}
685 	}
686 
687 	if (rte_hash_free_key_with_position(hash, pos) < 0) {
688 		printf("Failed to free the key #%d\n", keys[del]);
689 		return -1;
690 	}
691 	rte_free(hash_data[ti->ih][del]);
692 	hash_data[ti->ih][del] = NULL;
693 
694 	return 0;
695 }
696 
697 static struct rte_hash *
698 init_hash(int hash_id)
699 {
700 	int i;
701 	struct rte_hash *h = NULL;
702 
703 	sprintf(hash_name[hash_id], "hash%d", hash_id);
704 	struct rte_hash_parameters hash_params = {
705 		.entries = TOTAL_ENTRY,
706 		.key_len = sizeof(uint32_t),
707 		.hash_func_init_val = 0,
708 		.socket_id = rte_socket_id(),
709 		.hash_func = rte_hash_crc,
710 		.extra_flag =
711 			RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF,
712 		.name = hash_name[hash_id],
713 	};
714 
715 	h = rte_hash_create(&hash_params);
716 	if (h == NULL) {
717 		printf("Hash create Failed\n");
718 		return NULL;
719 	}
720 
721 	for (i = 0; i < TOTAL_ENTRY; i++) {
722 		hash_data[hash_id][i] =
723 			rte_zmalloc(NULL, sizeof(uint32_t) * RTE_MAX_LCORE, 0);
724 		if (hash_data[hash_id][i] == NULL) {
725 			printf("No memory\n");
726 			return NULL;
727 		}
728 	}
729 	keys = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_ENTRY, 0);
730 	if (keys == NULL) {
731 		printf("No memory\n");
732 		return NULL;
733 	}
734 
735 	for (i = 0; i < TOTAL_ENTRY; i++)
736 		keys[i] = i;
737 
738 	for (i = 0; i < TOTAL_ENTRY; i++) {
739 		if (rte_hash_add_key_data(h, keys + i,
740 				(void *)((uintptr_t)hash_data[hash_id][i]))
741 				< 0) {
742 			printf("Hash key add Failed #%d\n", i);
743 			return NULL;
744 		}
745 	}
746 	return h;
747 }
748 
749 /*
750  * Functional test:
751  * Single writer, Single QS variable, simultaneous QSBR Queries
752  */
753 static int
754 test_rcu_qsbr_sw_sv_3qs(void)
755 {
756 	uint64_t token[3];
757 	uint32_t c;
758 	int i;
759 	int32_t pos[3];
760 
761 	writer_done = 0;
762 
763 	printf("Test: 1 writer, 1 QSBR variable, simultaneous QSBR queries\n");
764 
765 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
766 
767 	/* Shared data structure created */
768 	h[0] = init_hash(0);
769 	if (h[0] == NULL) {
770 		printf("Hash init failed\n");
771 		goto error;
772 	}
773 
774 	/* No need to fill the registered core IDs as the writer
775 	 * thread is not launched.
776 	 */
777 	thread_info[0].ir = 0;
778 	thread_info[0].ih = 0;
779 
780 	/* Reader threads are launched */
781 	for (i = 0; i < 4; i++)
782 		rte_eal_remote_launch(test_rcu_qsbr_reader, &thread_info[0],
783 					enabled_core_ids[i]);
784 
785 	/* Delete element from the shared data structure */
786 	pos[0] = rte_hash_del_key(h[0], keys + 0);
787 	if (pos[0] < 0) {
788 		printf("Delete key failed #%d\n", keys[0]);
789 		goto error;
790 	}
791 	/* Start the quiescent state query process */
792 	token[0] = rte_rcu_qsbr_start(t[0]);
793 
794 	/* Delete element from the shared data structure */
795 	pos[1] = rte_hash_del_key(h[0], keys + 3);
796 	if (pos[1] < 0) {
797 		printf("Delete key failed #%d\n", keys[3]);
798 		goto error;
799 	}
800 	/* Start the quiescent state query process */
801 	token[1] = rte_rcu_qsbr_start(t[0]);
802 
803 	/* Delete element from the shared data structure */
804 	pos[2] = rte_hash_del_key(h[0], keys + 6);
805 	if (pos[2] < 0) {
806 		printf("Delete key failed #%d\n", keys[6]);
807 		goto error;
808 	}
809 	/* Start the quiescent state query process */
810 	token[2] = rte_rcu_qsbr_start(t[0]);
811 
812 	/* Check the quiescent state status */
813 	rte_rcu_qsbr_check(t[0], token[0], true);
814 	for (i = 0; i < 4; i++) {
815 		c = hash_data[0][0][enabled_core_ids[i]];
816 		if (c != COUNTER_VALUE && c != 0) {
817 			printf("Reader lcore %d did not complete #0 = %d\n",
818 				enabled_core_ids[i], c);
819 			goto error;
820 		}
821 	}
822 
823 	if (rte_hash_free_key_with_position(h[0], pos[0]) < 0) {
824 		printf("Failed to free the key #%d\n", keys[0]);
825 		goto error;
826 	}
827 	rte_free(hash_data[0][0]);
828 	hash_data[0][0] = NULL;
829 
830 	/* Check the quiescent state status */
831 	rte_rcu_qsbr_check(t[0], token[1], true);
832 	for (i = 0; i < 4; i++) {
833 		c = hash_data[0][3][enabled_core_ids[i]];
834 		if (c != COUNTER_VALUE && c != 0) {
835 			printf("Reader lcore %d did not complete #3 = %d\n",
836 				enabled_core_ids[i], c);
837 			goto error;
838 		}
839 	}
840 
841 	if (rte_hash_free_key_with_position(h[0], pos[1]) < 0) {
842 		printf("Failed to free the key #%d\n", keys[3]);
843 		goto error;
844 	}
845 	rte_free(hash_data[0][3]);
846 	hash_data[0][3] = NULL;
847 
848 	/* Check the quiescent state status */
849 	rte_rcu_qsbr_check(t[0], token[2], true);
850 	for (i = 0; i < 4; i++) {
851 		c = hash_data[0][6][enabled_core_ids[i]];
852 		if (c != COUNTER_VALUE && c != 0) {
853 			printf("Reader lcore %d did not complete #6 = %d\n",
854 				enabled_core_ids[i], c);
855 			goto error;
856 		}
857 	}
858 
859 	if (rte_hash_free_key_with_position(h[0], pos[2]) < 0) {
860 		printf("Failed to free the key #%d\n", keys[6]);
861 		goto error;
862 	}
863 	rte_free(hash_data[0][6]);
864 	hash_data[0][6] = NULL;
865 
866 	writer_done = 1;
867 
868 	/* Wait and check return value from reader threads */
869 	for (i = 0; i < 4; i++)
870 		if (rte_eal_wait_lcore(enabled_core_ids[i]) < 0)
871 			goto error;
872 	rte_hash_free(h[0]);
873 	rte_free(keys);
874 
875 	return 0;
876 
877 error:
878 	writer_done = 1;
879 	/* Wait until all readers have exited */
880 	rte_eal_mp_wait_lcore();
881 
882 	rte_hash_free(h[0]);
883 	rte_free(keys);
884 	for (i = 0; i < TOTAL_ENTRY; i++)
885 		rte_free(hash_data[0][i]);
886 
887 	return -1;
888 }
889 
890 /*
891  * Multi writer, Multiple QS variable, simultaneous QSBR queries
892  */
893 static int
894 test_rcu_qsbr_mw_mv_mqs(void)
895 {
896 	unsigned int i, j;
897 	unsigned int test_cores;
898 
899 	writer_done = 0;
900 	test_cores = num_cores / 4;
901 	test_cores = test_cores * 4;
902 
903 	printf("Test: %d writers, %d QSBR variable, simultaneous QSBR queries\n",
904 	       test_cores / 2, test_cores / 4);
905 
906 	for (i = 0; i < test_cores / 4; i++) {
907 		j = i * 4;
908 		rte_rcu_qsbr_init(t[i], RTE_MAX_LCORE);
909 		h[i] = init_hash(i);
910 		if (h[i] == NULL) {
911 			printf("Hash init failed\n");
912 			goto error;
913 		}
914 		thread_info[i].ir = i;
915 		thread_info[i].ih = i;
916 		thread_info[i].r_core_ids[0] = enabled_core_ids[j];
917 		thread_info[i].r_core_ids[1] = enabled_core_ids[j + 1];
918 
919 		/* Reader threads are launched */
920 		rte_eal_remote_launch(test_rcu_qsbr_reader,
921 					(void *)&thread_info[i],
922 					enabled_core_ids[j]);
923 		rte_eal_remote_launch(test_rcu_qsbr_reader,
924 					(void *)&thread_info[i],
925 					enabled_core_ids[j + 1]);
926 
927 		/* Writer threads are launched */
928 		rte_eal_remote_launch(test_rcu_qsbr_writer,
929 					(void *)&thread_info[i],
930 					enabled_core_ids[j + 2]);
931 		rte_eal_remote_launch(test_rcu_qsbr_writer,
932 					(void *)&thread_info[i],
933 					enabled_core_ids[j + 3]);
934 	}
935 
936 	/* Wait and check return value from writer threads */
937 	for (i = 0; i < test_cores / 4; i++) {
938 		j = i * 4;
939 		if (rte_eal_wait_lcore(enabled_core_ids[j + 2]) < 0)
940 			goto error;
941 
942 		if (rte_eal_wait_lcore(enabled_core_ids[j + 3]) < 0)
943 			goto error;
944 	}
945 	writer_done = 1;
946 
947 	/* Wait and check return value from reader threads */
948 	for (i = 0; i < test_cores / 4; i++) {
949 		j = i * 4;
950 		if (rte_eal_wait_lcore(enabled_core_ids[j]) < 0)
951 			goto error;
952 
953 		if (rte_eal_wait_lcore(enabled_core_ids[j + 1]) < 0)
954 			goto error;
955 	}
956 
957 	for (i = 0; i < test_cores / 4; i++)
958 		rte_hash_free(h[i]);
959 
960 	rte_free(keys);
961 
962 	return 0;
963 
964 error:
965 	writer_done = 1;
966 	/* Wait until all readers and writers have exited */
967 	rte_eal_mp_wait_lcore();
968 
969 	for (i = 0; i < test_cores / 4; i++)
970 		rte_hash_free(h[i]);
971 	rte_free(keys);
972 	for (j = 0; j < test_cores / 4; j++)
973 		for (i = 0; i < TOTAL_ENTRY; i++)
974 			rte_free(hash_data[j][i]);
975 
976 	return -1;
977 }
978 
979 static int
980 test_rcu_qsbr_main(void)
981 {
982 	uint16_t core_id;
983 
984 	if (rte_lcore_count() < 5) {
985 		printf("Not enough cores for rcu_qsbr_autotest, expecting at least 5\n");
986 		return TEST_SKIPPED;
987 	}
988 
989 	num_cores = 0;
990 	RTE_LCORE_FOREACH_SLAVE(core_id) {
991 		enabled_core_ids[num_cores] = core_id;
992 		num_cores++;
993 	}
994 
995 	/* Error-checking test cases */
996 	if (test_rcu_qsbr_get_memsize() < 0)
997 		goto test_fail;
998 
999 	if (test_rcu_qsbr_init() < 0)
1000 		goto test_fail;
1001 
1002 	alloc_rcu();
1003 
1004 	if (test_rcu_qsbr_thread_register() < 0)
1005 		goto test_fail;
1006 
1007 	if (test_rcu_qsbr_thread_unregister() < 0)
1008 		goto test_fail;
1009 
1010 	if (test_rcu_qsbr_start() < 0)
1011 		goto test_fail;
1012 
1013 	if (test_rcu_qsbr_check() < 0)
1014 		goto test_fail;
1015 
1016 	if (test_rcu_qsbr_synchronize() < 0)
1017 		goto test_fail;
1018 
1019 	if (test_rcu_qsbr_dump() < 0)
1020 		goto test_fail;
1021 
1022 	if (test_rcu_qsbr_thread_online() < 0)
1023 		goto test_fail;
1024 
1025 	if (test_rcu_qsbr_thread_offline() < 0)
1026 		goto test_fail;
1027 
1028 	printf("\nFunctional tests\n");
1029 
1030 	if (test_rcu_qsbr_sw_sv_3qs() < 0)
1031 		goto test_fail;
1032 
1033 	if (test_rcu_qsbr_mw_mv_mqs() < 0)
1034 		goto test_fail;
1035 
1036 	free_rcu();
1037 
1038 	printf("\n");
1039 	return 0;
1040 
1041 test_fail:
1042 	free_rcu();
1043 
1044 	return -1;
1045 }
1046 
1047 REGISTER_TEST_COMMAND(rcu_qsbr_autotest, test_rcu_qsbr_main);
1048