xref: /dpdk/app/test/test_hash_readwrite_lf_perf.c (revision 68a03efeed657e6e05f281479b33b51102797e15)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Arm Limited
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 #ifndef RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF
20 #define RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF 0
21 #endif
22 
23 #define BULK_LOOKUP_SIZE 32
24 
25 #define RUN_WITH_HTM_DISABLED 0
26 
27 #if (RUN_WITH_HTM_DISABLED)
28 
29 #define TOTAL_ENTRY (5*1024)
30 #define TOTAL_INSERT (5*1024)
31 
32 #else
33 
34 #define TOTAL_ENTRY (4*1024*1024)
35 #define TOTAL_INSERT (4*1024*1024)
36 
37 #endif
38 
39 #define READ_FAIL 1
40 #define READ_PASS_NO_KEY_SHIFTS 2
41 #define READ_PASS_SHIFT_PATH 4
42 #define READ_PASS_NON_SHIFT_PATH 8
43 #define BULK_LOOKUP 16
44 #define READ_PASS_KEY_SHIFTS_EXTBKT 32
45 
46 #define WRITE_NO_KEY_SHIFT 0
47 #define WRITE_KEY_SHIFT 1
48 #define WRITE_EXT_BKT 2
49 
50 #define NUM_TEST 3
51 
52 #define QSBR_REPORTING_INTERVAL 1024
53 
54 static unsigned int rwc_core_cnt[NUM_TEST] = {1, 2, 4};
55 
56 struct rwc_perf {
57 	uint32_t w_no_ks_r_hit[2][NUM_TEST];
58 	uint32_t w_no_ks_r_miss[2][NUM_TEST];
59 	uint32_t w_ks_r_hit_nsp[2][NUM_TEST];
60 	uint32_t w_ks_r_hit_sp[2][NUM_TEST];
61 	uint32_t w_ks_r_miss[2][NUM_TEST];
62 	uint32_t multi_rw[NUM_TEST - 1][2][NUM_TEST];
63 	uint32_t w_ks_r_hit_extbkt[2][NUM_TEST];
64 	uint32_t writer_add_del[NUM_TEST];
65 };
66 
67 static struct rwc_perf rwc_lf_results, rwc_non_lf_results;
68 
69 static struct {
70 	uint32_t *keys;
71 	uint32_t *keys_no_ks;
72 	uint32_t *keys_ks;
73 	uint32_t *keys_absent;
74 	uint32_t *keys_shift_path;
75 	uint32_t *keys_non_shift_path;
76 	uint32_t *keys_ext_bkt;
77 	uint32_t *keys_ks_extbkt;
78 	uint32_t count_keys_no_ks;
79 	uint32_t count_keys_ks;
80 	uint32_t count_keys_absent;
81 	uint32_t count_keys_shift_path;
82 	uint32_t count_keys_non_shift_path;
83 	uint32_t count_keys_extbkt;
84 	uint32_t count_keys_ks_extbkt;
85 	uint32_t single_insert;
86 	struct rte_hash *h;
87 } tbl_rwc_test_param;
88 
89 static uint64_t gread_cycles;
90 static uint64_t greads;
91 static uint64_t gwrite_cycles;
92 static uint64_t gwrites;
93 
94 static volatile uint8_t writer_done;
95 
96 static uint16_t enabled_core_ids[RTE_MAX_LCORE];
97 
98 static uint8_t *scanned_bkts;
99 
100 static inline uint16_t
101 get_short_sig(const hash_sig_t hash)
102 {
103 	return hash >> 16;
104 }
105 
106 static inline uint32_t
107 get_prim_bucket_index(__rte_unused const struct rte_hash *h,
108 		      const hash_sig_t hash)
109 {
110 	uint32_t num_buckets;
111 	uint32_t bucket_bitmask;
112 	num_buckets  = rte_align32pow2(TOTAL_ENTRY) / 8;
113 	bucket_bitmask = num_buckets - 1;
114 	return hash & bucket_bitmask;
115 }
116 
117 static inline uint32_t
118 get_alt_bucket_index(__rte_unused const struct rte_hash *h,
119 			uint32_t cur_bkt_idx, uint16_t sig)
120 {
121 	uint32_t num_buckets;
122 	uint32_t bucket_bitmask;
123 	num_buckets  = rte_align32pow2(TOTAL_ENTRY) / 8;
124 	bucket_bitmask = num_buckets - 1;
125 	return (cur_bkt_idx ^ sig) & bucket_bitmask;
126 }
127 
128 
129 static inline int
130 get_enabled_cores_list(void)
131 {
132 	uint32_t i = 0;
133 	uint16_t core_id;
134 	uint32_t max_cores = rte_lcore_count();
135 	RTE_LCORE_FOREACH(core_id) {
136 		enabled_core_ids[i] = core_id;
137 		i++;
138 	}
139 
140 	if (i != max_cores) {
141 		printf("Number of enabled cores in list is different from "
142 			"number given by rte_lcore_count()\n");
143 		return -1;
144 	}
145 	return 0;
146 }
147 
148 static int
149 init_params(int rwc_lf, int use_jhash, int htm, int ext_bkt)
150 {
151 	struct rte_hash *handle;
152 
153 	struct rte_hash_parameters hash_params = {
154 		.entries = TOTAL_ENTRY,
155 		.key_len = sizeof(uint32_t),
156 		.hash_func_init_val = 0,
157 		.socket_id = rte_socket_id(),
158 	};
159 
160 	if (use_jhash)
161 		hash_params.hash_func = rte_jhash;
162 	else
163 		hash_params.hash_func = rte_hash_crc;
164 
165 	if (rwc_lf)
166 		hash_params.extra_flag =
167 			RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF |
168 			RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD;
169 	else if (htm)
170 		hash_params.extra_flag =
171 			RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT |
172 			RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY |
173 			RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD;
174 	else
175 		hash_params.extra_flag =
176 			RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY |
177 			RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD;
178 
179 	if (ext_bkt)
180 		hash_params.extra_flag |= RTE_HASH_EXTRA_FLAGS_EXT_TABLE;
181 
182 	hash_params.name = "tests";
183 
184 	handle = rte_hash_create(&hash_params);
185 	if (handle == NULL) {
186 		printf("hash creation failed");
187 		return -1;
188 	}
189 
190 	tbl_rwc_test_param.h = handle;
191 	return 0;
192 }
193 
194 static inline int
195 check_bucket(uint32_t bkt_idx, uint32_t key)
196 {
197 	uint32_t iter;
198 	uint32_t prev_iter;
199 	uint32_t diff;
200 	uint32_t count = 0;
201 	const void *next_key;
202 	void *next_data;
203 
204 	/* Temporary bucket to hold the keys */
205 	uint32_t keys_in_bkt[8];
206 
207 	iter = bkt_idx * 8;
208 	prev_iter = iter;
209 	while (rte_hash_iterate(tbl_rwc_test_param.h,
210 			&next_key, &next_data, &iter) >= 0) {
211 
212 		/* Check for duplicate entries */
213 		if (*(const uint32_t *)next_key == key)
214 			return 1;
215 
216 		/* Identify if there is any free entry in the bucket */
217 		diff = iter - prev_iter;
218 		if (diff > 1)
219 			break;
220 
221 		prev_iter = iter;
222 		keys_in_bkt[count] = *(const uint32_t *)next_key;
223 		count++;
224 
225 		/* All entries in the bucket are occupied */
226 		if (count == 8) {
227 
228 			/*
229 			 * Check if bucket was not scanned before, to avoid
230 			 * duplicate keys.
231 			 */
232 			if (scanned_bkts[bkt_idx] == 0) {
233 				/*
234 				 * Since this bucket (pointed to by bkt_idx) is
235 				 * full, it is likely that key(s) in this
236 				 * bucket will be on the shift path, when
237 				 * collision occurs. Thus, add it to
238 				 * keys_shift_path.
239 				 */
240 				memcpy(tbl_rwc_test_param.keys_shift_path +
241 					tbl_rwc_test_param.count_keys_shift_path
242 					, keys_in_bkt, 32);
243 				tbl_rwc_test_param.count_keys_shift_path += 8;
244 				scanned_bkts[bkt_idx] = 1;
245 			}
246 			return -1;
247 		}
248 	}
249 	return 0;
250 }
251 
252 static int
253 generate_keys(void)
254 {
255 	uint32_t *keys = NULL;
256 	uint32_t *keys_no_ks = NULL;
257 	uint32_t *keys_ks = NULL;
258 	uint32_t *keys_absent = NULL;
259 	uint32_t *keys_non_shift_path = NULL;
260 	uint32_t *keys_ext_bkt = NULL;
261 	uint32_t *keys_ks_extbkt = NULL;
262 	uint32_t *found = NULL;
263 	uint32_t count_keys_no_ks = 0;
264 	uint32_t count_keys_ks = 0;
265 	uint32_t count_keys_extbkt = 0;
266 	uint32_t i;
267 
268 	if (init_params(0, 0, 0, 0) != 0)
269 		return -1;
270 
271 	/*
272 	 * keys will consist of a) keys whose addition to the hash table
273 	 * will result in shifting of the existing keys to their alternate
274 	 * locations b) keys whose addition to the hash table will not result
275 	 * in shifting of the existing keys.
276 	 */
277 	keys = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0);
278 	if (keys == NULL) {
279 		printf("RTE_MALLOC failed\n");
280 		goto err;
281 	}
282 
283 	/*
284 	 * keys_no_ks (no key-shifts): Subset of 'keys' - consists of keys  that
285 	 * will NOT result in shifting of the existing keys to their alternate
286 	 * locations. Roughly around 900K keys.
287 	 */
288 	keys_no_ks = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0);
289 	if (keys_no_ks == NULL) {
290 		printf("RTE_MALLOC failed\n");
291 		goto err;
292 	}
293 
294 	/*
295 	 * keys_ks (key-shifts): Subset of 'keys' - consists of keys that will
296 	 * result in shifting of the existing keys to their alternate locations.
297 	 * Roughly around 146K keys. There might be repeating keys. More code is
298 	 * required to filter out these keys which will complicate the test case
299 	 */
300 	keys_ks = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0);
301 	if (keys_ks == NULL) {
302 		printf("RTE_MALLOC failed\n");
303 		goto err;
304 	}
305 
306 	/* Used to identify keys not inserted in the hash table */
307 	found = rte_zmalloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0);
308 	if (found == NULL) {
309 		printf("RTE_MALLOC failed\n");
310 		goto err;
311 	}
312 
313 	/*
314 	 * This consist of keys not inserted to the hash table.
315 	 * Used to test perf of lookup on keys that do not exist in the table.
316 	 */
317 	keys_absent = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0);
318 	if (keys_absent == NULL) {
319 		printf("RTE_MALLOC failed\n");
320 		goto err;
321 	}
322 
323 	/*
324 	 * This consist of keys which are likely to be on the shift
325 	 * path (i.e. being moved to alternate location), when collision occurs
326 	 * on addition of a key to an already full primary bucket.
327 	 * Used to test perf of lookup on keys that are on the shift path.
328 	 */
329 	tbl_rwc_test_param.keys_shift_path = rte_malloc(NULL, sizeof(uint32_t) *
330 							TOTAL_INSERT, 0);
331 	if (tbl_rwc_test_param.keys_shift_path == NULL) {
332 		printf("RTE_MALLOC failed\n");
333 		goto err;
334 	}
335 
336 	/*
337 	 * This consist of keys which are never on the shift
338 	 * path (i.e. being moved to alternate location), when collision occurs
339 	 * on addition of a key to an already full primary bucket.
340 	 * Used to test perf of lookup on keys that are not on the shift path.
341 	 */
342 	keys_non_shift_path = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT,
343 					 0);
344 	if (keys_non_shift_path == NULL) {
345 		printf("RTE_MALLOC failed\n");
346 		goto err;
347 	}
348 
349 	/*
350 	 * This consist of keys which will be stored in extended buckets
351 	 */
352 	keys_ext_bkt = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0);
353 	if (keys_ext_bkt == NULL) {
354 		printf("RTE_MALLOC failed\n");
355 		goto err;
356 	}
357 
358 	/*
359 	 * This consist of keys which when deleted causes shifting of keys
360 	 * in extended buckets to respective secondary buckets
361 	 */
362 	keys_ks_extbkt = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0);
363 	if (keys_ks_extbkt == NULL) {
364 		printf("RTE_MALLOC failed\n");
365 		goto err;
366 	}
367 
368 	hash_sig_t sig;
369 	uint32_t prim_bucket_idx;
370 	uint32_t sec_bucket_idx;
371 	uint16_t short_sig;
372 	uint32_t num_buckets;
373 	num_buckets  = rte_align32pow2(TOTAL_ENTRY) / 8;
374 	int ret;
375 
376 	/*
377 	 * Used to mark bkts in which at least one key was shifted to its
378 	 * alternate location
379 	 */
380 	scanned_bkts = rte_malloc(NULL, sizeof(uint8_t) * num_buckets, 0);
381 	if (scanned_bkts == NULL) {
382 		printf("RTE_MALLOC failed\n");
383 		goto err;
384 	}
385 
386 	tbl_rwc_test_param.keys = keys;
387 	tbl_rwc_test_param.keys_no_ks = keys_no_ks;
388 	tbl_rwc_test_param.keys_ks = keys_ks;
389 	tbl_rwc_test_param.keys_absent = keys_absent;
390 	tbl_rwc_test_param.keys_non_shift_path = keys_non_shift_path;
391 	tbl_rwc_test_param.keys_ext_bkt = keys_ext_bkt;
392 	tbl_rwc_test_param.keys_ks_extbkt = keys_ks_extbkt;
393 	/* Generate keys by adding previous two keys, neglect overflow */
394 	printf("Generating keys...\n");
395 	keys[0] = 0;
396 	keys[1] = 1;
397 	for (i = 2; i < TOTAL_INSERT; i++)
398 		keys[i] = keys[i-1] + keys[i-2];
399 
400 	/* Segregate keys into keys_no_ks and keys_ks */
401 	for (i = 0; i < TOTAL_INSERT; i++) {
402 		/* Check if primary bucket has space.*/
403 		sig = rte_hash_hash(tbl_rwc_test_param.h,
404 					tbl_rwc_test_param.keys+i);
405 		prim_bucket_idx = get_prim_bucket_index(tbl_rwc_test_param.h,
406 							sig);
407 		ret = check_bucket(prim_bucket_idx, keys[i]);
408 		if (ret < 0) {
409 			/*
410 			 * Primary bucket is full, this key will result in
411 			 * shifting of the keys to their alternate locations.
412 			 */
413 			keys_ks[count_keys_ks] = keys[i];
414 			count_keys_ks++;
415 		} else if (ret == 0) {
416 			/*
417 			 * Primary bucket has space, this key will not result in
418 			 * shifting of the keys. Hence, add key to the table.
419 			 */
420 			ret = rte_hash_add_key_data(tbl_rwc_test_param.h,
421 							keys+i,
422 							(void *)((uintptr_t)i));
423 			if (ret < 0) {
424 				printf("writer failed %"PRIu32"\n", i);
425 				break;
426 			}
427 			keys_no_ks[count_keys_no_ks] = keys[i];
428 			count_keys_no_ks++;
429 		}
430 	}
431 
432 	for (i = 0; i < count_keys_no_ks; i++) {
433 		/*
434 		 * Identify keys in keys_no_ks with value less than
435 		 * 4M (HTM enabled) OR 5K (HTM disabled)
436 		 */
437 		if (keys_no_ks[i] < TOTAL_INSERT)
438 			found[keys_no_ks[i]]++;
439 	}
440 
441 	for (i = 0; i < count_keys_ks; i++) {
442 		/*
443 		 * Identify keys in keys_ks with value less than
444 		 * 4M (HTM enabled) OR 5K (HTM disabled)
445 		 */
446 		if (keys_ks[i] < TOTAL_INSERT)
447 			found[keys_ks[i]]++;
448 	}
449 
450 	uint32_t count_keys_absent = 0;
451 	for (i = 0; i < TOTAL_INSERT; i++) {
452 		/*
453 		 * Identify missing keys between 0 and
454 		 * 4M (HTM enabled) OR 5K (HTM disabled)
455 		 */
456 		if (found[i] == 0)
457 			keys_absent[count_keys_absent++] = i;
458 	}
459 
460 	/* Find keys that will not be on the shift path */
461 	uint32_t iter;
462 	const void *next_key;
463 	void *next_data;
464 	uint32_t count = 0;
465 	for (i = 0; i < num_buckets; i++) {
466 		/* Check bucket for no keys shifted to alternate locations */
467 		if (scanned_bkts[i] == 0) {
468 			iter = i * 8;
469 			while (rte_hash_iterate(tbl_rwc_test_param.h,
470 				&next_key, &next_data, &iter) >= 0) {
471 
472 				/* Check if key belongs to the current bucket */
473 				if (i >= (iter-1)/8)
474 					keys_non_shift_path[count++]
475 						= *(const uint32_t *)next_key;
476 				else
477 					break;
478 			}
479 		}
480 	}
481 
482 	tbl_rwc_test_param.count_keys_no_ks = count_keys_no_ks;
483 	tbl_rwc_test_param.count_keys_ks = count_keys_ks;
484 	tbl_rwc_test_param.count_keys_absent = count_keys_absent;
485 	tbl_rwc_test_param.count_keys_non_shift_path = count;
486 
487 	memset(scanned_bkts, 0, num_buckets);
488 	count = 0;
489 	/* Find keys that will be in extended buckets */
490 	for (i = 0; i < count_keys_ks; i++) {
491 		ret = rte_hash_add_key(tbl_rwc_test_param.h, keys_ks + i);
492 		if (ret < 0) {
493 			/* Key will be added to ext bkt */
494 			keys_ext_bkt[count_keys_extbkt++] = keys_ks[i];
495 			/* Sec bkt to be added to keys_ks_extbkt */
496 			sig = rte_hash_hash(tbl_rwc_test_param.h,
497 					tbl_rwc_test_param.keys_ks + i);
498 			prim_bucket_idx = get_prim_bucket_index(
499 						tbl_rwc_test_param.h, sig);
500 			short_sig = get_short_sig(sig);
501 			sec_bucket_idx = get_alt_bucket_index(
502 						tbl_rwc_test_param.h,
503 						prim_bucket_idx, short_sig);
504 			if (scanned_bkts[sec_bucket_idx] == 0)
505 				scanned_bkts[sec_bucket_idx] = 1;
506 		}
507 	}
508 
509 	/* Find keys that will shift keys in ext bucket*/
510 	for (i = 0; i < num_buckets; i++) {
511 		if (scanned_bkts[i] == 1) {
512 			iter = i * 8;
513 			while (rte_hash_iterate(tbl_rwc_test_param.h,
514 				&next_key, &next_data, &iter) >= 0) {
515 				/* Check if key belongs to the current bucket */
516 				if (i >= (iter-1)/8)
517 					keys_ks_extbkt[count++]
518 						= *(const uint32_t *)next_key;
519 				else
520 					break;
521 			}
522 		}
523 	}
524 
525 	tbl_rwc_test_param.count_keys_ks_extbkt = count;
526 	tbl_rwc_test_param.count_keys_extbkt = count_keys_extbkt;
527 
528 	printf("\nCount of keys NOT causing shifting of existing keys to "
529 	"alternate location: %d\n", tbl_rwc_test_param.count_keys_no_ks);
530 	printf("\nCount of keys causing shifting of existing keys to alternate "
531 		"locations: %d\n\n", tbl_rwc_test_param.count_keys_ks);
532 	printf("Count of absent keys that will never be added to the hash "
533 		"table: %d\n\n", tbl_rwc_test_param.count_keys_absent);
534 	printf("Count of keys likely to be on the shift path: %d\n\n",
535 	       tbl_rwc_test_param.count_keys_shift_path);
536 	printf("Count of keys not likely to be on the shift path: %d\n\n",
537 	       tbl_rwc_test_param.count_keys_non_shift_path);
538 	printf("Count of keys in extended buckets: %d\n\n",
539 	       tbl_rwc_test_param.count_keys_extbkt);
540 	printf("Count of keys shifting keys in ext buckets: %d\n\n",
541 	       tbl_rwc_test_param.count_keys_ks_extbkt);
542 
543 	rte_free(found);
544 	rte_free(scanned_bkts);
545 	rte_hash_free(tbl_rwc_test_param.h);
546 	return 0;
547 
548 err:
549 	rte_free(keys);
550 	rte_free(keys_no_ks);
551 	rte_free(keys_ks);
552 	rte_free(keys_absent);
553 	rte_free(found);
554 	rte_free(tbl_rwc_test_param.keys_shift_path);
555 	rte_free(keys_non_shift_path);
556 	rte_free(keys_ext_bkt);
557 	rte_free(keys_ks_extbkt);
558 	rte_free(scanned_bkts);
559 	rte_hash_free(tbl_rwc_test_param.h);
560 	return -1;
561 }
562 
563 static int
564 test_rwc_reader(__rte_unused void *arg)
565 {
566 	uint32_t i, j;
567 	int ret;
568 	uint64_t begin, cycles;
569 	uint32_t loop_cnt = 0;
570 	uint8_t read_type = (uint8_t)((uintptr_t)arg);
571 	uint32_t read_cnt;
572 	uint32_t *keys;
573 	uint32_t extra_keys;
574 	int32_t pos[BULK_LOOKUP_SIZE];
575 	void *temp_a[BULK_LOOKUP_SIZE];
576 
577 	if (read_type & READ_FAIL) {
578 		keys = tbl_rwc_test_param.keys_absent;
579 		read_cnt = tbl_rwc_test_param.count_keys_absent;
580 	} else if (read_type & READ_PASS_NO_KEY_SHIFTS) {
581 		keys = tbl_rwc_test_param.keys_no_ks;
582 		read_cnt = tbl_rwc_test_param.count_keys_no_ks;
583 	} else if (read_type & READ_PASS_SHIFT_PATH) {
584 		keys = tbl_rwc_test_param.keys_shift_path;
585 		read_cnt = tbl_rwc_test_param.count_keys_shift_path;
586 	} else if (read_type & READ_PASS_KEY_SHIFTS_EXTBKT) {
587 		keys = tbl_rwc_test_param.keys_ext_bkt;
588 		read_cnt = tbl_rwc_test_param.count_keys_extbkt;
589 	} else {
590 		keys = tbl_rwc_test_param.keys_non_shift_path;
591 		read_cnt = tbl_rwc_test_param.count_keys_non_shift_path;
592 	}
593 
594 	extra_keys = read_cnt & (BULK_LOOKUP_SIZE - 1);
595 
596 	begin = rte_rdtsc_precise();
597 	do {
598 		if (read_type & BULK_LOOKUP) {
599 			for (i = 0; i < (read_cnt - extra_keys);
600 			     i += BULK_LOOKUP_SIZE) {
601 				/* Array of  pointer to the list of keys */
602 				for (j = 0; j < BULK_LOOKUP_SIZE; j++)
603 					temp_a[j] = keys + i + j;
604 				rte_hash_lookup_bulk(tbl_rwc_test_param.h,
605 						   (const void **)
606 						   ((uintptr_t)temp_a),
607 						   BULK_LOOKUP_SIZE, pos);
608 				/* Validate lookup result */
609 				for (j = 0; j < BULK_LOOKUP_SIZE; j++)
610 					if ((read_type & READ_FAIL &&
611 					     pos[j] != -ENOENT) ||
612 					    (!(read_type & READ_FAIL) &&
613 					     pos[j] == -ENOENT)) {
614 						printf("lookup failed!"
615 						       "%"PRIu32"\n",
616 						       keys[i + j]);
617 						return -1;
618 					}
619 			}
620 			for (j = 0; j < extra_keys; j++)
621 				temp_a[j] = keys + i + j;
622 
623 			rte_hash_lookup_bulk(tbl_rwc_test_param.h,
624 					   (const void **)
625 					   ((uintptr_t)temp_a),
626 					   extra_keys, pos);
627 			for (j = 0; j < extra_keys; j++)
628 				if ((read_type & READ_FAIL &&
629 				     pos[j] != -ENOENT) ||
630 				    (!(read_type & READ_FAIL) &&
631 				     pos[j] == -ENOENT)) {
632 					printf("lookup failed! %"PRIu32"\n",
633 					       keys[i + j]);
634 					return -1;
635 				}
636 		} else {
637 			for (i = 0; i < read_cnt; i++) {
638 				ret = rte_hash_lookup
639 					(tbl_rwc_test_param.h, keys + i);
640 				if (((read_type & READ_FAIL) &&
641 				     (ret != -ENOENT)) ||
642 				    (!(read_type & READ_FAIL) &&
643 					ret == -ENOENT)) {
644 					printf("lookup failed! %"PRIu32"\n",
645 					       keys[i]);
646 					return -1;
647 				}
648 			}
649 		}
650 		loop_cnt++;
651 	} while (!writer_done);
652 
653 	cycles = rte_rdtsc_precise() - begin;
654 	__atomic_fetch_add(&gread_cycles, cycles, __ATOMIC_RELAXED);
655 	__atomic_fetch_add(&greads, read_cnt*loop_cnt, __ATOMIC_RELAXED);
656 	return 0;
657 }
658 
659 static int
660 write_keys(uint8_t write_type)
661 {
662 	uint32_t i;
663 	int ret;
664 	uint32_t key_cnt = 0;
665 	uint32_t *keys;
666 	if (write_type == WRITE_KEY_SHIFT) {
667 		key_cnt = tbl_rwc_test_param.count_keys_ks;
668 		keys = tbl_rwc_test_param.keys_ks;
669 	} else if (write_type == WRITE_NO_KEY_SHIFT) {
670 		key_cnt = tbl_rwc_test_param.count_keys_no_ks;
671 		keys = tbl_rwc_test_param.keys_no_ks;
672 	} else if (write_type == WRITE_EXT_BKT) {
673 		key_cnt = tbl_rwc_test_param.count_keys_extbkt;
674 		keys = tbl_rwc_test_param.keys_ext_bkt;
675 	}
676 	for (i = 0; i < key_cnt; i++) {
677 		ret = rte_hash_add_key(tbl_rwc_test_param.h, keys + i);
678 		if ((write_type == WRITE_NO_KEY_SHIFT) && ret < 0) {
679 			printf("writer failed %"PRIu32"\n", i);
680 			return -1;
681 		}
682 	}
683 	return 0;
684 }
685 
686 static int
687 test_rwc_multi_writer(__rte_unused void *arg)
688 {
689 	uint32_t i, offset;
690 	uint32_t pos_core = (uint32_t)((uintptr_t)arg);
691 	offset = pos_core * tbl_rwc_test_param.single_insert;
692 	for (i = offset; i < offset + tbl_rwc_test_param.single_insert; i++)
693 		rte_hash_add_key(tbl_rwc_test_param.h,
694 				 tbl_rwc_test_param.keys_ks + i);
695 	return 0;
696 }
697 
698 /*
699  * Test lookup perf:
700  * Reader(s) lookup keys present in the table.
701  */
702 static int
703 test_hash_add_no_ks_lookup_hit(struct rwc_perf *rwc_perf_results, int rwc_lf,
704 				int htm, int ext_bkt)
705 {
706 	unsigned int n, m;
707 	uint64_t i;
708 	int use_jhash = 0;
709 	uint8_t write_type = WRITE_NO_KEY_SHIFT;
710 	uint8_t read_type = READ_PASS_NO_KEY_SHIFTS;
711 
712 	if (init_params(rwc_lf, use_jhash, htm, ext_bkt) != 0)
713 		goto err;
714 	printf("\nTest: Hash add - no key-shifts, read - hit\n");
715 	for (m = 0; m < 2; m++) {
716 		if (m == 1) {
717 			printf("\n** With bulk-lookup **\n");
718 			read_type |= BULK_LOOKUP;
719 		}
720 		for (n = 0; n < NUM_TEST; n++) {
721 			unsigned int tot_lcore = rte_lcore_count();
722 			if (tot_lcore < rwc_core_cnt[n] + 1)
723 				goto finish;
724 
725 			printf("\nNumber of readers: %u\n", rwc_core_cnt[n]);
726 
727 			__atomic_store_n(&greads, 0, __ATOMIC_RELAXED);
728 			__atomic_store_n(&gread_cycles, 0, __ATOMIC_RELAXED);
729 
730 			rte_hash_reset(tbl_rwc_test_param.h);
731 			writer_done = 0;
732 			if (write_keys(write_type) < 0)
733 				goto err;
734 			writer_done = 1;
735 			for (i = 1; i <= rwc_core_cnt[n]; i++)
736 				rte_eal_remote_launch(test_rwc_reader,
737 						(void *)(uintptr_t)read_type,
738 							enabled_core_ids[i]);
739 
740 			for (i = 1; i <= rwc_core_cnt[n]; i++)
741 				if (rte_eal_wait_lcore(enabled_core_ids[i]) < 0)
742 					goto err;
743 
744 			unsigned long long cycles_per_lookup =
745 				__atomic_load_n(&gread_cycles, __ATOMIC_RELAXED)
746 				/ __atomic_load_n(&greads, __ATOMIC_RELAXED);
747 			rwc_perf_results->w_no_ks_r_hit[m][n]
748 						= cycles_per_lookup;
749 			printf("Cycles per lookup: %llu\n", cycles_per_lookup);
750 		}
751 	}
752 
753 finish:
754 	rte_hash_free(tbl_rwc_test_param.h);
755 	return 0;
756 
757 err:
758 	rte_eal_mp_wait_lcore();
759 	rte_hash_free(tbl_rwc_test_param.h);
760 	return -1;
761 }
762 
763 /*
764  * Test lookup perf:
765  * Reader(s) lookup keys absent in the table while
766  * 'Main' thread adds with no key-shifts.
767  */
768 static int
769 test_hash_add_no_ks_lookup_miss(struct rwc_perf *rwc_perf_results, int rwc_lf,
770 				int htm, int ext_bkt)
771 {
772 	unsigned int n, m;
773 	uint64_t i;
774 	int use_jhash = 0;
775 	uint8_t write_type = WRITE_NO_KEY_SHIFT;
776 	uint8_t read_type = READ_FAIL;
777 	int ret;
778 
779 	if (init_params(rwc_lf, use_jhash, htm, ext_bkt) != 0)
780 		goto err;
781 	printf("\nTest: Hash add - no key-shifts, Hash lookup - miss\n");
782 	for (m = 0; m < 2; m++) {
783 		if (m == 1) {
784 			printf("\n** With bulk-lookup **\n");
785 			read_type |= BULK_LOOKUP;
786 		}
787 		for (n = 0; n < NUM_TEST; n++) {
788 			unsigned int tot_lcore = rte_lcore_count();
789 			if (tot_lcore < rwc_core_cnt[n] + 1)
790 				goto finish;
791 
792 			printf("\nNumber of readers: %u\n", rwc_core_cnt[n]);
793 
794 			__atomic_store_n(&greads, 0, __ATOMIC_RELAXED);
795 			__atomic_store_n(&gread_cycles, 0, __ATOMIC_RELAXED);
796 
797 			rte_hash_reset(tbl_rwc_test_param.h);
798 			writer_done = 0;
799 
800 			for (i = 1; i <= rwc_core_cnt[n]; i++)
801 				rte_eal_remote_launch(test_rwc_reader,
802 						(void *)(uintptr_t)read_type,
803 							enabled_core_ids[i]);
804 			ret = write_keys(write_type);
805 			writer_done = 1;
806 
807 			if (ret < 0)
808 				goto err;
809 			for (i = 1; i <= rwc_core_cnt[n]; i++)
810 				if (rte_eal_wait_lcore(enabled_core_ids[i]) < 0)
811 					goto err;
812 
813 			unsigned long long cycles_per_lookup =
814 				__atomic_load_n(&gread_cycles, __ATOMIC_RELAXED)
815 				/ __atomic_load_n(&greads, __ATOMIC_RELAXED);
816 			rwc_perf_results->w_no_ks_r_miss[m][n]
817 						= cycles_per_lookup;
818 			printf("Cycles per lookup: %llu\n", cycles_per_lookup);
819 		}
820 	}
821 
822 finish:
823 	rte_hash_free(tbl_rwc_test_param.h);
824 	return 0;
825 
826 err:
827 	rte_eal_mp_wait_lcore();
828 	rte_hash_free(tbl_rwc_test_param.h);
829 	return -1;
830 }
831 
832 /*
833  * Test lookup perf:
834  * Reader(s) lookup keys present in the table and not likely to be on the
835  * shift path  while 'Main' thread adds keys causing key-shifts.
836  */
837 static int
838 test_hash_add_ks_lookup_hit_non_sp(struct rwc_perf *rwc_perf_results,
839 				    int rwc_lf, int htm, int ext_bkt)
840 {
841 	unsigned int n, m;
842 	uint64_t i;
843 	int use_jhash = 0;
844 	int ret;
845 	uint8_t write_type;
846 	uint8_t read_type = READ_PASS_NON_SHIFT_PATH;
847 
848 	if (init_params(rwc_lf, use_jhash, htm, ext_bkt) != 0)
849 		goto err;
850 	printf("\nTest: Hash add - key shift, Hash lookup - hit"
851 	       " (non-shift-path)\n");
852 	for (m = 0; m < 2; m++) {
853 		if (m == 1) {
854 			printf("\n** With bulk-lookup **\n");
855 			read_type |= BULK_LOOKUP;
856 		}
857 		for (n = 0; n < NUM_TEST; n++) {
858 			unsigned int tot_lcore = rte_lcore_count();
859 			if (tot_lcore < rwc_core_cnt[n] + 1)
860 				goto finish;
861 
862 			printf("\nNumber of readers: %u\n", rwc_core_cnt[n]);
863 
864 			__atomic_store_n(&greads, 0, __ATOMIC_RELAXED);
865 			__atomic_store_n(&gread_cycles, 0, __ATOMIC_RELAXED);
866 
867 			rte_hash_reset(tbl_rwc_test_param.h);
868 			writer_done = 0;
869 			write_type = WRITE_NO_KEY_SHIFT;
870 			if (write_keys(write_type) < 0)
871 				goto err;
872 			for (i = 1; i <= rwc_core_cnt[n]; i++)
873 				rte_eal_remote_launch(test_rwc_reader,
874 						(void *)(uintptr_t)read_type,
875 							enabled_core_ids[i]);
876 			write_type = WRITE_KEY_SHIFT;
877 			ret = write_keys(write_type);
878 			writer_done = 1;
879 
880 			if (ret < 0)
881 				goto err;
882 			for (i = 1; i <= rwc_core_cnt[n]; i++)
883 				if (rte_eal_wait_lcore(enabled_core_ids[i]) < 0)
884 					goto err;
885 
886 			unsigned long long cycles_per_lookup =
887 				__atomic_load_n(&gread_cycles, __ATOMIC_RELAXED)
888 				/ __atomic_load_n(&greads, __ATOMIC_RELAXED);
889 			rwc_perf_results->w_ks_r_hit_nsp[m][n]
890 						= cycles_per_lookup;
891 			printf("Cycles per lookup: %llu\n", cycles_per_lookup);
892 		}
893 	}
894 
895 finish:
896 	rte_hash_free(tbl_rwc_test_param.h);
897 	return 0;
898 
899 err:
900 	rte_eal_mp_wait_lcore();
901 	rte_hash_free(tbl_rwc_test_param.h);
902 	return -1;
903 }
904 
905 /*
906  * Test lookup perf:
907  * Reader(s) lookup keys present in the table and likely on the shift-path while
908  * 'Main' thread adds keys causing key-shifts.
909  */
910 static int
911 test_hash_add_ks_lookup_hit_sp(struct rwc_perf *rwc_perf_results, int rwc_lf,
912 				int htm, int ext_bkt)
913 {
914 	unsigned int n, m;
915 	uint64_t i;
916 	int use_jhash = 0;
917 	int ret;
918 	uint8_t write_type;
919 	uint8_t read_type = READ_PASS_SHIFT_PATH;
920 
921 	if (init_params(rwc_lf, use_jhash, htm, ext_bkt) != 0)
922 		goto err;
923 	printf("\nTest: Hash add - key shift, Hash lookup - hit (shift-path)"
924 	       "\n");
925 
926 	for (m = 0; m < 2; m++) {
927 		if (m == 1) {
928 			printf("\n** With bulk-lookup **\n");
929 			read_type |= BULK_LOOKUP;
930 		}
931 		for (n = 0; n < NUM_TEST; n++) {
932 			unsigned int tot_lcore = rte_lcore_count();
933 			if (tot_lcore < rwc_core_cnt[n] + 1)
934 				goto finish;
935 
936 			printf("\nNumber of readers: %u\n", rwc_core_cnt[n]);
937 
938 			__atomic_store_n(&greads, 0, __ATOMIC_RELAXED);
939 			__atomic_store_n(&gread_cycles, 0, __ATOMIC_RELAXED);
940 
941 			rte_hash_reset(tbl_rwc_test_param.h);
942 			writer_done = 0;
943 			write_type = WRITE_NO_KEY_SHIFT;
944 			if (write_keys(write_type) < 0)
945 				goto err;
946 			for (i = 1; i <= rwc_core_cnt[n]; i++)
947 				rte_eal_remote_launch(test_rwc_reader,
948 						(void *)(uintptr_t)read_type,
949 						enabled_core_ids[i]);
950 			write_type = WRITE_KEY_SHIFT;
951 			ret = write_keys(write_type);
952 			writer_done = 1;
953 
954 			if (ret < 0)
955 				goto err;
956 			for (i = 1; i <= rwc_core_cnt[n]; i++)
957 				if (rte_eal_wait_lcore(enabled_core_ids[i]) < 0)
958 					goto err;
959 
960 			unsigned long long cycles_per_lookup =
961 				__atomic_load_n(&gread_cycles, __ATOMIC_RELAXED)
962 				/ __atomic_load_n(&greads, __ATOMIC_RELAXED);
963 			rwc_perf_results->w_ks_r_hit_sp[m][n]
964 						= cycles_per_lookup;
965 			printf("Cycles per lookup: %llu\n", cycles_per_lookup);
966 		}
967 	}
968 
969 finish:
970 	rte_hash_free(tbl_rwc_test_param.h);
971 	return 0;
972 
973 err:
974 	rte_eal_mp_wait_lcore();
975 	rte_hash_free(tbl_rwc_test_param.h);
976 	return -1;
977 }
978 
979 /*
980  * Test lookup perf:
981  * Reader(s) lookup keys absent in the table while
982  * 'Main' thread adds keys causing key-shifts.
983  */
984 static int
985 test_hash_add_ks_lookup_miss(struct rwc_perf *rwc_perf_results, int rwc_lf, int
986 			     htm, int ext_bkt)
987 {
988 	unsigned int n, m;
989 	uint64_t i;
990 	int use_jhash = 0;
991 	int ret;
992 	uint8_t write_type;
993 	uint8_t read_type = READ_FAIL;
994 
995 	if (init_params(rwc_lf, use_jhash, htm, ext_bkt) != 0)
996 		goto err;
997 	printf("\nTest: Hash add - key shift, Hash lookup - miss\n");
998 	for (m = 0; m < 2; m++) {
999 		if (m == 1) {
1000 			printf("\n** With bulk-lookup **\n");
1001 			read_type |= BULK_LOOKUP;
1002 		}
1003 		for (n = 0; n < NUM_TEST; n++) {
1004 			unsigned int tot_lcore = rte_lcore_count();
1005 			if (tot_lcore < rwc_core_cnt[n] + 1)
1006 				goto finish;
1007 
1008 			printf("\nNumber of readers: %u\n", rwc_core_cnt[n]);
1009 
1010 			__atomic_store_n(&greads, 0, __ATOMIC_RELAXED);
1011 			__atomic_store_n(&gread_cycles, 0, __ATOMIC_RELAXED);
1012 
1013 			rte_hash_reset(tbl_rwc_test_param.h);
1014 			writer_done = 0;
1015 			write_type = WRITE_NO_KEY_SHIFT;
1016 			if (write_keys(write_type) < 0)
1017 				goto err;
1018 			for (i = 1; i <= rwc_core_cnt[n]; i++)
1019 				rte_eal_remote_launch(test_rwc_reader,
1020 						(void *)(uintptr_t)read_type,
1021 							enabled_core_ids[i]);
1022 			write_type = WRITE_KEY_SHIFT;
1023 			ret = write_keys(write_type);
1024 			writer_done = 1;
1025 
1026 			if (ret < 0)
1027 				goto err;
1028 			for (i = 1; i <= rwc_core_cnt[n]; i++)
1029 				if (rte_eal_wait_lcore(enabled_core_ids[i]) < 0)
1030 					goto err;
1031 
1032 			unsigned long long cycles_per_lookup =
1033 				__atomic_load_n(&gread_cycles, __ATOMIC_RELAXED)
1034 				/ __atomic_load_n(&greads, __ATOMIC_RELAXED);
1035 			rwc_perf_results->w_ks_r_miss[m][n] = cycles_per_lookup;
1036 			printf("Cycles per lookup: %llu\n", cycles_per_lookup);
1037 		}
1038 	}
1039 
1040 finish:
1041 	rte_hash_free(tbl_rwc_test_param.h);
1042 	return 0;
1043 
1044 err:
1045 	rte_eal_mp_wait_lcore();
1046 	rte_hash_free(tbl_rwc_test_param.h);
1047 	return -1;
1048 }
1049 
1050 /*
1051  * Test lookup perf for multi-writer:
1052  * Reader(s) lookup keys present in the table and likely on the shift-path while
1053  * Writers add keys causing key-shiftsi.
1054  * Writers are running in parallel, on different data plane cores.
1055  */
1056 static int
1057 test_hash_multi_add_lookup(struct rwc_perf *rwc_perf_results, int rwc_lf,
1058 			   int htm, int ext_bkt)
1059 {
1060 	unsigned int n, m, k;
1061 	uint64_t i;
1062 	int use_jhash = 0;
1063 	uint8_t write_type;
1064 	uint8_t read_type = READ_PASS_SHIFT_PATH;
1065 
1066 	if (init_params(rwc_lf, use_jhash, htm, ext_bkt) != 0)
1067 		goto err;
1068 	printf("\nTest: Multi-add-lookup\n");
1069 	uint8_t pos_core;
1070 	for (m = 1; m < NUM_TEST; m++) {
1071 		/* Calculate keys added by each writer */
1072 		tbl_rwc_test_param.single_insert =
1073 			tbl_rwc_test_param.count_keys_ks / rwc_core_cnt[m];
1074 		for (k = 0; k < 2; k++) {
1075 			if (k == 1) {
1076 				printf("\n** With bulk-lookup **\n");
1077 				read_type |= BULK_LOOKUP;
1078 			}
1079 			for (n = 0; n < NUM_TEST; n++) {
1080 				unsigned int tot_lcore	= rte_lcore_count();
1081 				if (tot_lcore < (rwc_core_cnt[n] +
1082 				     rwc_core_cnt[m] + 1))
1083 					goto finish;
1084 
1085 				printf("\nNumber of writers: %u",
1086 				       rwc_core_cnt[m]);
1087 				printf("\nNumber of readers: %u\n",
1088 				       rwc_core_cnt[n]);
1089 
1090 				__atomic_store_n(&greads, 0, __ATOMIC_RELAXED);
1091 				__atomic_store_n(&gread_cycles, 0,
1092 						 __ATOMIC_RELAXED);
1093 
1094 				rte_hash_reset(tbl_rwc_test_param.h);
1095 				writer_done = 0;
1096 				write_type = WRITE_NO_KEY_SHIFT;
1097 				if (write_keys(write_type) < 0)
1098 					goto err;
1099 
1100 				/* Launch reader(s) */
1101 				for (i = 1; i <= rwc_core_cnt[n]; i++)
1102 					rte_eal_remote_launch(test_rwc_reader,
1103 						(void *)(uintptr_t)read_type,
1104 						enabled_core_ids[i]);
1105 				write_type = WRITE_KEY_SHIFT;
1106 				pos_core = 0;
1107 
1108 				/* Launch writers */
1109 				for (; i <= rwc_core_cnt[m]
1110 				     + rwc_core_cnt[n];	i++) {
1111 					rte_eal_remote_launch
1112 						(test_rwc_multi_writer,
1113 						(void *)(uintptr_t)pos_core,
1114 						enabled_core_ids[i]);
1115 					pos_core++;
1116 				}
1117 
1118 				/* Wait for writers to complete */
1119 				for (i = rwc_core_cnt[n] + 1;
1120 				     i <= rwc_core_cnt[m] + rwc_core_cnt[n];
1121 				     i++)
1122 					rte_eal_wait_lcore(enabled_core_ids[i]);
1123 
1124 				writer_done = 1;
1125 
1126 				for (i = 1; i <= rwc_core_cnt[n]; i++)
1127 					if (rte_eal_wait_lcore(enabled_core_ids[i]) < 0)
1128 						goto err;
1129 
1130 				unsigned long long cycles_per_lookup =
1131 					__atomic_load_n(&gread_cycles,
1132 							__ATOMIC_RELAXED) /
1133 					__atomic_load_n(&greads,
1134 							  __ATOMIC_RELAXED);
1135 				rwc_perf_results->multi_rw[m][k][n]
1136 					= cycles_per_lookup;
1137 				printf("Cycles per lookup: %llu\n",
1138 				       cycles_per_lookup);
1139 			}
1140 		}
1141 	}
1142 
1143 finish:
1144 	rte_hash_free(tbl_rwc_test_param.h);
1145 	return 0;
1146 
1147 err:
1148 	rte_eal_mp_wait_lcore();
1149 	rte_hash_free(tbl_rwc_test_param.h);
1150 	return -1;
1151 }
1152 
1153 /*
1154  * Test lookup perf:
1155  * Reader(s) lookup keys present in the extendable bkt.
1156  */
1157 static int
1158 test_hash_add_ks_lookup_hit_extbkt(struct rwc_perf *rwc_perf_results,
1159 				int rwc_lf, int htm, int ext_bkt)
1160 {
1161 	unsigned int n, m;
1162 	uint64_t i;
1163 	int use_jhash = 0;
1164 	uint8_t write_type;
1165 	uint8_t read_type = READ_PASS_KEY_SHIFTS_EXTBKT;
1166 
1167 	if (init_params(rwc_lf, use_jhash, htm, ext_bkt) != 0)
1168 		goto err;
1169 	printf("\nTest: Hash add - key-shifts, read - hit (ext_bkt)\n");
1170 	for (m = 0; m < 2; m++) {
1171 		if (m == 1) {
1172 			printf("\n** With bulk-lookup **\n");
1173 			read_type |= BULK_LOOKUP;
1174 		}
1175 		for (n = 0; n < NUM_TEST; n++) {
1176 			unsigned int tot_lcore = rte_lcore_count();
1177 			if (tot_lcore < rwc_core_cnt[n] + 1)
1178 				goto finish;
1179 
1180 			printf("\nNumber of readers: %u\n", rwc_core_cnt[n]);
1181 
1182 			__atomic_store_n(&greads, 0, __ATOMIC_RELAXED);
1183 			__atomic_store_n(&gread_cycles, 0, __ATOMIC_RELAXED);
1184 
1185 			rte_hash_reset(tbl_rwc_test_param.h);
1186 			write_type = WRITE_NO_KEY_SHIFT;
1187 			if (write_keys(write_type) < 0)
1188 				goto err;
1189 			write_type = WRITE_KEY_SHIFT;
1190 			if (write_keys(write_type) < 0)
1191 				goto err;
1192 			writer_done = 0;
1193 			for (i = 1; i <= rwc_core_cnt[n]; i++)
1194 				rte_eal_remote_launch(test_rwc_reader,
1195 						(void *)(uintptr_t)read_type,
1196 							enabled_core_ids[i]);
1197 			for (i = 0; i < tbl_rwc_test_param.count_keys_ks_extbkt;
1198 			     i++) {
1199 				if (rte_hash_del_key(tbl_rwc_test_param.h,
1200 					tbl_rwc_test_param.keys_ks_extbkt + i)
1201 							< 0) {
1202 					printf("Delete Failed: %u\n",
1203 					tbl_rwc_test_param.keys_ks_extbkt[i]);
1204 					goto err;
1205 				}
1206 			}
1207 			writer_done = 1;
1208 
1209 			for (i = 1; i <= rwc_core_cnt[n]; i++)
1210 				if (rte_eal_wait_lcore(enabled_core_ids[i]) < 0)
1211 					goto err;
1212 
1213 			unsigned long long cycles_per_lookup =
1214 				__atomic_load_n(&gread_cycles, __ATOMIC_RELAXED)
1215 				/ __atomic_load_n(&greads, __ATOMIC_RELAXED);
1216 			rwc_perf_results->w_ks_r_hit_extbkt[m][n]
1217 						= cycles_per_lookup;
1218 			printf("Cycles per lookup: %llu\n", cycles_per_lookup);
1219 		}
1220 	}
1221 
1222 finish:
1223 	rte_hash_free(tbl_rwc_test_param.h);
1224 	return 0;
1225 
1226 err:
1227 	rte_eal_mp_wait_lcore();
1228 	rte_hash_free(tbl_rwc_test_param.h);
1229 	return -1;
1230 }
1231 
1232 static struct rte_rcu_qsbr *rv;
1233 
1234 /*
1235  * Reader thread using rte_hash data structure with RCU
1236  */
1237 static int
1238 test_hash_rcu_qsbr_reader(void *arg)
1239 {
1240 	unsigned int i, j;
1241 	uint32_t num_keys = tbl_rwc_test_param.count_keys_no_ks
1242 				- QSBR_REPORTING_INTERVAL;
1243 	uint32_t *keys = tbl_rwc_test_param.keys_no_ks;
1244 	uint32_t lcore_id = rte_lcore_id();
1245 	RTE_SET_USED(arg);
1246 
1247 	(void)rte_rcu_qsbr_thread_register(rv, lcore_id);
1248 	rte_rcu_qsbr_thread_online(rv, lcore_id);
1249 	do {
1250 		for (i = 0; i < num_keys; i += j) {
1251 			for (j = 0; j < QSBR_REPORTING_INTERVAL; j++)
1252 				rte_hash_lookup(tbl_rwc_test_param.h,
1253 						keys + i + j);
1254 			/* Update quiescent state counter */
1255 			rte_rcu_qsbr_quiescent(rv, lcore_id);
1256 		}
1257 	} while (!writer_done);
1258 	rte_rcu_qsbr_thread_offline(rv, lcore_id);
1259 	(void)rte_rcu_qsbr_thread_unregister(rv, lcore_id);
1260 
1261 	return 0;
1262 }
1263 
1264 /*
1265  * Writer thread using rte_hash data structure with RCU
1266  */
1267 static int
1268 test_hash_rcu_qsbr_writer(void *arg)
1269 {
1270 	uint32_t i, offset;
1271 	uint64_t begin, cycles;
1272 	uint8_t pos_core = (uint32_t)((uintptr_t)arg);
1273 	offset = pos_core * tbl_rwc_test_param.single_insert;
1274 
1275 	begin = rte_rdtsc_precise();
1276 	for (i = offset; i < offset + tbl_rwc_test_param.single_insert; i++) {
1277 		/* Delete element from the shared data structure */
1278 		rte_hash_del_key(tbl_rwc_test_param.h,
1279 					tbl_rwc_test_param.keys_no_ks + i);
1280 		rte_hash_add_key(tbl_rwc_test_param.h,
1281 				tbl_rwc_test_param.keys_no_ks + i);
1282 	}
1283 	cycles = rte_rdtsc_precise() - begin;
1284 	__atomic_fetch_add(&gwrite_cycles, cycles, __ATOMIC_RELAXED);
1285 	__atomic_fetch_add(&gwrites, tbl_rwc_test_param.single_insert,
1286 			   __ATOMIC_RELAXED);
1287 	return 0;
1288 }
1289 
1290 /*
1291  * Writer perf test with RCU QSBR in DQ mode:
1292  * Writer(s) delete and add keys in the table.
1293  * Readers lookup keys in the hash table
1294  */
1295 static int
1296 test_hash_rcu_qsbr_writer_perf(struct rwc_perf *rwc_perf_results, int rwc_lf,
1297 				int htm, int ext_bkt)
1298 {
1299 	unsigned int n;
1300 	uint64_t i;
1301 	uint8_t write_type;
1302 	int use_jhash = 0;
1303 	struct rte_hash_rcu_config rcu_config = {0};
1304 	uint32_t sz;
1305 	uint8_t pos_core;
1306 
1307 	printf("\nTest: Writer perf with integrated RCU\n");
1308 
1309 	if (init_params(rwc_lf, use_jhash, htm, ext_bkt) != 0)
1310 		goto err;
1311 
1312 	sz = rte_rcu_qsbr_get_memsize(RTE_MAX_LCORE);
1313 	rv = (struct rte_rcu_qsbr *)rte_zmalloc(NULL, sz, RTE_CACHE_LINE_SIZE);
1314 	rcu_config.v = rv;
1315 
1316 	if (rte_hash_rcu_qsbr_add(tbl_rwc_test_param.h, &rcu_config) < 0) {
1317 		printf("RCU init in hash failed\n");
1318 		goto err;
1319 	}
1320 
1321 	for (n = 0; n < NUM_TEST; n++) {
1322 		unsigned int tot_lcore = rte_lcore_count();
1323 		if (tot_lcore < rwc_core_cnt[n] + 3)
1324 			goto finish;
1325 
1326 		/* Calculate keys added by each writer */
1327 		tbl_rwc_test_param.single_insert =
1328 			tbl_rwc_test_param.count_keys_no_ks /
1329 				rwc_core_cnt[n];
1330 		printf("\nNumber of writers: %u\n", rwc_core_cnt[n]);
1331 
1332 		__atomic_store_n(&gwrites, 0, __ATOMIC_RELAXED);
1333 		__atomic_store_n(&gwrite_cycles, 0, __ATOMIC_RELAXED);
1334 
1335 		rte_hash_reset(tbl_rwc_test_param.h);
1336 		rte_rcu_qsbr_init(rv, RTE_MAX_LCORE);
1337 
1338 		write_type = WRITE_NO_KEY_SHIFT;
1339 		if (write_keys(write_type) < 0)
1340 			goto err;
1341 		write_type = WRITE_KEY_SHIFT;
1342 		if (write_keys(write_type) < 0)
1343 			goto err;
1344 
1345 		/* Launch 2 readers */
1346 		for (i = 1; i <= 2; i++)
1347 			rte_eal_remote_launch(test_hash_rcu_qsbr_reader, NULL,
1348 					      enabled_core_ids[i]);
1349 		pos_core = 0;
1350 		/* Launch writer(s) */
1351 		for (; i <= rwc_core_cnt[n] + 2; i++) {
1352 			rte_eal_remote_launch(test_hash_rcu_qsbr_writer,
1353 				(void *)(uintptr_t)pos_core,
1354 				enabled_core_ids[i]);
1355 			pos_core++;
1356 		}
1357 
1358 		/* Wait for writers to complete */
1359 		for (i = 3; i <= rwc_core_cnt[n] + 2; i++)
1360 			rte_eal_wait_lcore(enabled_core_ids[i]);
1361 
1362 		writer_done = 1;
1363 
1364 		/* Wait for readers to complete */
1365 		rte_eal_mp_wait_lcore();
1366 
1367 		unsigned long long cycles_per_write_operation =
1368 			__atomic_load_n(&gwrite_cycles, __ATOMIC_RELAXED) /
1369 			__atomic_load_n(&gwrites, __ATOMIC_RELAXED);
1370 		rwc_perf_results->writer_add_del[n]
1371 					= cycles_per_write_operation;
1372 		printf("Cycles per write operation: %llu\n",
1373 				cycles_per_write_operation);
1374 	}
1375 
1376 finish:
1377 	rte_hash_free(tbl_rwc_test_param.h);
1378 	rte_free(rv);
1379 	return 0;
1380 
1381 err:
1382 	writer_done = 1;
1383 	rte_eal_mp_wait_lcore();
1384 	rte_hash_free(tbl_rwc_test_param.h);
1385 	rte_free(rv);
1386 	return -1;
1387 }
1388 
1389 static int
1390 test_hash_readwrite_lf_perf_main(void)
1391 {
1392 	/*
1393 	 * Variables used to choose different tests.
1394 	 * rwc_lf indicates if read-write concurrency lock-free support is
1395 	 * enabled.
1396 	 * htm indicates if Hardware transactional memory support is enabled.
1397 	 */
1398 	int rwc_lf = 0;
1399 	int htm;
1400 	int ext_bkt = 0;
1401 
1402 	if (rte_lcore_count() < 2) {
1403 		printf("Not enough cores for hash_readwrite_lf_perf_autotest, expecting at least 2\n");
1404 		return TEST_SKIPPED;
1405 	}
1406 
1407 	setlocale(LC_NUMERIC, "");
1408 
1409 	/* Reset tbl_rwc_test_param to discard values from previous run */
1410 	memset(&tbl_rwc_test_param, 0, sizeof(tbl_rwc_test_param));
1411 
1412 	if (rte_tm_supported())
1413 		htm = 1;
1414 	else
1415 		htm = 0;
1416 
1417 	if (generate_keys() != 0)
1418 		return -1;
1419 	if (get_enabled_cores_list() != 0)
1420 		return -1;
1421 
1422 	if (RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF) {
1423 		rwc_lf = 1;
1424 		ext_bkt = 1;
1425 		printf("Test lookup with read-write concurrency lock free support"
1426 		       " enabled\n");
1427 		if (test_hash_add_no_ks_lookup_hit(&rwc_lf_results, rwc_lf,
1428 							htm, ext_bkt) < 0)
1429 			return -1;
1430 		if (test_hash_add_no_ks_lookup_miss(&rwc_lf_results, rwc_lf,
1431 							htm, ext_bkt) < 0)
1432 			return -1;
1433 		if (test_hash_add_ks_lookup_hit_non_sp(&rwc_lf_results, rwc_lf,
1434 							htm, ext_bkt) < 0)
1435 			return -1;
1436 		if (test_hash_add_ks_lookup_hit_sp(&rwc_lf_results, rwc_lf,
1437 							htm, ext_bkt) < 0)
1438 			return -1;
1439 		if (test_hash_add_ks_lookup_miss(&rwc_lf_results, rwc_lf, htm,
1440 						 ext_bkt) < 0)
1441 			return -1;
1442 		if (test_hash_multi_add_lookup(&rwc_lf_results, rwc_lf, htm,
1443 					       ext_bkt) < 0)
1444 			return -1;
1445 		if (test_hash_add_ks_lookup_hit_extbkt(&rwc_lf_results, rwc_lf,
1446 							htm, ext_bkt) < 0)
1447 			return -1;
1448 		if (test_hash_rcu_qsbr_writer_perf(&rwc_lf_results, rwc_lf,
1449 						   htm, ext_bkt) < 0)
1450 			return -1;
1451 	}
1452 	printf("\nTest lookup with read-write concurrency lock free support"
1453 	       " disabled\n");
1454 	rwc_lf = 0;
1455 	if (!htm) {
1456 		printf("With HTM Disabled\n");
1457 		if (!RUN_WITH_HTM_DISABLED) {
1458 			printf("Enable RUN_WITH_HTM_DISABLED to test with"
1459 			       " lock-free disabled");
1460 			goto results;
1461 		}
1462 	} else
1463 		printf("With HTM Enabled\n");
1464 	if (test_hash_add_no_ks_lookup_hit(&rwc_non_lf_results, rwc_lf, htm,
1465 					   ext_bkt) < 0)
1466 		return -1;
1467 	if (test_hash_add_no_ks_lookup_miss(&rwc_non_lf_results, rwc_lf, htm,
1468 						ext_bkt) < 0)
1469 		return -1;
1470 	if (test_hash_add_ks_lookup_hit_non_sp(&rwc_non_lf_results, rwc_lf,
1471 						htm, ext_bkt) < 0)
1472 		return -1;
1473 	if (test_hash_add_ks_lookup_hit_sp(&rwc_non_lf_results, rwc_lf, htm,
1474 						ext_bkt) < 0)
1475 		return -1;
1476 	if (test_hash_add_ks_lookup_miss(&rwc_non_lf_results, rwc_lf, htm,
1477 					 ext_bkt) < 0)
1478 		return -1;
1479 	if (test_hash_multi_add_lookup(&rwc_non_lf_results, rwc_lf, htm,
1480 							ext_bkt) < 0)
1481 		return -1;
1482 	if (test_hash_add_ks_lookup_hit_extbkt(&rwc_non_lf_results, rwc_lf,
1483 						htm, ext_bkt) < 0)
1484 		return -1;
1485 results:
1486 	printf("\n\t\t\t\t\t\t********** Results summary **********\n\n");
1487 	int i, j, k;
1488 	for (j = 0; j < 2; j++) {
1489 		if (j == 1)
1490 			printf("\n\t\t\t\t\t#######********** Bulk Lookup "
1491 			       "**********#######\n\n");
1492 		printf("_______\t\t_______\t\t_________\t___\t\t_________\t\t"
1493 			"\t\t\t\t_________________\n");
1494 		printf("Writers\t\tReaders\t\tLock-free\tHTM\t\tTest-case\t\t\t"
1495 		       "\t\t\tCycles per lookup\n");
1496 		printf("_______\t\t_______\t\t_________\t___\t\t_________\t\t\t"
1497 		       "\t\t\t_________________\n");
1498 		for (i = 0; i < NUM_TEST; i++) {
1499 			printf("%u\t\t%u\t\t", 1, rwc_core_cnt[i]);
1500 			printf("Enabled\t\t");
1501 			printf("N/A\t\t");
1502 			printf("Hash add - no key-shifts, lookup - hit\t\t\t\t"
1503 				"%u\n\t\t\t\t\t\t\t\t",
1504 				rwc_lf_results.w_no_ks_r_hit[j][i]);
1505 			printf("Hash add - no key-shifts, lookup - miss\t\t\t\t"
1506 				"%u\n\t\t\t\t\t\t\t\t",
1507 				rwc_lf_results.w_no_ks_r_miss[j][i]);
1508 			printf("Hash add - key-shifts, lookup - hit"
1509 			       "(non-shift-path)\t\t%u\n\t\t\t\t\t\t\t\t",
1510 			       rwc_lf_results.w_ks_r_hit_nsp[j][i]);
1511 			printf("Hash add - key-shifts, lookup - hit "
1512 			       "(shift-path)\t\t%u\n\t\t\t\t\t\t\t\t",
1513 			       rwc_lf_results.w_ks_r_hit_sp[j][i]);
1514 			printf("Hash add - key-shifts, Hash lookup miss\t\t\t\t"
1515 				"%u\n\t\t\t\t\t\t\t\t",
1516 				rwc_lf_results.w_ks_r_miss[j][i]);
1517 			printf("Hash add - key-shifts, Hash lookup hit (ext_bkt)\t\t"
1518 				"%u\n\n\t\t\t\t",
1519 				rwc_lf_results.w_ks_r_hit_extbkt[j][i]);
1520 
1521 			printf("Disabled\t");
1522 			if (htm)
1523 				printf("Enabled\t\t");
1524 			else
1525 				printf("Disabled\t");
1526 			printf("Hash add - no key-shifts, lookup - hit\t\t\t\t"
1527 				"%u\n\t\t\t\t\t\t\t\t",
1528 				rwc_non_lf_results.w_no_ks_r_hit[j][i]);
1529 			printf("Hash add - no key-shifts, lookup - miss\t\t\t\t"
1530 				"%u\n\t\t\t\t\t\t\t\t",
1531 				rwc_non_lf_results.w_no_ks_r_miss[j][i]);
1532 			printf("Hash add - key-shifts, lookup - hit "
1533 			       "(non-shift-path)\t\t%u\n\t\t\t\t\t\t\t\t",
1534 			       rwc_non_lf_results.w_ks_r_hit_nsp[j][i]);
1535 			printf("Hash add - key-shifts, lookup - hit "
1536 			       "(shift-path)\t\t%u\n\t\t\t\t\t\t\t\t",
1537 			       rwc_non_lf_results.w_ks_r_hit_sp[j][i]);
1538 			printf("Hash add - key-shifts, Hash lookup miss\t\t\t\t"
1539 			       "%u\n\t\t\t\t\t\t\t\t",
1540 			       rwc_non_lf_results.w_ks_r_miss[j][i]);
1541 			printf("Hash add - key-shifts, Hash lookup hit (ext_bkt)\t\t"
1542 				"%u\n",
1543 				rwc_non_lf_results.w_ks_r_hit_extbkt[j][i]);
1544 
1545 			printf("_______\t\t_______\t\t_________\t___\t\t"
1546 			       "_________\t\t\t\t\t\t_________________\n");
1547 		}
1548 
1549 		for (i = 1; i < NUM_TEST; i++) {
1550 			for (k = 0; k < NUM_TEST; k++) {
1551 				printf("%u", rwc_core_cnt[i]);
1552 				printf("\t\t%u\t\t", rwc_core_cnt[k]);
1553 				printf("Enabled\t\t");
1554 				printf("N/A\t\t");
1555 				printf("Multi-add-lookup\t\t\t\t\t\t%u\n\n\t\t"
1556 				       "\t\t",
1557 				       rwc_lf_results.multi_rw[i][j][k]);
1558 				printf("Disabled\t");
1559 				if (htm)
1560 					printf("Enabled\t\t");
1561 				else
1562 					printf("Disabled\t");
1563 				printf("Multi-add-lookup\t\t\t\t\t\t%u\n",
1564 				       rwc_non_lf_results.multi_rw[i][j][k]);
1565 
1566 				printf("_______\t\t_______\t\t_________\t___"
1567 				       "\t\t_________\t\t\t\t\t\t"
1568 				       "_________________\n");
1569 			}
1570 		}
1571 	}
1572 	rte_free(tbl_rwc_test_param.keys);
1573 	rte_free(tbl_rwc_test_param.keys_no_ks);
1574 	rte_free(tbl_rwc_test_param.keys_ks);
1575 	rte_free(tbl_rwc_test_param.keys_absent);
1576 	rte_free(tbl_rwc_test_param.keys_shift_path);
1577 	rte_free(tbl_rwc_test_param.keys_non_shift_path);
1578 	rte_free(tbl_rwc_test_param.keys_ext_bkt);
1579 	rte_free(tbl_rwc_test_param.keys_ks_extbkt);
1580 	return 0;
1581 }
1582 
1583 REGISTER_TEST_COMMAND(hash_readwrite_lf_perf_autotest,
1584 	test_hash_readwrite_lf_perf_main);
1585