xref: /dpdk/app/test/test_rcu_qsbr_perf.c (revision b6a7e6852e9ab82ae0e05e2d2a0b83abca17de3b)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2018 Arm Limited
3  */
4 
5 #include <stdio.h>
6 #include <stdbool.h>
7 #include <inttypes.h>
8 #include <rte_pause.h>
9 #include <rte_rcu_qsbr.h>
10 #include <rte_hash.h>
11 #include <rte_hash_crc.h>
12 #include <rte_malloc.h>
13 #include <rte_cycles.h>
14 #include <unistd.h>
15 
16 #include "test.h"
17 
18 /* Check condition and return an error if true. */
19 static uint16_t enabled_core_ids[RTE_MAX_LCORE];
20 static unsigned int num_cores;
21 
22 static uint32_t *keys;
23 #define TOTAL_ENTRY (1024 * 8)
24 #define COUNTER_VALUE 4096
25 static uint32_t *hash_data[TOTAL_ENTRY];
26 static volatile uint8_t writer_done;
27 static volatile uint8_t all_registered;
28 static volatile RTE_ATOMIC(uint32_t) thr_id;
29 
30 static struct rte_rcu_qsbr *t[RTE_MAX_LCORE];
31 static struct rte_hash *h;
32 static char hash_name[8];
33 static RTE_ATOMIC(uint64_t) updates;
34 static RTE_ATOMIC(uint64_t) checks;
35 static RTE_ATOMIC(uint64_t) update_cycles;
36 static RTE_ATOMIC(uint64_t) check_cycles;
37 
38 /* Scale down results to 1000 operations to support lower
39  * granularity clocks.
40  */
41 #define RCU_SCALE_DOWN 1000
42 
43 /* Simple way to allocate thread ids in 0 to RTE_MAX_LCORE space */
44 static inline uint32_t
alloc_thread_id(void)45 alloc_thread_id(void)
46 {
47 	uint32_t tmp_thr_id;
48 
49 	tmp_thr_id = rte_atomic_fetch_add_explicit(&thr_id, 1, rte_memory_order_relaxed);
50 	if (tmp_thr_id >= RTE_MAX_LCORE)
51 		printf("Invalid thread id %u\n", tmp_thr_id);
52 
53 	return tmp_thr_id;
54 }
55 
56 static int
test_rcu_qsbr_reader_perf(void * arg)57 test_rcu_qsbr_reader_perf(void *arg)
58 {
59 	bool writer_present = (bool)arg;
60 	uint32_t thread_id = alloc_thread_id();
61 	uint64_t loop_cnt = 0;
62 	uint64_t begin, cycles;
63 
64 	/* Register for report QS */
65 	rte_rcu_qsbr_thread_register(t[0], thread_id);
66 	/* Make the thread online */
67 	rte_rcu_qsbr_thread_online(t[0], thread_id);
68 
69 	begin = rte_rdtsc_precise();
70 
71 	if (writer_present) {
72 		while (!writer_done) {
73 			/* Update quiescent state counter */
74 			rte_rcu_qsbr_quiescent(t[0], thread_id);
75 			loop_cnt++;
76 		}
77 	} else {
78 		while (loop_cnt < 100000000) {
79 			/* Update quiescent state counter */
80 			rte_rcu_qsbr_quiescent(t[0], thread_id);
81 			loop_cnt++;
82 		}
83 	}
84 
85 	cycles = rte_rdtsc_precise() - begin;
86 	rte_atomic_fetch_add_explicit(&update_cycles, cycles, rte_memory_order_relaxed);
87 	rte_atomic_fetch_add_explicit(&updates, loop_cnt, rte_memory_order_relaxed);
88 
89 	/* Make the thread offline */
90 	rte_rcu_qsbr_thread_offline(t[0], thread_id);
91 	/* Unregister before exiting to avoid writer from waiting */
92 	rte_rcu_qsbr_thread_unregister(t[0], thread_id);
93 
94 	return 0;
95 }
96 
97 static int
test_rcu_qsbr_writer_perf(void * arg)98 test_rcu_qsbr_writer_perf(void *arg)
99 {
100 	bool wait = (bool)arg;
101 	uint64_t token = 0;
102 	uint64_t loop_cnt = 0;
103 	uint64_t begin, cycles;
104 
105 	begin = rte_rdtsc_precise();
106 
107 	do {
108 		/* Start the quiescent state query process */
109 		if (wait)
110 			token = rte_rcu_qsbr_start(t[0]);
111 
112 		/* Check quiescent state status */
113 		rte_rcu_qsbr_check(t[0], token, wait);
114 		loop_cnt++;
115 	} while (loop_cnt < 20000000);
116 
117 	cycles = rte_rdtsc_precise() - begin;
118 	rte_atomic_fetch_add_explicit(&check_cycles, cycles, rte_memory_order_relaxed);
119 	rte_atomic_fetch_add_explicit(&checks, loop_cnt, rte_memory_order_relaxed);
120 	return 0;
121 }
122 
123 /*
124  * Perf test: Reader/writer
125  * Single writer, Multiple Readers, Single QS var, Non-Blocking rcu_qsbr_check
126  */
127 static int
test_rcu_qsbr_perf(void)128 test_rcu_qsbr_perf(void)
129 {
130 	size_t sz;
131 	unsigned int i, tmp_num_cores;
132 
133 	writer_done = 0;
134 
135 	rte_atomic_store_explicit(&updates, 0, rte_memory_order_relaxed);
136 	rte_atomic_store_explicit(&update_cycles, 0, rte_memory_order_relaxed);
137 	rte_atomic_store_explicit(&checks, 0, rte_memory_order_relaxed);
138 	rte_atomic_store_explicit(&check_cycles, 0, rte_memory_order_relaxed);
139 
140 	printf("\nPerf Test: %d Readers/1 Writer('wait' in qsbr_check == true)\n",
141 		num_cores - 1);
142 
143 	rte_atomic_store_explicit(&thr_id, 0, rte_memory_order_seq_cst);
144 
145 	if (all_registered == 1)
146 		tmp_num_cores = num_cores - 1;
147 	else
148 		tmp_num_cores = RTE_MAX_LCORE;
149 
150 	sz = rte_rcu_qsbr_get_memsize(tmp_num_cores);
151 	t[0] = (struct rte_rcu_qsbr *)rte_zmalloc("rcu0", sz,
152 						RTE_CACHE_LINE_SIZE);
153 	/* QS variable is initialized */
154 	rte_rcu_qsbr_init(t[0], tmp_num_cores);
155 
156 	/* Reader threads are launched */
157 	for (i = 0; i < num_cores - 1; i++)
158 		rte_eal_remote_launch(test_rcu_qsbr_reader_perf, (void *)1,
159 					enabled_core_ids[i]);
160 
161 	/* Writer thread is launched */
162 	rte_eal_remote_launch(test_rcu_qsbr_writer_perf,
163 			      (void *)1, enabled_core_ids[i]);
164 
165 	/* Wait for the writer thread */
166 	rte_eal_wait_lcore(enabled_core_ids[i]);
167 	writer_done = 1;
168 
169 	/* Wait until all readers have exited */
170 	rte_eal_mp_wait_lcore();
171 
172 	printf("Total quiescent state updates = %"PRIi64"\n",
173 		rte_atomic_load_explicit(&updates, rte_memory_order_relaxed));
174 	printf("Cycles per %d quiescent state updates: %"PRIi64"\n",
175 		RCU_SCALE_DOWN,
176 		rte_atomic_load_explicit(&update_cycles, rte_memory_order_relaxed) /
177 		(rte_atomic_load_explicit(&updates, rte_memory_order_relaxed) / RCU_SCALE_DOWN));
178 	printf("Total RCU checks = %"PRIi64"\n", rte_atomic_load_explicit(&checks,
179 			rte_memory_order_relaxed));
180 	printf("Cycles per %d checks: %"PRIi64"\n", RCU_SCALE_DOWN,
181 		rte_atomic_load_explicit(&check_cycles, rte_memory_order_relaxed) /
182 		(rte_atomic_load_explicit(&checks, rte_memory_order_relaxed) / RCU_SCALE_DOWN));
183 
184 	rte_free(t[0]);
185 
186 	return 0;
187 }
188 
189 /*
190  * Perf test: Readers
191  * Single writer, Multiple readers, Single QS variable
192  */
193 static int
test_rcu_qsbr_rperf(void)194 test_rcu_qsbr_rperf(void)
195 {
196 	size_t sz;
197 	unsigned int i, tmp_num_cores;
198 
199 	rte_atomic_store_explicit(&updates, 0, rte_memory_order_relaxed);
200 	rte_atomic_store_explicit(&update_cycles, 0, rte_memory_order_relaxed);
201 
202 	rte_atomic_store_explicit(&thr_id, 0, rte_memory_order_seq_cst);
203 
204 	printf("\nPerf Test: %d Readers\n", num_cores);
205 
206 	if (all_registered == 1)
207 		tmp_num_cores = num_cores;
208 	else
209 		tmp_num_cores = RTE_MAX_LCORE;
210 
211 	sz = rte_rcu_qsbr_get_memsize(tmp_num_cores);
212 	t[0] = (struct rte_rcu_qsbr *)rte_zmalloc("rcu0", sz,
213 						RTE_CACHE_LINE_SIZE);
214 	/* QS variable is initialized */
215 	rte_rcu_qsbr_init(t[0], tmp_num_cores);
216 
217 	/* Reader threads are launched */
218 	for (i = 0; i < num_cores; i++)
219 		rte_eal_remote_launch(test_rcu_qsbr_reader_perf, NULL,
220 					enabled_core_ids[i]);
221 
222 	/* Wait until all readers have exited */
223 	rte_eal_mp_wait_lcore();
224 
225 	printf("Total quiescent state updates = %"PRIi64"\n",
226 		rte_atomic_load_explicit(&updates, rte_memory_order_relaxed));
227 	printf("Cycles per %d quiescent state updates: %"PRIi64"\n",
228 		RCU_SCALE_DOWN,
229 		rte_atomic_load_explicit(&update_cycles, rte_memory_order_relaxed) /
230 		(rte_atomic_load_explicit(&updates, rte_memory_order_relaxed) / RCU_SCALE_DOWN));
231 
232 	rte_free(t[0]);
233 
234 	return 0;
235 }
236 
237 /*
238  * Perf test:
239  * Multiple writer, Single QS variable, Non-blocking rcu_qsbr_check
240  */
241 static int
test_rcu_qsbr_wperf(void)242 test_rcu_qsbr_wperf(void)
243 {
244 	size_t sz;
245 	unsigned int i;
246 
247 	rte_atomic_store_explicit(&checks, 0, rte_memory_order_relaxed);
248 	rte_atomic_store_explicit(&check_cycles, 0, rte_memory_order_relaxed);
249 
250 	rte_atomic_store_explicit(&thr_id, 0, rte_memory_order_seq_cst);
251 
252 	printf("\nPerf test: %d Writers ('wait' in qsbr_check == false)\n",
253 		num_cores);
254 
255 	/* Number of readers does not matter for QS variable in this test
256 	 * case as no reader will be registered.
257 	 */
258 	sz = rte_rcu_qsbr_get_memsize(RTE_MAX_LCORE);
259 	t[0] = (struct rte_rcu_qsbr *)rte_zmalloc("rcu0", sz,
260 						RTE_CACHE_LINE_SIZE);
261 	/* QS variable is initialized */
262 	rte_rcu_qsbr_init(t[0], RTE_MAX_LCORE);
263 
264 	/* Writer threads are launched */
265 	for (i = 0; i < num_cores; i++)
266 		rte_eal_remote_launch(test_rcu_qsbr_writer_perf,
267 				(void *)0, enabled_core_ids[i]);
268 
269 	/* Wait until all readers have exited */
270 	rte_eal_mp_wait_lcore();
271 
272 	printf("Total RCU checks = %"PRIi64"\n", rte_atomic_load_explicit(&checks,
273 			rte_memory_order_relaxed));
274 	printf("Cycles per %d checks: %"PRIi64"\n", RCU_SCALE_DOWN,
275 		rte_atomic_load_explicit(&check_cycles, rte_memory_order_relaxed) /
276 		(rte_atomic_load_explicit(&checks, rte_memory_order_relaxed) / RCU_SCALE_DOWN));
277 
278 	rte_free(t[0]);
279 
280 	return 0;
281 }
282 
283 /*
284  * RCU test cases using rte_hash data structure.
285  */
286 static int
test_rcu_qsbr_hash_reader(void * arg)287 test_rcu_qsbr_hash_reader(void *arg)
288 {
289 	struct rte_rcu_qsbr *temp;
290 	struct rte_hash *hash = NULL;
291 	int i;
292 	uint64_t loop_cnt = 0;
293 	uint64_t begin, cycles;
294 	uint32_t thread_id = alloc_thread_id();
295 	uint8_t read_type = (uint8_t)((uintptr_t)arg);
296 	uint32_t *pdata;
297 
298 	temp = t[read_type];
299 	hash = h;
300 
301 	rte_rcu_qsbr_thread_register(temp, thread_id);
302 
303 	begin = rte_rdtsc_precise();
304 
305 	do {
306 		rte_rcu_qsbr_thread_online(temp, thread_id);
307 		for (i = 0; i < TOTAL_ENTRY; i++) {
308 			rte_rcu_qsbr_lock(temp, thread_id);
309 			if (rte_hash_lookup_data(hash, keys + i,
310 					(void **)&pdata) != -ENOENT) {
311 				pdata[thread_id] = 0;
312 				while (pdata[thread_id] < COUNTER_VALUE)
313 					pdata[thread_id]++;
314 			}
315 			rte_rcu_qsbr_unlock(temp, thread_id);
316 		}
317 		/* Update quiescent state counter */
318 		rte_rcu_qsbr_quiescent(temp, thread_id);
319 		rte_rcu_qsbr_thread_offline(temp, thread_id);
320 		loop_cnt++;
321 	} while (!writer_done);
322 
323 	cycles = rte_rdtsc_precise() - begin;
324 	rte_atomic_fetch_add_explicit(&update_cycles, cycles, rte_memory_order_relaxed);
325 	rte_atomic_fetch_add_explicit(&updates, loop_cnt, rte_memory_order_relaxed);
326 
327 	rte_rcu_qsbr_thread_unregister(temp, thread_id);
328 
329 	return 0;
330 }
331 
init_hash(void)332 static struct rte_hash *init_hash(void)
333 {
334 	int i;
335 	struct rte_hash *hash = NULL;
336 
337 	snprintf(hash_name, 8, "hash");
338 	struct rte_hash_parameters hash_params = {
339 		.entries = TOTAL_ENTRY,
340 		.key_len = sizeof(uint32_t),
341 		.hash_func_init_val = 0,
342 		.socket_id = rte_socket_id(),
343 		.hash_func = rte_hash_crc,
344 		.extra_flag =
345 			RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF,
346 		.name = hash_name,
347 	};
348 
349 	hash = rte_hash_create(&hash_params);
350 	if (hash == NULL) {
351 		printf("Hash create Failed\n");
352 		return NULL;
353 	}
354 
355 	for (i = 0; i < TOTAL_ENTRY; i++) {
356 		hash_data[i] = rte_zmalloc(NULL,
357 				sizeof(uint32_t) * RTE_MAX_LCORE, 0);
358 		if (hash_data[i] == NULL) {
359 			printf("No memory\n");
360 			return NULL;
361 		}
362 	}
363 	keys = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_ENTRY, 0);
364 	if (keys == NULL) {
365 		printf("No memory\n");
366 		return NULL;
367 	}
368 
369 	for (i = 0; i < TOTAL_ENTRY; i++)
370 		keys[i] = i;
371 
372 	for (i = 0; i < TOTAL_ENTRY; i++) {
373 		if (rte_hash_add_key_data(hash, keys + i,
374 				(void *)((uintptr_t)hash_data[i])) < 0) {
375 			printf("Hash key add Failed #%d\n", i);
376 			return NULL;
377 		}
378 	}
379 	return hash;
380 }
381 
382 /*
383  * Functional test:
384  * Single writer, Single QS variable Single QSBR query, Blocking rcu_qsbr_check
385  */
386 static int
test_rcu_qsbr_sw_sv_1qs(void)387 test_rcu_qsbr_sw_sv_1qs(void)
388 {
389 	uint64_t token, begin, cycles;
390 	size_t sz;
391 	unsigned int i, j, tmp_num_cores;
392 	int32_t pos;
393 
394 	writer_done = 0;
395 
396 	rte_atomic_store_explicit(&updates, 0, rte_memory_order_relaxed);
397 	rte_atomic_store_explicit(&update_cycles, 0, rte_memory_order_relaxed);
398 	rte_atomic_store_explicit(&checks, 0, rte_memory_order_relaxed);
399 	rte_atomic_store_explicit(&check_cycles, 0, rte_memory_order_relaxed);
400 
401 	rte_atomic_store_explicit(&thr_id, 0, rte_memory_order_seq_cst);
402 
403 	printf("\nPerf test: 1 writer, %d readers, 1 QSBR variable, 1 QSBR Query, Blocking QSBR Check\n", num_cores);
404 
405 	if (all_registered == 1)
406 		tmp_num_cores = num_cores;
407 	else
408 		tmp_num_cores = RTE_MAX_LCORE;
409 
410 	sz = rte_rcu_qsbr_get_memsize(tmp_num_cores);
411 	t[0] = (struct rte_rcu_qsbr *)rte_zmalloc("rcu0", sz,
412 						RTE_CACHE_LINE_SIZE);
413 	/* QS variable is initialized */
414 	rte_rcu_qsbr_init(t[0], tmp_num_cores);
415 
416 	/* Shared data structure created */
417 	h = init_hash();
418 	if (h == NULL) {
419 		printf("Hash init failed\n");
420 		goto error;
421 	}
422 
423 	/* Reader threads are launched */
424 	for (i = 0; i < num_cores; i++)
425 		rte_eal_remote_launch(test_rcu_qsbr_hash_reader, NULL,
426 					enabled_core_ids[i]);
427 
428 	begin = rte_rdtsc_precise();
429 
430 	for (i = 0; i < TOTAL_ENTRY; i++) {
431 		/* Delete elements from the shared data structure */
432 		pos = rte_hash_del_key(h, keys + i);
433 		if (pos < 0) {
434 			printf("Delete key failed #%d\n", keys[i]);
435 			goto error;
436 		}
437 		/* Start the quiescent state query process */
438 		token = rte_rcu_qsbr_start(t[0]);
439 
440 		/* Check the quiescent state status */
441 		rte_rcu_qsbr_check(t[0], token, true);
442 		for (j = 0; j < tmp_num_cores; j++) {
443 			if (hash_data[i][j] != COUNTER_VALUE &&
444 				hash_data[i][j] != 0) {
445 				printf("Reader thread ID %u did not complete #%d =  %d\n",
446 					j, i, hash_data[i][j]);
447 				goto error;
448 			}
449 		}
450 
451 		if (rte_hash_free_key_with_position(h, pos) < 0) {
452 			printf("Failed to free the key #%d\n", keys[i]);
453 			goto error;
454 		}
455 		rte_free(hash_data[i]);
456 		hash_data[i] = NULL;
457 	}
458 
459 	cycles = rte_rdtsc_precise() - begin;
460 	rte_atomic_fetch_add_explicit(&check_cycles, cycles, rte_memory_order_relaxed);
461 	rte_atomic_fetch_add_explicit(&checks, i, rte_memory_order_relaxed);
462 
463 	writer_done = 1;
464 
465 	/* Wait and check return value from reader threads */
466 	for (i = 0; i < num_cores; i++)
467 		if (rte_eal_wait_lcore(enabled_core_ids[i]) < 0)
468 			goto error;
469 	rte_hash_free(h);
470 	rte_free(keys);
471 
472 	printf("Following numbers include calls to rte_hash functions\n");
473 	printf("Cycles per 1 quiescent state update(online/update/offline): %"PRIi64"\n",
474 		rte_atomic_load_explicit(&update_cycles, rte_memory_order_relaxed) /
475 		rte_atomic_load_explicit(&updates, rte_memory_order_relaxed));
476 
477 	printf("Cycles per 1 check(start, check): %"PRIi64"\n\n",
478 		rte_atomic_load_explicit(&check_cycles, rte_memory_order_relaxed) /
479 		rte_atomic_load_explicit(&checks, rte_memory_order_relaxed));
480 
481 	rte_free(t[0]);
482 
483 	return 0;
484 
485 error:
486 	writer_done = 1;
487 	/* Wait until all readers have exited */
488 	rte_eal_mp_wait_lcore();
489 
490 	rte_hash_free(h);
491 	rte_free(keys);
492 	for (i = 0; i < TOTAL_ENTRY; i++)
493 		rte_free(hash_data[i]);
494 
495 	rte_free(t[0]);
496 
497 	return -1;
498 }
499 
500 /*
501  * Functional test:
502  * Single writer, Single QS variable, Single QSBR query,
503  * Non-blocking rcu_qsbr_check
504  */
505 static int
test_rcu_qsbr_sw_sv_1qs_non_blocking(void)506 test_rcu_qsbr_sw_sv_1qs_non_blocking(void)
507 {
508 	uint64_t token, begin, cycles;
509 	int ret;
510 	size_t sz;
511 	unsigned int i, j, tmp_num_cores;
512 	int32_t pos;
513 
514 	writer_done = 0;
515 
516 	printf("Perf test: 1 writer, %d readers, 1 QSBR variable, 1 QSBR Query, Non-Blocking QSBR check\n", num_cores);
517 
518 	rte_atomic_store_explicit(&thr_id, 0, rte_memory_order_relaxed);
519 
520 	if (all_registered == 1)
521 		tmp_num_cores = num_cores;
522 	else
523 		tmp_num_cores = RTE_MAX_LCORE;
524 
525 	sz = rte_rcu_qsbr_get_memsize(tmp_num_cores);
526 	t[0] = (struct rte_rcu_qsbr *)rte_zmalloc("rcu0", sz,
527 						RTE_CACHE_LINE_SIZE);
528 	/* QS variable is initialized */
529 	rte_rcu_qsbr_init(t[0], tmp_num_cores);
530 
531 	/* Shared data structure created */
532 	h = init_hash();
533 	if (h == NULL) {
534 		printf("Hash init failed\n");
535 		goto error;
536 	}
537 
538 	/* Reader threads are launched */
539 	for (i = 0; i < num_cores; i++)
540 		rte_eal_remote_launch(test_rcu_qsbr_hash_reader, NULL,
541 					enabled_core_ids[i]);
542 
543 	begin = rte_rdtsc_precise();
544 
545 	for (i = 0; i < TOTAL_ENTRY; i++) {
546 		/* Delete elements from the shared data structure */
547 		pos = rte_hash_del_key(h, keys + i);
548 		if (pos < 0) {
549 			printf("Delete key failed #%d\n", keys[i]);
550 			goto error;
551 		}
552 		/* Start the quiescent state query process */
553 		token = rte_rcu_qsbr_start(t[0]);
554 
555 		/* Check the quiescent state status */
556 		do {
557 			ret = rte_rcu_qsbr_check(t[0], token, false);
558 		} while (ret == 0);
559 		for (j = 0; j < tmp_num_cores; j++) {
560 			if (hash_data[i][j] != COUNTER_VALUE &&
561 				hash_data[i][j] != 0) {
562 				printf("Reader thread ID %u did not complete #%d =  %d\n",
563 					j, i, hash_data[i][j]);
564 				goto error;
565 			}
566 		}
567 
568 		if (rte_hash_free_key_with_position(h, pos) < 0) {
569 			printf("Failed to free the key #%d\n", keys[i]);
570 			goto error;
571 		}
572 		rte_free(hash_data[i]);
573 		hash_data[i] = NULL;
574 	}
575 
576 	cycles = rte_rdtsc_precise() - begin;
577 	rte_atomic_fetch_add_explicit(&check_cycles, cycles, rte_memory_order_relaxed);
578 	rte_atomic_fetch_add_explicit(&checks, i, rte_memory_order_relaxed);
579 
580 	writer_done = 1;
581 	/* Wait and check return value from reader threads */
582 	for (i = 0; i < num_cores; i++)
583 		if (rte_eal_wait_lcore(enabled_core_ids[i]) < 0)
584 			goto error;
585 	rte_hash_free(h);
586 	rte_free(keys);
587 
588 	printf("Following numbers include calls to rte_hash functions\n");
589 	printf("Cycles per 1 quiescent state update(online/update/offline): %"PRIi64"\n",
590 		rte_atomic_load_explicit(&update_cycles, rte_memory_order_relaxed) /
591 		rte_atomic_load_explicit(&updates, rte_memory_order_relaxed));
592 
593 	printf("Cycles per 1 check(start, check): %"PRIi64"\n\n",
594 		rte_atomic_load_explicit(&check_cycles, rte_memory_order_relaxed) /
595 		rte_atomic_load_explicit(&checks, rte_memory_order_relaxed));
596 
597 	rte_free(t[0]);
598 
599 	return 0;
600 
601 error:
602 	writer_done = 1;
603 	/* Wait until all readers have exited */
604 	rte_eal_mp_wait_lcore();
605 
606 	rte_hash_free(h);
607 	rte_free(keys);
608 	for (i = 0; i < TOTAL_ENTRY; i++)
609 		rte_free(hash_data[i]);
610 
611 	rte_free(t[0]);
612 
613 	return -1;
614 }
615 
616 static int
test_rcu_qsbr_main(void)617 test_rcu_qsbr_main(void)
618 {
619 	uint16_t core_id;
620 
621 	if (RTE_EXEC_ENV_IS_WINDOWS)
622 		return TEST_SKIPPED;
623 
624 	if (rte_lcore_count() < 3) {
625 		printf("Not enough cores for rcu_qsbr_perf_autotest, expecting at least 3\n");
626 		return TEST_SKIPPED;
627 	}
628 
629 	rte_atomic_store_explicit(&updates, 0, rte_memory_order_relaxed);
630 	rte_atomic_store_explicit(&update_cycles, 0, rte_memory_order_relaxed);
631 	rte_atomic_store_explicit(&checks, 0, rte_memory_order_relaxed);
632 	rte_atomic_store_explicit(&check_cycles, 0, rte_memory_order_relaxed);
633 
634 	num_cores = 0;
635 	RTE_LCORE_FOREACH_WORKER(core_id) {
636 		enabled_core_ids[num_cores] = core_id;
637 		num_cores++;
638 	}
639 
640 	printf("Number of cores provided = %d\n", num_cores);
641 	printf("Perf test with all reader threads registered\n");
642 	printf("--------------------------------------------\n");
643 	all_registered = 1;
644 
645 	if (test_rcu_qsbr_perf() < 0)
646 		goto test_fail;
647 
648 	if (test_rcu_qsbr_rperf() < 0)
649 		goto test_fail;
650 
651 	if (test_rcu_qsbr_wperf() < 0)
652 		goto test_fail;
653 
654 	if (test_rcu_qsbr_sw_sv_1qs() < 0)
655 		goto test_fail;
656 
657 	if (test_rcu_qsbr_sw_sv_1qs_non_blocking() < 0)
658 		goto test_fail;
659 
660 	/* Make sure the actual number of cores provided is less than
661 	 * RTE_MAX_LCORE. This will allow for some threads not
662 	 * to be registered on the QS variable.
663 	 */
664 	if (num_cores >= RTE_MAX_LCORE) {
665 		printf("Test failed! number of cores provided should be less than %d\n",
666 			RTE_MAX_LCORE);
667 		goto test_fail;
668 	}
669 
670 	printf("Perf test with some of reader threads registered\n");
671 	printf("------------------------------------------------\n");
672 	all_registered = 0;
673 
674 	if (test_rcu_qsbr_perf() < 0)
675 		goto test_fail;
676 
677 	if (test_rcu_qsbr_rperf() < 0)
678 		goto test_fail;
679 
680 	if (test_rcu_qsbr_wperf() < 0)
681 		goto test_fail;
682 
683 	if (test_rcu_qsbr_sw_sv_1qs() < 0)
684 		goto test_fail;
685 
686 	if (test_rcu_qsbr_sw_sv_1qs_non_blocking() < 0)
687 		goto test_fail;
688 
689 	printf("\n");
690 
691 	return 0;
692 
693 test_fail:
694 	return -1;
695 }
696 
697 REGISTER_PERF_TEST(rcu_qsbr_perf_autotest, test_rcu_qsbr_main);
698