test_hash.c revision 5d4e5dcd
1/*-
2 *   BSD LICENSE
3 *
4 *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
5 *   All rights reserved.
6 *
7 *   Redistribution and use in source and binary forms, with or without
8 *   modification, are permitted provided that the following conditions
9 *   are met:
10 *
11 *     * Redistributions of source code must retain the above copyright
12 *       notice, this list of conditions and the following disclaimer.
13 *     * Redistributions in binary form must reproduce the above copyright
14 *       notice, this list of conditions and the following disclaimer in
15 *       the documentation and/or other materials provided with the
16 *       distribution.
17 *     * Neither the name of Intel Corporation nor the names of its
18 *       contributors may be used to endorse or promote products derived
19 *       from this software without specific prior written permission.
20 *
21 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <stdio.h>
35#include <stdint.h>
36#include <string.h>
37#include <stdlib.h>
38#include <stdarg.h>
39#include <errno.h>
40#include <sys/queue.h>
41
42#include <rte_common.h>
43#include <rte_malloc.h>
44#include <rte_cycles.h>
45#include <rte_random.h>
46#include <rte_memory.h>
47#include <rte_memzone.h>
48#include <rte_eal.h>
49#include <rte_ip.h>
50#include <rte_string_fns.h>
51
52#include "test.h"
53
54#include <rte_hash.h>
55#include <rte_fbk_hash.h>
56#include <rte_jhash.h>
57#include <rte_hash_crc.h>
58
59/*******************************************************************************
60 * Hash function performance test configuration section. Each performance test
61 * will be performed HASHTEST_ITERATIONS times.
62 *
63 * The five arrays below control what tests are performed. Every combination
64 * from the array entries is tested.
65 */
66static rte_hash_function hashtest_funcs[] = {rte_jhash, rte_hash_crc};
67static uint32_t hashtest_initvals[] = {0};
68static uint32_t hashtest_key_lens[] = {0, 2, 4, 5, 6, 7, 8, 10, 11, 15, 16, 21, 31, 32, 33, 63, 64};
69#define MAX_KEYSIZE 64
70/******************************************************************************/
71#define LOCAL_FBK_HASH_ENTRIES_MAX (1 << 15)
72
73/*
74 * Check condition and return an error if true. Assumes that "handle" is the
75 * name of the hash structure pointer to be freed.
76 */
77#define RETURN_IF_ERROR(cond, str, ...) do {				\
78	if (cond) {							\
79		printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \
80		if (handle) rte_hash_free(handle);			\
81		return -1;						\
82	}								\
83} while(0)
84
85#define RETURN_IF_ERROR_FBK(cond, str, ...) do {				\
86	if (cond) {							\
87		printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \
88		if (handle) rte_fbk_hash_free(handle);			\
89		return -1;						\
90	}								\
91} while(0)
92
93/* 5-tuple key type */
94struct flow_key {
95	uint32_t ip_src;
96	uint32_t ip_dst;
97	uint16_t port_src;
98	uint16_t port_dst;
99	uint8_t proto;
100} __attribute__((packed));
101
102/*
103 * Hash function that always returns the same value, to easily test what
104 * happens when a bucket is full.
105 */
106static uint32_t pseudo_hash(__attribute__((unused)) const void *keys,
107			    __attribute__((unused)) uint32_t key_len,
108			    __attribute__((unused)) uint32_t init_val)
109{
110	return 3;
111}
112
113/*
114 * Print out result of unit test hash operation.
115 */
116#if defined(UNIT_TEST_HASH_VERBOSE)
117static void print_key_info(const char *msg, const struct flow_key *key,
118								int32_t pos)
119{
120	uint8_t *p = (uint8_t *)key;
121	unsigned i;
122
123	printf("%s key:0x", msg);
124	for (i = 0; i < sizeof(struct flow_key); i++) {
125		printf("%02X", p[i]);
126	}
127	printf(" @ pos %d\n", pos);
128}
129#else
130static void print_key_info(__attribute__((unused)) const char *msg,
131		__attribute__((unused)) const struct flow_key *key,
132		__attribute__((unused)) int32_t pos)
133{
134}
135#endif
136
137/* Keys used by unit test functions */
138static struct flow_key keys[5] = { {
139	.ip_src = IPv4(0x03, 0x02, 0x01, 0x00),
140	.ip_dst = IPv4(0x07, 0x06, 0x05, 0x04),
141	.port_src = 0x0908,
142	.port_dst = 0x0b0a,
143	.proto = 0x0c,
144}, {
145	.ip_src = IPv4(0x13, 0x12, 0x11, 0x10),
146	.ip_dst = IPv4(0x17, 0x16, 0x15, 0x14),
147	.port_src = 0x1918,
148	.port_dst = 0x1b1a,
149	.proto = 0x1c,
150}, {
151	.ip_src = IPv4(0x23, 0x22, 0x21, 0x20),
152	.ip_dst = IPv4(0x27, 0x26, 0x25, 0x24),
153	.port_src = 0x2928,
154	.port_dst = 0x2b2a,
155	.proto = 0x2c,
156}, {
157	.ip_src = IPv4(0x33, 0x32, 0x31, 0x30),
158	.ip_dst = IPv4(0x37, 0x36, 0x35, 0x34),
159	.port_src = 0x3938,
160	.port_dst = 0x3b3a,
161	.proto = 0x3c,
162}, {
163	.ip_src = IPv4(0x43, 0x42, 0x41, 0x40),
164	.ip_dst = IPv4(0x47, 0x46, 0x45, 0x44),
165	.port_src = 0x4948,
166	.port_dst = 0x4b4a,
167	.proto = 0x4c,
168} };
169
170/* Parameters used for hash table in unit test functions. Name set later. */
171static struct rte_hash_parameters ut_params = {
172	.entries = 64,
173	.key_len = sizeof(struct flow_key), /* 13 */
174	.hash_func = rte_jhash,
175	.hash_func_init_val = 0,
176	.socket_id = 0,
177};
178
179#define CRC32_ITERATIONS (1U << 10)
180#define CRC32_DWORDS (1U << 6)
181/*
182 * Test if all CRC32 implementations yield the same hash value
183 */
184static int
185test_crc32_hash_alg_equiv(void)
186{
187	uint32_t hash_val;
188	uint32_t init_val;
189	uint64_t data64[CRC32_DWORDS];
190	unsigned i, j;
191	size_t data_len;
192
193	printf("\n# CRC32 implementations equivalence test\n");
194	for (i = 0; i < CRC32_ITERATIONS; i++) {
195		/* Randomizing data_len of data set */
196		data_len = (size_t) ((rte_rand() % sizeof(data64)) + 1);
197		init_val = (uint32_t) rte_rand();
198
199		/* Fill the data set */
200		for (j = 0; j < CRC32_DWORDS; j++)
201			data64[j] = rte_rand();
202
203		/* Calculate software CRC32 */
204		rte_hash_crc_set_alg(CRC32_SW);
205		hash_val = rte_hash_crc(data64, data_len, init_val);
206
207		/* Check against 4-byte-operand sse4.2 CRC32 if available */
208		rte_hash_crc_set_alg(CRC32_SSE42);
209		if (hash_val != rte_hash_crc(data64, data_len, init_val)) {
210			printf("Failed checking CRC32_SW against CRC32_SSE42\n");
211			break;
212		}
213
214		/* Check against 8-byte-operand sse4.2 CRC32 if available */
215		rte_hash_crc_set_alg(CRC32_SSE42_x64);
216		if (hash_val != rte_hash_crc(data64, data_len, init_val)) {
217			printf("Failed checking CRC32_SW against CRC32_SSE42_x64\n");
218			break;
219		}
220
221		/* Check against 8-byte-operand ARM64 CRC32 if available */
222		rte_hash_crc_set_alg(CRC32_ARM64);
223		if (hash_val != rte_hash_crc(data64, data_len, init_val)) {
224			printf("Failed checking CRC32_SW against CRC32_ARM64\n");
225			break;
226		}
227	}
228
229	/* Resetting to best available algorithm */
230	rte_hash_crc_set_alg(CRC32_SSE42_x64);
231
232	if (i == CRC32_ITERATIONS)
233		return 0;
234
235	printf("Failed test data (hex, %zu bytes total):\n", data_len);
236	for (j = 0; j < data_len; j++)
237		printf("%02X%c", ((uint8_t *)data64)[j],
238				((j+1) % 16 == 0 || j == data_len - 1) ? '\n' : ' ');
239
240	return -1;
241}
242
243/*
244 * Test a hash function.
245 */
246static void run_hash_func_test(rte_hash_function f, uint32_t init_val,
247		uint32_t key_len)
248{
249	static uint8_t key[MAX_KEYSIZE];
250	unsigned i;
251
252
253	for (i = 0; i < key_len; i++)
254		key[i] = (uint8_t) rte_rand();
255
256	/* just to be on the safe side */
257	if (!f)
258		return;
259
260	f(key, key_len, init_val);
261}
262
263/*
264 * Test all hash functions.
265 */
266static void run_hash_func_tests(void)
267{
268	unsigned i, j, k;
269
270	for (i = 0;
271	     i < sizeof(hashtest_funcs) / sizeof(rte_hash_function);
272	     i++) {
273		for (j = 0;
274		     j < sizeof(hashtest_initvals) / sizeof(uint32_t);
275		     j++) {
276			for (k = 0;
277			     k < sizeof(hashtest_key_lens) / sizeof(uint32_t);
278			     k++) {
279				run_hash_func_test(hashtest_funcs[i],
280						hashtest_initvals[j],
281						hashtest_key_lens[k]);
282			}
283		}
284	}
285}
286
287/*
288 * Basic sequence of operations for a single key:
289 *	- add
290 *	- lookup (hit)
291 *	- delete
292 *	- lookup (miss)
293 */
294static int test_add_delete(void)
295{
296	struct rte_hash *handle;
297	/* test with standard add/lookup/delete functions */
298	int pos0, expectedPos0;
299
300	ut_params.name = "test1";
301	handle = rte_hash_create(&ut_params);
302	RETURN_IF_ERROR(handle == NULL, "hash creation failed");
303
304	pos0 = rte_hash_add_key(handle, &keys[0]);
305	print_key_info("Add", &keys[0], pos0);
306	RETURN_IF_ERROR(pos0 < 0, "failed to add key (pos0=%d)", pos0);
307	expectedPos0 = pos0;
308
309	pos0 = rte_hash_lookup(handle, &keys[0]);
310	print_key_info("Lkp", &keys[0], pos0);
311	RETURN_IF_ERROR(pos0 != expectedPos0,
312			"failed to find key (pos0=%d)", pos0);
313
314	pos0 = rte_hash_del_key(handle, &keys[0]);
315	print_key_info("Del", &keys[0], pos0);
316	RETURN_IF_ERROR(pos0 != expectedPos0,
317			"failed to delete key (pos0=%d)", pos0);
318
319	pos0 = rte_hash_lookup(handle, &keys[0]);
320	print_key_info("Lkp", &keys[0], pos0);
321	RETURN_IF_ERROR(pos0 != -ENOENT,
322			"fail: found key after deleting! (pos0=%d)", pos0);
323
324	rte_hash_free(handle);
325
326	/* repeat test with precomputed hash functions */
327	hash_sig_t hash_value;
328	int pos1, expectedPos1;
329
330	handle = rte_hash_create(&ut_params);
331	RETURN_IF_ERROR(handle == NULL, "hash creation failed");
332
333	hash_value = rte_hash_hash(handle, &keys[0]);
334	pos1 = rte_hash_add_key_with_hash(handle, &keys[0], hash_value);
335	print_key_info("Add", &keys[0], pos1);
336	RETURN_IF_ERROR(pos1 < 0, "failed to add key (pos1=%d)", pos1);
337	expectedPos1 = pos1;
338
339	pos1 = rte_hash_lookup_with_hash(handle, &keys[0], hash_value);
340	print_key_info("Lkp", &keys[0], pos1);
341	RETURN_IF_ERROR(pos1 != expectedPos1,
342			"failed to find key (pos1=%d)", pos1);
343
344	pos1 = rte_hash_del_key_with_hash(handle, &keys[0], hash_value);
345	print_key_info("Del", &keys[0], pos1);
346	RETURN_IF_ERROR(pos1 != expectedPos1,
347			"failed to delete key (pos1=%d)", pos1);
348
349	pos1 = rte_hash_lookup_with_hash(handle, &keys[0], hash_value);
350	print_key_info("Lkp", &keys[0], pos1);
351	RETURN_IF_ERROR(pos1 != -ENOENT,
352			"fail: found key after deleting! (pos1=%d)", pos1);
353
354	rte_hash_free(handle);
355
356	return 0;
357}
358
359/*
360 * Sequence of operations for a single key:
361 *	- delete: miss
362 *	- add
363 *	- lookup: hit
364 *	- add: update
365 *	- lookup: hit (updated data)
366 *	- delete: hit
367 *	- delete: miss
368 *	- lookup: miss
369 */
370static int test_add_update_delete(void)
371{
372	struct rte_hash *handle;
373	int pos0, expectedPos0;
374
375	ut_params.name = "test2";
376	handle = rte_hash_create(&ut_params);
377	RETURN_IF_ERROR(handle == NULL, "hash creation failed");
378
379	pos0 = rte_hash_del_key(handle, &keys[0]);
380	print_key_info("Del", &keys[0], pos0);
381	RETURN_IF_ERROR(pos0 != -ENOENT,
382			"fail: found non-existent key (pos0=%d)", pos0);
383
384	pos0 = rte_hash_add_key(handle, &keys[0]);
385	print_key_info("Add", &keys[0], pos0);
386	RETURN_IF_ERROR(pos0 < 0, "failed to add key (pos0=%d)", pos0);
387	expectedPos0 = pos0;
388
389	pos0 = rte_hash_lookup(handle, &keys[0]);
390	print_key_info("Lkp", &keys[0], pos0);
391	RETURN_IF_ERROR(pos0 != expectedPos0,
392			"failed to find key (pos0=%d)", pos0);
393
394	pos0 = rte_hash_add_key(handle, &keys[0]);
395	print_key_info("Add", &keys[0], pos0);
396	RETURN_IF_ERROR(pos0 != expectedPos0,
397			"failed to re-add key (pos0=%d)", pos0);
398
399	pos0 = rte_hash_lookup(handle, &keys[0]);
400	print_key_info("Lkp", &keys[0], pos0);
401	RETURN_IF_ERROR(pos0 != expectedPos0,
402			"failed to find key (pos0=%d)", pos0);
403
404	pos0 = rte_hash_del_key(handle, &keys[0]);
405	print_key_info("Del", &keys[0], pos0);
406	RETURN_IF_ERROR(pos0 != expectedPos0,
407			"failed to delete key (pos0=%d)", pos0);
408
409	pos0 = rte_hash_del_key(handle, &keys[0]);
410	print_key_info("Del", &keys[0], pos0);
411	RETURN_IF_ERROR(pos0 != -ENOENT,
412			"fail: deleted already deleted key (pos0=%d)", pos0);
413
414	pos0 = rte_hash_lookup(handle, &keys[0]);
415	print_key_info("Lkp", &keys[0], pos0);
416	RETURN_IF_ERROR(pos0 != -ENOENT,
417			"fail: found key after deleting! (pos0=%d)", pos0);
418
419	rte_hash_free(handle);
420	return 0;
421}
422
423/*
424 * Sequence of operations for retrieving a key with its position
425 *
426 *  - create table
427 *  - add key
428 *  - get the key with its position: hit
429 *  - delete key
430 *  - try to get the deleted key: miss
431 *
432 */
433static int test_hash_get_key_with_position(void)
434{
435	struct rte_hash *handle = NULL;
436	int pos, expectedPos, result;
437	void *key;
438
439	ut_params.name = "hash_get_key_w_pos";
440	handle = rte_hash_create(&ut_params);
441	RETURN_IF_ERROR(handle == NULL, "hash creation failed");
442
443	pos = rte_hash_add_key(handle, &keys[0]);
444	print_key_info("Add", &keys[0], pos);
445	RETURN_IF_ERROR(pos < 0, "failed to add key (pos0=%d)", pos);
446	expectedPos = pos;
447
448	result = rte_hash_get_key_with_position(handle, pos, &key);
449	RETURN_IF_ERROR(result != 0, "error retrieving a key");
450
451	pos = rte_hash_del_key(handle, &keys[0]);
452	print_key_info("Del", &keys[0], pos);
453	RETURN_IF_ERROR(pos != expectedPos,
454			"failed to delete key (pos0=%d)", pos);
455
456	result = rte_hash_get_key_with_position(handle, pos, &key);
457	RETURN_IF_ERROR(result != -ENOENT, "non valid key retrieved");
458
459	rte_hash_free(handle);
460	return 0;
461}
462
463/*
464 * Sequence of operations for find existing hash table
465 *
466 *  - create table
467 *  - find existing table: hit
468 *  - find non-existing table: miss
469 *
470 */
471static int test_hash_find_existing(void)
472{
473	struct rte_hash *handle = NULL, *result = NULL;
474
475	/* Create hash table. */
476	ut_params.name = "hash_find_existing";
477	handle = rte_hash_create(&ut_params);
478	RETURN_IF_ERROR(handle == NULL, "hash creation failed");
479
480	/* Try to find existing hash table */
481	result = rte_hash_find_existing("hash_find_existing");
482	RETURN_IF_ERROR(result != handle, "could not find existing hash table");
483
484	/* Try to find non-existing hash table */
485	result = rte_hash_find_existing("hash_find_non_existing");
486	RETURN_IF_ERROR(!(result == NULL), "found table that shouldn't exist");
487
488	/* Cleanup. */
489	rte_hash_free(handle);
490
491	return 0;
492}
493
494/*
495 * Sequence of operations for 5 keys
496 *	- add keys
497 *	- lookup keys: hit
498 *	- add keys (update)
499 *	- lookup keys: hit (updated data)
500 *	- delete keys : hit
501 *	- lookup keys: miss
502 */
503static int test_five_keys(void)
504{
505	struct rte_hash *handle;
506	const void *key_array[5] = {0};
507	int pos[5];
508	int expected_pos[5];
509	unsigned i;
510	int ret;
511
512	ut_params.name = "test3";
513	handle = rte_hash_create(&ut_params);
514	RETURN_IF_ERROR(handle == NULL, "hash creation failed");
515
516	/* Add */
517	for (i = 0; i < 5; i++) {
518		pos[i] = rte_hash_add_key(handle, &keys[i]);
519		print_key_info("Add", &keys[i], pos[i]);
520		RETURN_IF_ERROR(pos[i] < 0,
521				"failed to add key (pos[%u]=%d)", i, pos[i]);
522		expected_pos[i] = pos[i];
523	}
524
525	/* Lookup */
526	for(i = 0; i < 5; i++)
527		key_array[i] = &keys[i];
528
529	ret = rte_hash_lookup_bulk(handle, &key_array[0], 5, (int32_t *)pos);
530	if(ret == 0)
531		for(i = 0; i < 5; i++) {
532			print_key_info("Lkp", key_array[i], pos[i]);
533			RETURN_IF_ERROR(pos[i] != expected_pos[i],
534					"failed to find key (pos[%u]=%d)", i, pos[i]);
535		}
536
537	/* Add - update */
538	for (i = 0; i < 5; i++) {
539		pos[i] = rte_hash_add_key(handle, &keys[i]);
540		print_key_info("Add", &keys[i], pos[i]);
541		RETURN_IF_ERROR(pos[i] != expected_pos[i],
542				"failed to add key (pos[%u]=%d)", i, pos[i]);
543	}
544
545	/* Lookup */
546	for (i = 0; i < 5; i++) {
547		pos[i] = rte_hash_lookup(handle, &keys[i]);
548		print_key_info("Lkp", &keys[i], pos[i]);
549		RETURN_IF_ERROR(pos[i] != expected_pos[i],
550				"failed to find key (pos[%u]=%d)", i, pos[i]);
551	}
552
553	/* Delete */
554	for (i = 0; i < 5; i++) {
555		pos[i] = rte_hash_del_key(handle, &keys[i]);
556		print_key_info("Del", &keys[i], pos[i]);
557		RETURN_IF_ERROR(pos[i] != expected_pos[i],
558				"failed to delete key (pos[%u]=%d)", i, pos[i]);
559	}
560
561	/* Lookup */
562	for (i = 0; i < 5; i++) {
563		pos[i] = rte_hash_lookup(handle, &keys[i]);
564		print_key_info("Lkp", &keys[i], pos[i]);
565		RETURN_IF_ERROR(pos[i] != -ENOENT,
566				"found non-existent key (pos[%u]=%d)", i, pos[i]);
567	}
568
569	/* Lookup multi */
570	ret = rte_hash_lookup_bulk(handle, &key_array[0], 5, (int32_t *)pos);
571	if (ret == 0)
572		for (i = 0; i < 5; i++) {
573			print_key_info("Lkp", key_array[i], pos[i]);
574			RETURN_IF_ERROR(pos[i] != -ENOENT,
575					"found not-existent key (pos[%u]=%d)", i, pos[i]);
576		}
577
578	rte_hash_free(handle);
579
580	return 0;
581}
582
583/*
584 * Add keys to the same bucket until bucket full.
585 *	- add 5 keys to the same bucket (hash created with 4 keys per bucket):
586 *	  first 4 successful, 5th successful, pushing existing item in bucket
587 *	- lookup the 5 keys: 5 hits
588 *	- add the 5 keys again: 5 OK
589 *	- lookup the 5 keys: 5 hits (updated data)
590 *	- delete the 5 keys: 5 OK
591 *	- lookup the 5 keys: 5 misses
592 */
593static int test_full_bucket(void)
594{
595	struct rte_hash_parameters params_pseudo_hash = {
596		.name = "test4",
597		.entries = 64,
598		.key_len = sizeof(struct flow_key), /* 13 */
599		.hash_func = pseudo_hash,
600		.hash_func_init_val = 0,
601		.socket_id = 0,
602	};
603	struct rte_hash *handle;
604	int pos[5];
605	int expected_pos[5];
606	unsigned i;
607
608	handle = rte_hash_create(&params_pseudo_hash);
609	RETURN_IF_ERROR(handle == NULL, "hash creation failed");
610
611	/* Fill bucket */
612	for (i = 0; i < 4; i++) {
613		pos[i] = rte_hash_add_key(handle, &keys[i]);
614		print_key_info("Add", &keys[i], pos[i]);
615		RETURN_IF_ERROR(pos[i] < 0,
616			"failed to add key (pos[%u]=%d)", i, pos[i]);
617		expected_pos[i] = pos[i];
618	}
619	/*
620	 * This should work and will push one of the items
621	 * in the bucket because it is full
622	 */
623	pos[4] = rte_hash_add_key(handle, &keys[4]);
624	print_key_info("Add", &keys[4], pos[4]);
625	RETURN_IF_ERROR(pos[4] < 0,
626			"failed to add key (pos[4]=%d)", pos[4]);
627	expected_pos[4] = pos[4];
628
629	/* Lookup */
630	for (i = 0; i < 5; i++) {
631		pos[i] = rte_hash_lookup(handle, &keys[i]);
632		print_key_info("Lkp", &keys[i], pos[i]);
633		RETURN_IF_ERROR(pos[i] != expected_pos[i],
634			"failed to find key (pos[%u]=%d)", i, pos[i]);
635	}
636
637	/* Add - update */
638	for (i = 0; i < 5; i++) {
639		pos[i] = rte_hash_add_key(handle, &keys[i]);
640		print_key_info("Add", &keys[i], pos[i]);
641		RETURN_IF_ERROR(pos[i] != expected_pos[i],
642			"failed to add key (pos[%u]=%d)", i, pos[i]);
643	}
644
645	/* Lookup */
646	for (i = 0; i < 5; i++) {
647		pos[i] = rte_hash_lookup(handle, &keys[i]);
648		print_key_info("Lkp", &keys[i], pos[i]);
649		RETURN_IF_ERROR(pos[i] != expected_pos[i],
650			"failed to find key (pos[%u]=%d)", i, pos[i]);
651	}
652
653	/* Delete 1 key, check other keys are still found */
654	pos[1] = rte_hash_del_key(handle, &keys[1]);
655	print_key_info("Del", &keys[1], pos[1]);
656	RETURN_IF_ERROR(pos[1] != expected_pos[1],
657			"failed to delete key (pos[1]=%d)", pos[1]);
658	pos[3] = rte_hash_lookup(handle, &keys[3]);
659	print_key_info("Lkp", &keys[3], pos[3]);
660	RETURN_IF_ERROR(pos[3] != expected_pos[3],
661			"failed lookup after deleting key from same bucket "
662			"(pos[3]=%d)", pos[3]);
663
664	/* Go back to previous state */
665	pos[1] = rte_hash_add_key(handle, &keys[1]);
666	print_key_info("Add", &keys[1], pos[1]);
667	expected_pos[1] = pos[1];
668	RETURN_IF_ERROR(pos[1] < 0, "failed to add key (pos[1]=%d)", pos[1]);
669
670	/* Delete */
671	for (i = 0; i < 5; i++) {
672		pos[i] = rte_hash_del_key(handle, &keys[i]);
673		print_key_info("Del", &keys[i], pos[i]);
674		RETURN_IF_ERROR(pos[i] != expected_pos[i],
675			"failed to delete key (pos[%u]=%d)", i, pos[i]);
676	}
677
678	/* Lookup */
679	for (i = 0; i < 5; i++) {
680		pos[i] = rte_hash_lookup(handle, &keys[i]);
681		print_key_info("Lkp", &keys[i], pos[i]);
682		RETURN_IF_ERROR(pos[i] != -ENOENT,
683			"fail: found non-existent key (pos[%u]=%d)", i, pos[i]);
684	}
685
686	rte_hash_free(handle);
687
688	/* Cover the NULL case. */
689	rte_hash_free(0);
690	return 0;
691}
692
693/******************************************************************************/
694static int
695fbk_hash_unit_test(void)
696{
697	struct rte_fbk_hash_params params = {
698		.name = "fbk_hash_test",
699		.entries = LOCAL_FBK_HASH_ENTRIES_MAX,
700		.entries_per_bucket = 4,
701		.socket_id = 0,
702	};
703
704	struct rte_fbk_hash_params invalid_params_1 = {
705		.name = "invalid_1",
706		.entries = LOCAL_FBK_HASH_ENTRIES_MAX + 1, /* Not power of 2 */
707		.entries_per_bucket = 4,
708		.socket_id = 0,
709	};
710
711	struct rte_fbk_hash_params invalid_params_2 = {
712		.name = "invalid_2",
713		.entries = 4,
714		.entries_per_bucket = 3,         /* Not power of 2 */
715		.socket_id = 0,
716	};
717
718	struct rte_fbk_hash_params invalid_params_3 = {
719		.name = "invalid_3",
720		.entries = 0,                    /* Entries is 0 */
721		.entries_per_bucket = 4,
722		.socket_id = 0,
723	};
724
725	struct rte_fbk_hash_params invalid_params_4 = {
726		.name = "invalid_4",
727		.entries = LOCAL_FBK_HASH_ENTRIES_MAX,
728		.entries_per_bucket = 0,         /* Entries per bucket is 0 */
729		.socket_id = 0,
730	};
731
732	struct rte_fbk_hash_params invalid_params_5 = {
733		.name = "invalid_5",
734		.entries = 4,
735		.entries_per_bucket = 8,         /* Entries per bucket > entries */
736		.socket_id = 0,
737	};
738
739	struct rte_fbk_hash_params invalid_params_6 = {
740		.name = "invalid_6",
741		.entries = RTE_FBK_HASH_ENTRIES_MAX * 2,   /* Entries > max allowed */
742		.entries_per_bucket = 4,
743		.socket_id = 0,
744	};
745
746	struct rte_fbk_hash_params invalid_params_7 = {
747		.name = "invalid_7",
748		.entries = RTE_FBK_HASH_ENTRIES_MAX,
749		.entries_per_bucket = RTE_FBK_HASH_ENTRIES_PER_BUCKET_MAX * 2,	/* Entries > max allowed */
750		.socket_id = 0,
751	};
752
753	struct rte_fbk_hash_params invalid_params_8 = {
754		.name = "invalid_7",
755		.entries = RTE_FBK_HASH_ENTRIES_MAX,
756		.entries_per_bucket = 4,
757		.socket_id = RTE_MAX_NUMA_NODES + 1, /* invalid socket */
758	};
759
760	/* try to create two hashes with identical names
761	 * in this case, trying to create a second one will not
762	 * fail but will simply return pointer to the existing
763	 * hash with that name. sort of like a "find hash by name" :-)
764	 */
765	struct rte_fbk_hash_params invalid_params_same_name_1 = {
766		.name = "same_name",				/* hash with identical name */
767		.entries = 4,
768		.entries_per_bucket = 2,
769		.socket_id = 0,
770	};
771
772	/* trying to create this hash should return a pointer to an existing hash */
773	struct rte_fbk_hash_params invalid_params_same_name_2 = {
774		.name = "same_name",				/* hash with identical name */
775		.entries = RTE_FBK_HASH_ENTRIES_MAX,
776		.entries_per_bucket = 4,
777		.socket_id = 0,
778	};
779
780	/* this is a sanity check for "same name" test
781	 * creating this hash will check if we are actually able to create
782	 * multiple hashes with different names (instead of having just one).
783	 */
784	struct rte_fbk_hash_params different_name = {
785		.name = "different_name",			/* different name */
786		.entries = RTE_FBK_HASH_ENTRIES_MAX,
787		.entries_per_bucket = 4,
788		.socket_id = 0,
789	};
790
791	struct rte_fbk_hash_params params_jhash = {
792		.name = "valid",
793		.entries = LOCAL_FBK_HASH_ENTRIES_MAX,
794		.entries_per_bucket = 4,
795		.socket_id = 0,
796		.hash_func = rte_jhash_1word,              /* Tests for different hash_func */
797		.init_val = RTE_FBK_HASH_INIT_VAL_DEFAULT,
798	};
799
800	struct rte_fbk_hash_params params_nohash = {
801		.name = "valid nohash",
802		.entries = LOCAL_FBK_HASH_ENTRIES_MAX,
803		.entries_per_bucket = 4,
804		.socket_id = 0,
805		.hash_func = NULL,                            /* Tests for null hash_func */
806		.init_val = RTE_FBK_HASH_INIT_VAL_DEFAULT,
807	};
808
809	struct rte_fbk_hash_table *handle, *tmp;
810	uint32_t keys[5] =
811		{0xc6e18639, 0xe67c201c, 0xd4c8cffd, 0x44728691, 0xd5430fa9};
812	uint16_t vals[5] = {28108, 5699, 38490, 2166, 61571};
813	int status;
814	unsigned i;
815	double used_entries;
816
817	/* Try creating hashes with invalid parameters */
818	printf("# Testing hash creation with invalid parameters "
819			"- expect error msgs\n");
820	handle = rte_fbk_hash_create(&invalid_params_1);
821	RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed");
822
823	handle = rte_fbk_hash_create(&invalid_params_2);
824	RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed");
825
826	handle = rte_fbk_hash_create(&invalid_params_3);
827	RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed");
828
829	handle = rte_fbk_hash_create(&invalid_params_4);
830	RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed");
831
832	handle = rte_fbk_hash_create(&invalid_params_5);
833	RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed");
834
835	handle = rte_fbk_hash_create(&invalid_params_6);
836	RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed");
837
838	handle = rte_fbk_hash_create(&invalid_params_7);
839	RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed");
840
841	handle = rte_fbk_hash_create(&invalid_params_8);
842	RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed");
843
844	handle = rte_fbk_hash_create(&invalid_params_same_name_1);
845	RETURN_IF_ERROR_FBK(handle == NULL, "fbk hash creation should have succeeded");
846
847	tmp = rte_fbk_hash_create(&invalid_params_same_name_2);
848	if (tmp != NULL)
849		rte_fbk_hash_free(tmp);
850	RETURN_IF_ERROR_FBK(tmp != NULL, "fbk hash creation should have failed");
851
852	/* we are not freeing  handle here because we need a hash list
853	 * to be not empty for the next test */
854
855	/* create a hash in non-empty list - good for coverage */
856	tmp = rte_fbk_hash_create(&different_name);
857	RETURN_IF_ERROR_FBK(tmp == NULL, "fbk hash creation should have succeeded");
858
859	/* free both hashes */
860	rte_fbk_hash_free(handle);
861	rte_fbk_hash_free(tmp);
862
863	/* Create empty jhash hash. */
864	handle = rte_fbk_hash_create(&params_jhash);
865	RETURN_IF_ERROR_FBK(handle == NULL, "fbk jhash hash creation failed");
866
867	/* Cleanup. */
868	rte_fbk_hash_free(handle);
869
870	/* Create empty jhash hash. */
871	handle = rte_fbk_hash_create(&params_nohash);
872	RETURN_IF_ERROR_FBK(handle == NULL, "fbk nohash hash creation failed");
873
874	/* Cleanup. */
875	rte_fbk_hash_free(handle);
876
877	/* Create empty hash. */
878	handle = rte_fbk_hash_create(&params);
879	RETURN_IF_ERROR_FBK(handle == NULL, "fbk hash creation failed");
880
881	used_entries = rte_fbk_hash_get_load_factor(handle) * LOCAL_FBK_HASH_ENTRIES_MAX;
882	RETURN_IF_ERROR_FBK((unsigned)used_entries != 0, \
883				"load factor right after creation is not zero but it should be");
884	/* Add keys. */
885	for (i = 0; i < 5; i++) {
886		status = rte_fbk_hash_add_key(handle, keys[i], vals[i]);
887		RETURN_IF_ERROR_FBK(status != 0, "fbk hash add failed");
888	}
889
890	used_entries = rte_fbk_hash_get_load_factor(handle) * LOCAL_FBK_HASH_ENTRIES_MAX;
891	RETURN_IF_ERROR_FBK((unsigned)used_entries != (unsigned)((((double)5)/LOCAL_FBK_HASH_ENTRIES_MAX)*LOCAL_FBK_HASH_ENTRIES_MAX), \
892				"load factor now is not as expected");
893	/* Find value of added keys. */
894	for (i = 0; i < 5; i++) {
895		status = rte_fbk_hash_lookup(handle, keys[i]);
896		RETURN_IF_ERROR_FBK(status != vals[i],
897				"fbk hash lookup failed");
898	}
899
900	/* Change value of added keys. */
901	for (i = 0; i < 5; i++) {
902		status = rte_fbk_hash_add_key(handle, keys[i], vals[4 - i]);
903		RETURN_IF_ERROR_FBK(status != 0, "fbk hash update failed");
904	}
905
906	/* Find new values. */
907	for (i = 0; i < 5; i++) {
908		status = rte_fbk_hash_lookup(handle, keys[i]);
909		RETURN_IF_ERROR_FBK(status != vals[4-i],
910				"fbk hash lookup failed");
911	}
912
913	/* Delete keys individually. */
914	for (i = 0; i < 5; i++) {
915		status = rte_fbk_hash_delete_key(handle, keys[i]);
916		RETURN_IF_ERROR_FBK(status != 0, "fbk hash delete failed");
917	}
918
919	used_entries = rte_fbk_hash_get_load_factor(handle) * LOCAL_FBK_HASH_ENTRIES_MAX;
920	RETURN_IF_ERROR_FBK((unsigned)used_entries != 0, \
921				"load factor right after deletion is not zero but it should be");
922	/* Lookup should now fail. */
923	for (i = 0; i < 5; i++) {
924		status = rte_fbk_hash_lookup(handle, keys[i]);
925		RETURN_IF_ERROR_FBK(status == 0,
926				"fbk hash lookup should have failed");
927	}
928
929	/* Add keys again. */
930	for (i = 0; i < 5; i++) {
931		status = rte_fbk_hash_add_key(handle, keys[i], vals[i]);
932		RETURN_IF_ERROR_FBK(status != 0, "fbk hash add failed");
933	}
934
935	/* Make sure they were added. */
936	for (i = 0; i < 5; i++) {
937		status = rte_fbk_hash_lookup(handle, keys[i]);
938		RETURN_IF_ERROR_FBK(status != vals[i],
939				"fbk hash lookup failed");
940	}
941
942	/* Clear all entries. */
943	rte_fbk_hash_clear_all(handle);
944
945	/* Lookup should fail. */
946	for (i = 0; i < 5; i++) {
947		status = rte_fbk_hash_lookup(handle, keys[i]);
948		RETURN_IF_ERROR_FBK(status == 0,
949				"fbk hash lookup should have failed");
950	}
951
952	/* coverage */
953
954	/* fill up the hash_table */
955	for (i = 0; i < RTE_FBK_HASH_ENTRIES_MAX + 1; i++)
956		rte_fbk_hash_add_key(handle, i, (uint16_t) i);
957
958	/* Find non-existent key in a full hashtable */
959	status = rte_fbk_hash_lookup(handle, RTE_FBK_HASH_ENTRIES_MAX + 1);
960	RETURN_IF_ERROR_FBK(status != -ENOENT,
961			"fbk hash lookup succeeded");
962
963	/* Delete non-existent key in a full hashtable */
964	status = rte_fbk_hash_delete_key(handle, RTE_FBK_HASH_ENTRIES_MAX + 1);
965	RETURN_IF_ERROR_FBK(status != -ENOENT,
966			"fbk hash delete succeeded");
967
968	/* Delete one key from a full hashtable */
969	status = rte_fbk_hash_delete_key(handle, 1);
970	RETURN_IF_ERROR_FBK(status != 0,
971			"fbk hash delete failed");
972
973	/* Clear all entries. */
974	rte_fbk_hash_clear_all(handle);
975
976	/* Cleanup. */
977	rte_fbk_hash_free(handle);
978
979	/* Cover the NULL case. */
980	rte_fbk_hash_free(0);
981
982	return 0;
983}
984
985/*
986 * Sequence of operations for find existing fbk hash table
987 *
988 *  - create table
989 *  - find existing table: hit
990 *  - find non-existing table: miss
991 *
992 */
993static int test_fbk_hash_find_existing(void)
994{
995	struct rte_fbk_hash_params params = {
996			.name = "fbk_hash_find_existing",
997			.entries = LOCAL_FBK_HASH_ENTRIES_MAX,
998			.entries_per_bucket = 4,
999			.socket_id = 0,
1000	};
1001	struct rte_fbk_hash_table *handle = NULL, *result = NULL;
1002
1003	/* Create hash table. */
1004	handle = rte_fbk_hash_create(&params);
1005	RETURN_IF_ERROR_FBK(handle == NULL, "fbk hash creation failed");
1006
1007	/* Try to find existing fbk hash table */
1008	result = rte_fbk_hash_find_existing("fbk_hash_find_existing");
1009	RETURN_IF_ERROR_FBK(result != handle, "could not find existing fbk hash table");
1010
1011	/* Try to find non-existing fbk hash table */
1012	result = rte_fbk_hash_find_existing("fbk_hash_find_non_existing");
1013	RETURN_IF_ERROR_FBK(!(result == NULL), "found fbk table that shouldn't exist");
1014
1015	/* Cleanup. */
1016	rte_fbk_hash_free(handle);
1017
1018	return 0;
1019}
1020
1021#define BUCKET_ENTRIES 4
1022/*
1023 * Do tests for hash creation with bad parameters.
1024 */
1025static int test_hash_creation_with_bad_parameters(void)
1026{
1027	struct rte_hash *handle, *tmp;
1028	struct rte_hash_parameters params;
1029
1030	handle = rte_hash_create(NULL);
1031	if (handle != NULL) {
1032		rte_hash_free(handle);
1033		printf("Impossible creating hash sucessfully without any parameter\n");
1034		return -1;
1035	}
1036
1037	memcpy(&params, &ut_params, sizeof(params));
1038	params.name = "creation_with_bad_parameters_0";
1039	params.entries = RTE_HASH_ENTRIES_MAX + 1;
1040	handle = rte_hash_create(&params);
1041	if (handle != NULL) {
1042		rte_hash_free(handle);
1043		printf("Impossible creating hash sucessfully with entries in parameter exceeded\n");
1044		return -1;
1045	}
1046
1047	memcpy(&params, &ut_params, sizeof(params));
1048	params.name = "creation_with_bad_parameters_2";
1049	params.entries = BUCKET_ENTRIES - 1;
1050	handle = rte_hash_create(&params);
1051	if (handle != NULL) {
1052		rte_hash_free(handle);
1053		printf("Impossible creating hash sucessfully if entries less than bucket_entries in parameter\n");
1054		return -1;
1055	}
1056
1057	memcpy(&params, &ut_params, sizeof(params));
1058	params.name = "creation_with_bad_parameters_3";
1059	params.key_len = 0;
1060	handle = rte_hash_create(&params);
1061	if (handle != NULL) {
1062		rte_hash_free(handle);
1063		printf("Impossible creating hash sucessfully if key_len in parameter is zero\n");
1064		return -1;
1065	}
1066
1067	memcpy(&params, &ut_params, sizeof(params));
1068	params.name = "creation_with_bad_parameters_4";
1069	params.socket_id = RTE_MAX_NUMA_NODES + 1;
1070	handle = rte_hash_create(&params);
1071	if (handle != NULL) {
1072		rte_hash_free(handle);
1073		printf("Impossible creating hash sucessfully with invalid socket\n");
1074		return -1;
1075	}
1076
1077	/* test with same name should fail */
1078	memcpy(&params, &ut_params, sizeof(params));
1079	params.name = "same_name";
1080	handle = rte_hash_create(&params);
1081	if (handle == NULL) {
1082		printf("Cannot create first hash table with 'same_name'\n");
1083		return -1;
1084	}
1085	tmp = rte_hash_create(&params);
1086	if (tmp != NULL) {
1087		printf("Creation of hash table with same name should fail\n");
1088		rte_hash_free(handle);
1089		rte_hash_free(tmp);
1090		return -1;
1091	}
1092	rte_hash_free(handle);
1093
1094	printf("# Test successful. No more errors expected\n");
1095
1096	return 0;
1097}
1098
1099/*
1100 * Do tests for hash creation with parameters that look incorrect
1101 * but are actually valid.
1102 */
1103static int
1104test_hash_creation_with_good_parameters(void)
1105{
1106	struct rte_hash *handle;
1107	struct rte_hash_parameters params;
1108
1109	/* create with null hash function - should choose DEFAULT_HASH_FUNC */
1110	memcpy(&params, &ut_params, sizeof(params));
1111	params.name = "name";
1112	params.hash_func = NULL;
1113	handle = rte_hash_create(&params);
1114	if (handle == NULL) {
1115		printf("Creating hash with null hash_func failed\n");
1116		return -1;
1117	}
1118
1119	rte_hash_free(handle);
1120
1121	return 0;
1122}
1123
1124#define ITERATIONS 3
1125/*
1126 * Test to see the average table utilization (entries added/max entries)
1127 * before hitting a random entry that cannot be added
1128 */
1129static int test_average_table_utilization(void)
1130{
1131	struct rte_hash *handle;
1132	uint8_t simple_key[MAX_KEYSIZE];
1133	unsigned i, j;
1134	unsigned added_keys, average_keys_added = 0;
1135	int ret;
1136
1137	printf("\n# Running test to determine average utilization"
1138	       "\n  before adding elements begins to fail\n");
1139	printf("Measuring performance, please wait");
1140	fflush(stdout);
1141	ut_params.entries = 1 << 16;
1142	ut_params.name = "test_average_utilization";
1143	ut_params.hash_func = rte_jhash;
1144	handle = rte_hash_create(&ut_params);
1145	RETURN_IF_ERROR(handle == NULL, "hash creation failed");
1146
1147	for (j = 0; j < ITERATIONS; j++) {
1148		ret = 0;
1149		/* Add random entries until key cannot be added */
1150		for (added_keys = 0; ret >= 0; added_keys++) {
1151			for (i = 0; i < ut_params.key_len; i++)
1152				simple_key[i] = rte_rand() % 255;
1153			ret = rte_hash_add_key(handle, simple_key);
1154		}
1155		if (ret != -ENOSPC) {
1156			printf("Unexpected error when adding keys\n");
1157			rte_hash_free(handle);
1158			return -1;
1159		}
1160
1161		average_keys_added += added_keys;
1162
1163		/* Reset the table */
1164		rte_hash_reset(handle);
1165
1166		/* Print a dot to show progress on operations */
1167		printf(".");
1168		fflush(stdout);
1169	}
1170
1171	average_keys_added /= ITERATIONS;
1172
1173	printf("\nAverage table utilization = %.2f%% (%u/%u)\n",
1174		((double) average_keys_added / ut_params.entries * 100),
1175		average_keys_added, ut_params.entries);
1176	rte_hash_free(handle);
1177
1178	return 0;
1179}
1180
1181#define NUM_ENTRIES 256
1182static int test_hash_iteration(void)
1183{
1184	struct rte_hash *handle;
1185	unsigned i;
1186	uint8_t keys[NUM_ENTRIES][MAX_KEYSIZE];
1187	const void *next_key;
1188	void *next_data;
1189	void *data[NUM_ENTRIES];
1190	unsigned added_keys;
1191	uint32_t iter = 0;
1192	int ret = 0;
1193
1194	ut_params.entries = NUM_ENTRIES;
1195	ut_params.name = "test_hash_iteration";
1196	ut_params.hash_func = rte_jhash;
1197	ut_params.key_len = 16;
1198	handle = rte_hash_create(&ut_params);
1199	RETURN_IF_ERROR(handle == NULL, "hash creation failed");
1200
1201	/* Add random entries until key cannot be added */
1202	for (added_keys = 0; added_keys < NUM_ENTRIES; added_keys++) {
1203		data[added_keys] = (void *) ((uintptr_t) rte_rand());
1204		for (i = 0; i < ut_params.key_len; i++)
1205			keys[added_keys][i] = rte_rand() % 255;
1206		ret = rte_hash_add_key_data(handle, keys[added_keys], data[added_keys]);
1207		if (ret < 0)
1208			break;
1209	}
1210
1211	/* Iterate through the hash table */
1212	while (rte_hash_iterate(handle, &next_key, &next_data, &iter) >= 0) {
1213		/* Search for the key in the list of keys added */
1214		for (i = 0; i < NUM_ENTRIES; i++) {
1215			if (memcmp(next_key, keys[i], ut_params.key_len) == 0) {
1216				if (next_data != data[i]) {
1217					printf("Data found in the hash table is"
1218					       "not the data added with the key\n");
1219					goto err;
1220				}
1221				added_keys--;
1222				break;
1223			}
1224		}
1225		if (i == NUM_ENTRIES) {
1226			printf("Key found in the hash table was not added\n");
1227			goto err;
1228		}
1229	}
1230
1231	/* Check if all keys have been iterated */
1232	if (added_keys != 0) {
1233		printf("There were still %u keys to iterate\n", added_keys);
1234		goto err;
1235	}
1236
1237	rte_hash_free(handle);
1238	return 0;
1239
1240err:
1241	rte_hash_free(handle);
1242	return -1;
1243}
1244
1245static uint8_t key[16] = {0x00, 0x01, 0x02, 0x03,
1246			0x04, 0x05, 0x06, 0x07,
1247			0x08, 0x09, 0x0a, 0x0b,
1248			0x0c, 0x0d, 0x0e, 0x0f};
1249static struct rte_hash_parameters hash_params_ex = {
1250	.name = NULL,
1251	.entries = 64,
1252	.key_len = 0,
1253	.hash_func = NULL,
1254	.hash_func_init_val = 0,
1255	.socket_id = 0,
1256};
1257
1258/*
1259 * add/delete key with jhash2
1260 */
1261static int
1262test_hash_add_delete_jhash2(void)
1263{
1264	int ret = -1;
1265	struct rte_hash *handle;
1266	int32_t pos1, pos2;
1267
1268	hash_params_ex.name = "hash_test_jhash2";
1269	hash_params_ex.key_len = 4;
1270	hash_params_ex.hash_func = (rte_hash_function)rte_jhash_32b;
1271
1272	handle = rte_hash_create(&hash_params_ex);
1273	if (handle == NULL) {
1274		printf("test_hash_add_delete_jhash2 fail to create hash\n");
1275		goto fail_jhash2;
1276	}
1277	pos1 = rte_hash_add_key(handle, (void *)&key[0]);
1278	if (pos1 < 0) {
1279		printf("test_hash_add_delete_jhash2 fail to add hash key\n");
1280		goto fail_jhash2;
1281	}
1282
1283	pos2 = rte_hash_del_key(handle, (void *)&key[0]);
1284	if (pos2 < 0 || pos1 != pos2) {
1285		printf("test_hash_add_delete_jhash2 delete different key from being added\n");
1286		goto fail_jhash2;
1287	}
1288	ret = 0;
1289
1290fail_jhash2:
1291	if (handle != NULL)
1292		rte_hash_free(handle);
1293
1294	return ret;
1295}
1296
1297/*
1298 * add/delete (2) key with jhash2
1299 */
1300static int
1301test_hash_add_delete_2_jhash2(void)
1302{
1303	int ret = -1;
1304	struct rte_hash *handle;
1305	int32_t pos1, pos2;
1306
1307	hash_params_ex.name = "hash_test_2_jhash2";
1308	hash_params_ex.key_len = 8;
1309	hash_params_ex.hash_func = (rte_hash_function)rte_jhash_32b;
1310
1311	handle = rte_hash_create(&hash_params_ex);
1312	if (handle == NULL)
1313		goto fail_2_jhash2;
1314
1315	pos1 = rte_hash_add_key(handle, (void *)&key[0]);
1316	if (pos1 < 0)
1317		goto fail_2_jhash2;
1318
1319	pos2 = rte_hash_del_key(handle, (void *)&key[0]);
1320	if (pos2 < 0 || pos1 != pos2)
1321		goto fail_2_jhash2;
1322
1323	ret = 0;
1324
1325fail_2_jhash2:
1326	if (handle != NULL)
1327		rte_hash_free(handle);
1328
1329	return ret;
1330}
1331
1332static uint32_t
1333test_hash_jhash_1word(const void *key, uint32_t length, uint32_t initval)
1334{
1335	const uint32_t *k = key;
1336
1337	RTE_SET_USED(length);
1338
1339	return rte_jhash_1word(k[0], initval);
1340}
1341
1342static uint32_t
1343test_hash_jhash_2word(const void *key, uint32_t length, uint32_t initval)
1344{
1345	const uint32_t *k = key;
1346
1347	RTE_SET_USED(length);
1348
1349	return rte_jhash_2words(k[0], k[1], initval);
1350}
1351
1352static uint32_t
1353test_hash_jhash_3word(const void *key, uint32_t length, uint32_t initval)
1354{
1355	const uint32_t *k = key;
1356
1357	RTE_SET_USED(length);
1358
1359	return rte_jhash_3words(k[0], k[1], k[2], initval);
1360}
1361
1362/*
1363 * add/delete key with jhash 1word
1364 */
1365static int
1366test_hash_add_delete_jhash_1word(void)
1367{
1368	int ret = -1;
1369	struct rte_hash *handle;
1370	int32_t pos1, pos2;
1371
1372	hash_params_ex.name = "hash_test_jhash_1word";
1373	hash_params_ex.key_len = 4;
1374	hash_params_ex.hash_func = test_hash_jhash_1word;
1375
1376	handle = rte_hash_create(&hash_params_ex);
1377	if (handle == NULL)
1378		goto fail_jhash_1word;
1379
1380	pos1 = rte_hash_add_key(handle, (void *)&key[0]);
1381	if (pos1 < 0)
1382		goto fail_jhash_1word;
1383
1384	pos2 = rte_hash_del_key(handle, (void *)&key[0]);
1385	if (pos2 < 0 || pos1 != pos2)
1386		goto fail_jhash_1word;
1387
1388	ret = 0;
1389
1390fail_jhash_1word:
1391	if (handle != NULL)
1392		rte_hash_free(handle);
1393
1394	return ret;
1395}
1396
1397/*
1398 * add/delete key with jhash 2word
1399 */
1400static int
1401test_hash_add_delete_jhash_2word(void)
1402{
1403	int ret = -1;
1404	struct rte_hash *handle;
1405	int32_t pos1, pos2;
1406
1407	hash_params_ex.name = "hash_test_jhash_2word";
1408	hash_params_ex.key_len = 8;
1409	hash_params_ex.hash_func = test_hash_jhash_2word;
1410
1411	handle = rte_hash_create(&hash_params_ex);
1412	if (handle == NULL)
1413		goto fail_jhash_2word;
1414
1415	pos1 = rte_hash_add_key(handle, (void *)&key[0]);
1416	if (pos1 < 0)
1417		goto fail_jhash_2word;
1418
1419	pos2 = rte_hash_del_key(handle, (void *)&key[0]);
1420	if (pos2 < 0 || pos1 != pos2)
1421		goto fail_jhash_2word;
1422
1423	ret = 0;
1424
1425fail_jhash_2word:
1426	if (handle != NULL)
1427		rte_hash_free(handle);
1428
1429	return ret;
1430}
1431
1432/*
1433 * add/delete key with jhash 3word
1434 */
1435static int
1436test_hash_add_delete_jhash_3word(void)
1437{
1438	int ret = -1;
1439	struct rte_hash *handle;
1440	int32_t pos1, pos2;
1441
1442	hash_params_ex.name = "hash_test_jhash_3word";
1443	hash_params_ex.key_len = 12;
1444	hash_params_ex.hash_func = test_hash_jhash_3word;
1445
1446	handle = rte_hash_create(&hash_params_ex);
1447	if (handle == NULL)
1448		goto fail_jhash_3word;
1449
1450	pos1 = rte_hash_add_key(handle, (void *)&key[0]);
1451	if (pos1 < 0)
1452		goto fail_jhash_3word;
1453
1454	pos2 = rte_hash_del_key(handle, (void *)&key[0]);
1455	if (pos2 < 0 || pos1 != pos2)
1456		goto fail_jhash_3word;
1457
1458	ret = 0;
1459
1460fail_jhash_3word:
1461	if (handle != NULL)
1462		rte_hash_free(handle);
1463
1464	return ret;
1465}
1466
1467/*
1468 * Do all unit and performance tests.
1469 */
1470static int
1471test_hash(void)
1472{
1473	if (test_add_delete() < 0)
1474		return -1;
1475	if (test_hash_add_delete_jhash2() < 0)
1476		return -1;
1477	if (test_hash_add_delete_2_jhash2() < 0)
1478		return -1;
1479	if (test_hash_add_delete_jhash_1word() < 0)
1480		return -1;
1481	if (test_hash_add_delete_jhash_2word() < 0)
1482		return -1;
1483	if (test_hash_add_delete_jhash_3word() < 0)
1484		return -1;
1485	if (test_hash_get_key_with_position() < 0)
1486		return -1;
1487	if (test_hash_find_existing() < 0)
1488		return -1;
1489	if (test_add_update_delete() < 0)
1490		return -1;
1491	if (test_five_keys() < 0)
1492		return -1;
1493	if (test_full_bucket() < 0)
1494		return -1;
1495
1496	if (test_fbk_hash_find_existing() < 0)
1497		return -1;
1498	if (fbk_hash_unit_test() < 0)
1499		return -1;
1500	if (test_hash_creation_with_bad_parameters() < 0)
1501		return -1;
1502	if (test_hash_creation_with_good_parameters() < 0)
1503		return -1;
1504	if (test_average_table_utilization() < 0)
1505		return -1;
1506	if (test_hash_iteration() < 0)
1507		return -1;
1508
1509	run_hash_func_tests();
1510
1511	if (test_crc32_hash_alg_equiv() < 0)
1512		return -1;
1513
1514	return 0;
1515}
1516
1517REGISTER_TEST_COMMAND(hash_autotest, test_hash);
1518