xref: /dpdk/app/test/test_hash_readwrite.c (revision 7adf992fb9bf7162a7edc45b50d10fbb1d57824d)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4 
5 #include <inttypes.h>
6 #include <locale.h>
7 
8 #include <rte_cycles.h>
9 #include <rte_hash.h>
10 #include <rte_hash_crc.h>
11 #include <rte_jhash.h>
12 #include <rte_launch.h>
13 #include <rte_malloc.h>
14 #include <rte_random.h>
15 #include <rte_spinlock.h>
16 
17 #include "test.h"
18 
19 #define RTE_RWTEST_FAIL 0
20 
21 #define TOTAL_ENTRY (5*1024*1024)
22 #define TOTAL_INSERT (4.5*1024*1024)
23 #define TOTAL_INSERT_EXT (5*1024*1024)
24 
25 #define NUM_TEST 3
26 unsigned int core_cnt[NUM_TEST] = {2, 4, 8};
27 
28 unsigned int slave_core_ids[RTE_MAX_LCORE];
29 struct perf {
30 	uint32_t single_read;
31 	uint32_t single_write;
32 	uint32_t read_only[NUM_TEST];
33 	uint32_t write_only[NUM_TEST];
34 	uint32_t read_write_r[NUM_TEST];
35 	uint32_t read_write_w[NUM_TEST];
36 };
37 
38 static struct perf htm_results, non_htm_results;
39 
40 struct {
41 	uint32_t *keys;
42 	uint8_t *found;
43 	uint32_t num_insert;
44 	uint32_t rounded_tot_insert;
45 	struct rte_hash *h;
46 } tbl_rw_test_param;
47 
48 static rte_atomic64_t gcycles;
49 static rte_atomic64_t ginsertions;
50 
51 static rte_atomic64_t gread_cycles;
52 static rte_atomic64_t gwrite_cycles;
53 
54 static rte_atomic64_t greads;
55 static rte_atomic64_t gwrites;
56 
57 static int
58 test_hash_readwrite_worker(__attribute__((unused)) void *arg)
59 {
60 	uint64_t i, offset;
61 	uint32_t lcore_id = rte_lcore_id();
62 	uint64_t begin, cycles;
63 	int *ret;
64 
65 	ret = rte_malloc(NULL, sizeof(int) *
66 				tbl_rw_test_param.num_insert, 0);
67 	for (i = 0; i < rte_lcore_count(); i++) {
68 		if (slave_core_ids[i] == lcore_id)
69 			break;
70 	}
71 	offset = tbl_rw_test_param.num_insert * i;
72 
73 	printf("Core #%d inserting and reading %d: %'"PRId64" - %'"PRId64"\n",
74 	       lcore_id, tbl_rw_test_param.num_insert,
75 	       offset, offset + tbl_rw_test_param.num_insert - 1);
76 
77 	begin = rte_rdtsc_precise();
78 
79 	for (i = offset; i < offset + tbl_rw_test_param.num_insert; i++) {
80 
81 		if (rte_hash_lookup(tbl_rw_test_param.h,
82 				tbl_rw_test_param.keys + i) > 0)
83 			break;
84 
85 		ret[i - offset] = rte_hash_add_key(tbl_rw_test_param.h,
86 				     tbl_rw_test_param.keys + i);
87 		if (ret[i - offset] < 0)
88 			break;
89 
90 		/* lookup a random key */
91 		uint32_t rand = rte_rand() % (i + 1 - offset);
92 
93 		if (rte_hash_lookup(tbl_rw_test_param.h,
94 				tbl_rw_test_param.keys + rand) != ret[rand])
95 			break;
96 
97 
98 		if (rte_hash_del_key(tbl_rw_test_param.h,
99 				tbl_rw_test_param.keys + rand) != ret[rand])
100 			break;
101 
102 		ret[rand] = rte_hash_add_key(tbl_rw_test_param.h,
103 					tbl_rw_test_param.keys + rand);
104 		if (ret[rand] < 0)
105 			break;
106 
107 		if (rte_hash_lookup(tbl_rw_test_param.h,
108 			tbl_rw_test_param.keys + rand) != ret[rand])
109 			break;
110 	}
111 
112 	cycles = rte_rdtsc_precise() - begin;
113 	rte_atomic64_add(&gcycles, cycles);
114 	rte_atomic64_add(&ginsertions, i - offset);
115 
116 	for (; i < offset + tbl_rw_test_param.num_insert; i++)
117 		tbl_rw_test_param.keys[i] = RTE_RWTEST_FAIL;
118 
119 	rte_free(ret);
120 	return 0;
121 }
122 
123 static int
124 init_params(int use_ext, int use_htm, int rw_lf, int use_jhash)
125 {
126 	unsigned int i;
127 
128 	uint32_t *keys = NULL;
129 	uint8_t *found = NULL;
130 	struct rte_hash *handle;
131 
132 	struct rte_hash_parameters hash_params = {
133 		.entries = TOTAL_ENTRY,
134 		.key_len = sizeof(uint32_t),
135 		.hash_func_init_val = 0,
136 		.socket_id = rte_socket_id(),
137 	};
138 	if (use_jhash)
139 		hash_params.hash_func = rte_jhash;
140 	else
141 		hash_params.hash_func = rte_hash_crc;
142 
143 	hash_params.extra_flag = RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD;
144 	if (use_htm)
145 		hash_params.extra_flag |=
146 			RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT;
147 	if (rw_lf)
148 		hash_params.extra_flag |=
149 			RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF;
150 	else
151 		hash_params.extra_flag |=
152 			RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY;
153 
154 	if (use_ext)
155 		hash_params.extra_flag |=
156 			RTE_HASH_EXTRA_FLAGS_EXT_TABLE;
157 	else
158 		hash_params.extra_flag &=
159 		       ~RTE_HASH_EXTRA_FLAGS_EXT_TABLE;
160 
161 	hash_params.name = "tests";
162 
163 	handle = rte_hash_create(&hash_params);
164 	if (handle == NULL) {
165 		printf("hash creation failed");
166 		return -1;
167 	}
168 
169 	tbl_rw_test_param.h = handle;
170 	keys = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_ENTRY, 0);
171 
172 	if (keys == NULL) {
173 		printf("RTE_MALLOC failed\n");
174 		goto err;
175 	}
176 
177 	found = rte_zmalloc(NULL, sizeof(uint8_t) * TOTAL_ENTRY, 0);
178 	if (found == NULL) {
179 		printf("RTE_ZMALLOC failed\n");
180 		goto err;
181 	}
182 
183 	tbl_rw_test_param.keys = keys;
184 	tbl_rw_test_param.found = found;
185 
186 	for (i = 0; i < TOTAL_ENTRY; i++)
187 		keys[i] = i;
188 
189 	return 0;
190 
191 err:
192 	rte_free(keys);
193 	rte_hash_free(handle);
194 
195 	return -1;
196 }
197 
198 static int
199 test_hash_readwrite_functional(int use_htm, int use_rw_lf, int use_ext)
200 {
201 	unsigned int i;
202 	const void *next_key;
203 	void *next_data;
204 	uint32_t iter = 0;
205 
206 	uint32_t duplicated_keys = 0;
207 	uint32_t lost_keys = 0;
208 	int use_jhash = 1;
209 	int slave_cnt = rte_lcore_count() - 1;
210 	uint32_t tot_insert = 0;
211 
212 	rte_atomic64_init(&gcycles);
213 	rte_atomic64_clear(&gcycles);
214 
215 	rte_atomic64_init(&ginsertions);
216 	rte_atomic64_clear(&ginsertions);
217 
218 	if (init_params(use_ext, use_htm, use_rw_lf, use_jhash) != 0)
219 		goto err;
220 
221 	if (use_ext)
222 		tot_insert = TOTAL_INSERT_EXT;
223 	else
224 		tot_insert = TOTAL_INSERT;
225 
226 	tbl_rw_test_param.num_insert =
227 		tot_insert / slave_cnt;
228 
229 	tbl_rw_test_param.rounded_tot_insert =
230 		tbl_rw_test_param.num_insert
231 		* slave_cnt;
232 
233 	printf("\nHTM = %d, RW-LF = %d, EXT-Table = %d\n",
234 		use_htm, use_rw_lf, use_ext);
235 	printf("++++++++Start function tests:+++++++++\n");
236 
237 	/* Fire all threads. */
238 	rte_eal_mp_remote_launch(test_hash_readwrite_worker,
239 				 NULL, SKIP_MASTER);
240 	rte_eal_mp_wait_lcore();
241 
242 	while (rte_hash_iterate(tbl_rw_test_param.h, &next_key,
243 			&next_data, &iter) >= 0) {
244 		/* Search for the key in the list of keys added .*/
245 		i = *(const uint32_t *)next_key;
246 		tbl_rw_test_param.found[i]++;
247 	}
248 
249 	for (i = 0; i < tbl_rw_test_param.rounded_tot_insert; i++) {
250 		if (tbl_rw_test_param.keys[i] != RTE_RWTEST_FAIL) {
251 			if (tbl_rw_test_param.found[i] > 1) {
252 				duplicated_keys++;
253 				break;
254 			}
255 			if (tbl_rw_test_param.found[i] == 0) {
256 				lost_keys++;
257 				printf("key %d is lost\n", i);
258 				break;
259 			}
260 		}
261 	}
262 
263 	if (duplicated_keys > 0) {
264 		printf("%d key duplicated\n", duplicated_keys);
265 		goto err_free;
266 	}
267 
268 	if (lost_keys > 0) {
269 		printf("%d key lost\n", lost_keys);
270 		goto err_free;
271 	}
272 
273 	printf("No key corrupted during read-write test.\n");
274 
275 	unsigned long long int cycles_per_insertion =
276 		rte_atomic64_read(&gcycles) /
277 		rte_atomic64_read(&ginsertions);
278 
279 	printf("cycles per insertion and lookup: %llu\n", cycles_per_insertion);
280 
281 	rte_free(tbl_rw_test_param.found);
282 	rte_free(tbl_rw_test_param.keys);
283 	rte_hash_free(tbl_rw_test_param.h);
284 	printf("+++++++++Complete function tests+++++++++\n");
285 	return 0;
286 
287 err_free:
288 	rte_free(tbl_rw_test_param.found);
289 	rte_free(tbl_rw_test_param.keys);
290 	rte_hash_free(tbl_rw_test_param.h);
291 err:
292 	return -1;
293 }
294 
295 static int
296 test_rw_reader(void *arg)
297 {
298 	uint64_t i;
299 	uint64_t begin, cycles;
300 	uint64_t read_cnt = (uint64_t)((uintptr_t)arg);
301 
302 	begin = rte_rdtsc_precise();
303 	for (i = 0; i < read_cnt; i++) {
304 		void *data = arg;
305 		rte_hash_lookup_data(tbl_rw_test_param.h,
306 				tbl_rw_test_param.keys + i,
307 				&data);
308 		if (i != (uint64_t)(uintptr_t)data) {
309 			printf("lookup find wrong value %"PRIu64","
310 				"%"PRIu64"\n", i,
311 				(uint64_t)(uintptr_t)data);
312 			break;
313 		}
314 	}
315 
316 	cycles = rte_rdtsc_precise() - begin;
317 	rte_atomic64_add(&gread_cycles, cycles);
318 	rte_atomic64_add(&greads, i);
319 	return 0;
320 }
321 
322 static int
323 test_rw_writer(void *arg)
324 {
325 	uint64_t i;
326 	uint32_t lcore_id = rte_lcore_id();
327 	uint64_t begin, cycles;
328 	int ret;
329 	uint64_t start_coreid = (uint64_t)(uintptr_t)arg;
330 	uint64_t offset;
331 
332 	for (i = 0; i < rte_lcore_count(); i++) {
333 		if (slave_core_ids[i] == lcore_id)
334 			break;
335 	}
336 
337 	offset = TOTAL_INSERT / 2 + (i - (start_coreid)) *
338 				tbl_rw_test_param.num_insert;
339 	begin = rte_rdtsc_precise();
340 	for (i = offset; i < offset + tbl_rw_test_param.num_insert; i++) {
341 		ret = rte_hash_add_key_data(tbl_rw_test_param.h,
342 				tbl_rw_test_param.keys + i,
343 				(void *)((uintptr_t)i));
344 		if (ret < 0) {
345 			printf("writer failed %"PRIu64"\n", i);
346 			break;
347 		}
348 	}
349 
350 	cycles = rte_rdtsc_precise() - begin;
351 	rte_atomic64_add(&gwrite_cycles, cycles);
352 	rte_atomic64_add(&gwrites, tbl_rw_test_param.num_insert);
353 	return 0;
354 }
355 
356 static int
357 test_hash_readwrite_perf(struct perf *perf_results, int use_htm,
358 							int reader_faster)
359 {
360 	unsigned int n;
361 	int ret;
362 	int start_coreid;
363 	uint64_t i, read_cnt;
364 
365 	const void *next_key;
366 	void *next_data;
367 	uint32_t iter;
368 	int use_jhash = 0;
369 
370 	uint32_t duplicated_keys = 0;
371 	uint32_t lost_keys = 0;
372 
373 	uint64_t start = 0, end = 0;
374 
375 	rte_atomic64_init(&greads);
376 	rte_atomic64_init(&gwrites);
377 	rte_atomic64_clear(&gwrites);
378 	rte_atomic64_clear(&greads);
379 
380 	rte_atomic64_init(&gread_cycles);
381 	rte_atomic64_clear(&gread_cycles);
382 	rte_atomic64_init(&gwrite_cycles);
383 	rte_atomic64_clear(&gwrite_cycles);
384 
385 	if (init_params(0, use_htm, 0, use_jhash) != 0)
386 		goto err;
387 
388 	/*
389 	 * Do a readers finish faster or writers finish faster test.
390 	 * When readers finish faster, we timing the readers, and when writers
391 	 * finish faster, we timing the writers.
392 	 * Divided by 10 or 2 is just experimental values to vary the workload
393 	 * of readers.
394 	 */
395 	if (reader_faster) {
396 		printf("++++++Start perf test: reader++++++++\n");
397 		read_cnt = TOTAL_INSERT / 10;
398 	} else {
399 		printf("++++++Start perf test: writer++++++++\n");
400 		read_cnt = TOTAL_INSERT / 2;
401 	}
402 
403 	/* We first test single thread performance */
404 	start = rte_rdtsc_precise();
405 	/* Insert half of the keys */
406 	for (i = 0; i < TOTAL_INSERT / 2; i++) {
407 		ret = rte_hash_add_key_data(tbl_rw_test_param.h,
408 				     tbl_rw_test_param.keys + i,
409 					(void *)((uintptr_t)i));
410 		if (ret < 0) {
411 			printf("Failed to insert half of keys\n");
412 			goto err_free;
413 		}
414 	}
415 	end = rte_rdtsc_precise() - start;
416 	perf_results->single_write = end / i;
417 
418 	start = rte_rdtsc_precise();
419 
420 	for (i = 0; i < read_cnt; i++) {
421 		void *data;
422 		rte_hash_lookup_data(tbl_rw_test_param.h,
423 				tbl_rw_test_param.keys + i,
424 				&data);
425 		if (i != (uint64_t)(uintptr_t)data) {
426 			printf("lookup find wrong value"
427 					" %"PRIu64",%"PRIu64"\n", i,
428 					(uint64_t)(uintptr_t)data);
429 			break;
430 		}
431 	}
432 	end = rte_rdtsc_precise() - start;
433 	perf_results->single_read = end / i;
434 
435 	for (n = 0; n < NUM_TEST; n++) {
436 		unsigned int tot_slave_lcore = rte_lcore_count() - 1;
437 		if (tot_slave_lcore < core_cnt[n] * 2)
438 			goto finish;
439 
440 		rte_atomic64_clear(&greads);
441 		rte_atomic64_clear(&gread_cycles);
442 		rte_atomic64_clear(&gwrites);
443 		rte_atomic64_clear(&gwrite_cycles);
444 
445 		rte_hash_reset(tbl_rw_test_param.h);
446 
447 		tbl_rw_test_param.num_insert = TOTAL_INSERT / 2 / core_cnt[n];
448 		tbl_rw_test_param.rounded_tot_insert = TOTAL_INSERT / 2 +
449 						tbl_rw_test_param.num_insert *
450 						core_cnt[n];
451 
452 		for (i = 0; i < TOTAL_INSERT / 2; i++) {
453 			ret = rte_hash_add_key_data(tbl_rw_test_param.h,
454 					tbl_rw_test_param.keys + i,
455 					(void *)((uintptr_t)i));
456 			if (ret < 0) {
457 				printf("Failed to insert half of keys\n");
458 				goto err_free;
459 			}
460 		}
461 
462 		/* Then test multiple thread case but only all reads or
463 		 * all writes
464 		 */
465 
466 		/* Test only reader cases */
467 		for (i = 0; i < core_cnt[n]; i++)
468 			rte_eal_remote_launch(test_rw_reader,
469 					(void *)(uintptr_t)read_cnt,
470 					slave_core_ids[i]);
471 
472 		rte_eal_mp_wait_lcore();
473 
474 		start_coreid = i;
475 		/* Test only writer cases */
476 		for (; i < core_cnt[n] * 2; i++)
477 			rte_eal_remote_launch(test_rw_writer,
478 					(void *)((uintptr_t)start_coreid),
479 					slave_core_ids[i]);
480 
481 		rte_eal_mp_wait_lcore();
482 
483 		if (reader_faster) {
484 			unsigned long long int cycles_per_insertion =
485 				rte_atomic64_read(&gread_cycles) /
486 				rte_atomic64_read(&greads);
487 			perf_results->read_only[n] = cycles_per_insertion;
488 			printf("Reader only: cycles per lookup: %llu\n",
489 							cycles_per_insertion);
490 		}
491 
492 		else {
493 			unsigned long long int cycles_per_insertion =
494 				rte_atomic64_read(&gwrite_cycles) /
495 				rte_atomic64_read(&gwrites);
496 			perf_results->write_only[n] = cycles_per_insertion;
497 			printf("Writer only: cycles per writes: %llu\n",
498 							cycles_per_insertion);
499 		}
500 
501 		rte_atomic64_clear(&greads);
502 		rte_atomic64_clear(&gread_cycles);
503 		rte_atomic64_clear(&gwrites);
504 		rte_atomic64_clear(&gwrite_cycles);
505 
506 		rte_hash_reset(tbl_rw_test_param.h);
507 
508 		for (i = 0; i < TOTAL_INSERT / 2; i++) {
509 			ret = rte_hash_add_key_data(tbl_rw_test_param.h,
510 					tbl_rw_test_param.keys + i,
511 					(void *)((uintptr_t)i));
512 			if (ret < 0) {
513 				printf("Failed to insert half of keys\n");
514 				goto err_free;
515 			}
516 		}
517 
518 		start_coreid = core_cnt[n];
519 
520 		if (reader_faster) {
521 			for (i = core_cnt[n]; i < core_cnt[n] * 2; i++)
522 				rte_eal_remote_launch(test_rw_writer,
523 					(void *)((uintptr_t)start_coreid),
524 					slave_core_ids[i]);
525 			for (i = 0; i < core_cnt[n]; i++)
526 				rte_eal_remote_launch(test_rw_reader,
527 					(void *)(uintptr_t)read_cnt,
528 					slave_core_ids[i]);
529 		} else {
530 			for (i = 0; i < core_cnt[n]; i++)
531 				rte_eal_remote_launch(test_rw_reader,
532 					(void *)(uintptr_t)read_cnt,
533 					slave_core_ids[i]);
534 			for (; i < core_cnt[n] * 2; i++)
535 				rte_eal_remote_launch(test_rw_writer,
536 					(void *)((uintptr_t)start_coreid),
537 					slave_core_ids[i]);
538 		}
539 
540 		rte_eal_mp_wait_lcore();
541 
542 		iter = 0;
543 		memset(tbl_rw_test_param.found, 0, TOTAL_ENTRY);
544 		while (rte_hash_iterate(tbl_rw_test_param.h,
545 				&next_key, &next_data, &iter) >= 0) {
546 			/* Search for the key in the list of keys added .*/
547 			i = *(const uint32_t *)next_key;
548 			tbl_rw_test_param.found[i]++;
549 		}
550 
551 		for (i = 0; i < tbl_rw_test_param.rounded_tot_insert; i++) {
552 			if (tbl_rw_test_param.keys[i] != RTE_RWTEST_FAIL) {
553 				if (tbl_rw_test_param.found[i] > 1) {
554 					duplicated_keys++;
555 					break;
556 				}
557 				if (tbl_rw_test_param.found[i] == 0) {
558 					lost_keys++;
559 					printf("key %"PRIu64" is lost\n", i);
560 					break;
561 				}
562 			}
563 		}
564 
565 		if (duplicated_keys > 0) {
566 			printf("%d key duplicated\n", duplicated_keys);
567 			goto err_free;
568 		}
569 
570 		if (lost_keys > 0) {
571 			printf("%d key lost\n", lost_keys);
572 			goto err_free;
573 		}
574 
575 		printf("No key corrupted during read-write test.\n");
576 
577 		if (reader_faster) {
578 			unsigned long long int cycles_per_insertion =
579 				rte_atomic64_read(&gread_cycles) /
580 				rte_atomic64_read(&greads);
581 			perf_results->read_write_r[n] = cycles_per_insertion;
582 			printf("Read-write cycles per lookup: %llu\n",
583 							cycles_per_insertion);
584 		}
585 
586 		else {
587 			unsigned long long int cycles_per_insertion =
588 				rte_atomic64_read(&gwrite_cycles) /
589 				rte_atomic64_read(&gwrites);
590 			perf_results->read_write_w[n] = cycles_per_insertion;
591 			printf("Read-write cycles per writes: %llu\n",
592 							cycles_per_insertion);
593 		}
594 	}
595 
596 finish:
597 	rte_free(tbl_rw_test_param.found);
598 	rte_free(tbl_rw_test_param.keys);
599 	rte_hash_free(tbl_rw_test_param.h);
600 	return 0;
601 
602 err_free:
603 	rte_free(tbl_rw_test_param.found);
604 	rte_free(tbl_rw_test_param.keys);
605 	rte_hash_free(tbl_rw_test_param.h);
606 
607 err:
608 	return -1;
609 }
610 
611 static int
612 test_hash_rw_perf_main(void)
613 {
614 	/*
615 	 * Variables used to choose different tests.
616 	 * use_htm indicates if hardware transactional memory should be used.
617 	 * reader_faster indicates if the reader threads should finish earlier
618 	 * than writer threads. This is to timing either reader threads or
619 	 * writer threads for performance numbers.
620 	 */
621 	int use_htm, reader_faster;
622 	unsigned int i = 0, core_id = 0;
623 
624 	if (rte_lcore_count() < 3) {
625 		printf("Not enough cores for hash_readwrite_autotest, expecting at least 3\n");
626 		return TEST_SKIPPED;
627 	}
628 
629 	RTE_LCORE_FOREACH_SLAVE(core_id) {
630 		slave_core_ids[i] = core_id;
631 		i++;
632 	}
633 
634 	setlocale(LC_NUMERIC, "");
635 
636 	if (rte_tm_supported()) {
637 		printf("Hardware transactional memory (lock elision) "
638 			"is supported\n");
639 
640 		printf("Test read-write with Hardware transactional memory\n");
641 
642 		use_htm = 1;
643 
644 		reader_faster = 1;
645 		if (test_hash_readwrite_perf(&htm_results, use_htm,
646 							reader_faster) < 0)
647 			return -1;
648 
649 		reader_faster = 0;
650 		if (test_hash_readwrite_perf(&htm_results, use_htm,
651 							reader_faster) < 0)
652 			return -1;
653 	} else {
654 		printf("Hardware transactional memory (lock elision) "
655 			"is NOT supported\n");
656 	}
657 
658 	printf("Test read-write without Hardware transactional memory\n");
659 	use_htm = 0;
660 
661 	reader_faster = 1;
662 	if (test_hash_readwrite_perf(&non_htm_results, use_htm,
663 							reader_faster) < 0)
664 		return -1;
665 	reader_faster = 0;
666 	if (test_hash_readwrite_perf(&non_htm_results, use_htm,
667 							reader_faster) < 0)
668 		return -1;
669 
670 	printf("================\n");
671 	printf("Results summary:\n");
672 	printf("================\n");
673 
674 	printf("single read: %u\n", htm_results.single_read);
675 	printf("single write: %u\n", htm_results.single_write);
676 	for (i = 0; i < NUM_TEST; i++) {
677 		printf("+++ core_cnt: %u +++\n", core_cnt[i]);
678 		printf("HTM:\n");
679 		printf("  read only: %u\n", htm_results.read_only[i]);
680 		printf("  write only: %u\n", htm_results.write_only[i]);
681 		printf("  read-write read: %u\n", htm_results.read_write_r[i]);
682 		printf("  read-write write: %u\n", htm_results.read_write_w[i]);
683 
684 		printf("non HTM:\n");
685 		printf("  read only: %u\n", non_htm_results.read_only[i]);
686 		printf("  write only: %u\n", non_htm_results.write_only[i]);
687 		printf("  read-write read: %u\n",
688 			non_htm_results.read_write_r[i]);
689 		printf("  read-write write: %u\n",
690 			non_htm_results.read_write_w[i]);
691 	}
692 
693 	return 0;
694 }
695 
696 static int
697 test_hash_rw_func_main(void)
698 {
699 	/*
700 	 * Variables used to choose different tests.
701 	 * use_htm indicates if hardware transactional memory should be used.
702 	 * reader_faster indicates if the reader threads should finish earlier
703 	 * than writer threads. This is to timing either reader threads or
704 	 * writer threads for performance numbers.
705 	 */
706 	unsigned int i = 0, core_id = 0;
707 
708 	if (rte_lcore_count() < 3) {
709 		printf("Not enough cores for hash_readwrite_autotest, expecting at least 3\n");
710 		return TEST_SKIPPED;
711 	}
712 
713 	RTE_LCORE_FOREACH_SLAVE(core_id) {
714 		slave_core_ids[i] = core_id;
715 		i++;
716 	}
717 
718 	setlocale(LC_NUMERIC, "");
719 
720 	if (rte_tm_supported()) {
721 		printf("Hardware transactional memory (lock elision) "
722 			"is supported\n");
723 
724 		printf("Test read-write with Hardware transactional memory\n");
725 
726 		/* htm = 1, rw_lf = 0, ext = 0 */
727 		if (test_hash_readwrite_functional(1, 0, 0) < 0)
728 			return -1;
729 
730 		/* htm = 1, rw_lf = 1, ext = 0 */
731 		if (test_hash_readwrite_functional(1, 1, 0) < 0)
732 			return -1;
733 
734 		/* htm = 1, rw_lf = 0, ext = 1 */
735 		if (test_hash_readwrite_functional(1, 0, 1) < 0)
736 			return -1;
737 
738 		/* htm = 1, rw_lf = 1, ext = 1 */
739 		if (test_hash_readwrite_functional(1, 1, 1) < 0)
740 			return -1;
741 	} else {
742 		printf("Hardware transactional memory (lock elision) "
743 			"is NOT supported\n");
744 	}
745 
746 	printf("Test read-write without Hardware transactional memory\n");
747 	/* htm = 0, rw_lf = 0, ext = 0 */
748 	if (test_hash_readwrite_functional(0, 0, 0) < 0)
749 		return -1;
750 
751 	/* htm = 0, rw_lf = 1, ext = 0 */
752 	if (test_hash_readwrite_functional(0, 1, 0) < 0)
753 		return -1;
754 
755 	/* htm = 0, rw_lf = 0, ext = 1 */
756 	if (test_hash_readwrite_functional(0, 0, 1) < 0)
757 		return -1;
758 
759 	/* htm = 0, rw_lf = 1, ext = 1 */
760 	if (test_hash_readwrite_functional(0, 1, 1) < 0)
761 		return -1;
762 
763 	return 0;
764 }
765 
766 REGISTER_TEST_COMMAND(hash_readwrite_func_autotest, test_hash_rw_func_main);
767 REGISTER_TEST_COMMAND(hash_readwrite_perf_autotest, test_hash_rw_perf_main);
768