xref: /dpdk/lib/table/rte_table_hash_cuckoo.c (revision 85e327081f0649c0965e53f675ac3b55eb8be6db)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2010-2017 Intel Corporation
399a2dd95SBruce Richardson  */
4e9fd1ebfSTyler Retzlaff 
5e9fd1ebfSTyler Retzlaff #include <stdalign.h>
699a2dd95SBruce Richardson #include <stdio.h>
7e9fd1ebfSTyler Retzlaff #include <string.h>
899a2dd95SBruce Richardson 
999a2dd95SBruce Richardson #include <rte_common.h>
1099a2dd95SBruce Richardson #include <rte_malloc.h>
1199a2dd95SBruce Richardson #include <rte_log.h>
1299a2dd95SBruce Richardson 
1399a2dd95SBruce Richardson #include "rte_table_hash_cuckoo.h"
1499a2dd95SBruce Richardson 
15ae67895bSDavid Marchand #include "table_log.h"
16ae67895bSDavid Marchand 
1799a2dd95SBruce Richardson #ifdef RTE_TABLE_STATS_COLLECT
1899a2dd95SBruce Richardson 
1999a2dd95SBruce Richardson #define RTE_TABLE_HASH_CUCKOO_STATS_PKTS_IN_ADD(table, val) \
2099a2dd95SBruce Richardson 	(table->stats.n_pkts_in += val)
2199a2dd95SBruce Richardson #define RTE_TABLE_HASH_CUCKOO_STATS_PKTS_LOOKUP_MISS(table, val) \
2299a2dd95SBruce Richardson 	(table->stats.n_pkts_lookup_miss += val)
2399a2dd95SBruce Richardson 
2499a2dd95SBruce Richardson #else
2599a2dd95SBruce Richardson 
2699a2dd95SBruce Richardson #define RTE_TABLE_HASH_CUCKOO_STATS_PKTS_IN_ADD(table, val)
2799a2dd95SBruce Richardson #define RTE_TABLE_HASH_CUCKOO_STATS_PKTS_LOOKUP_MISS(table, val)
2899a2dd95SBruce Richardson 
2999a2dd95SBruce Richardson #endif
3099a2dd95SBruce Richardson 
3199a2dd95SBruce Richardson 
3299a2dd95SBruce Richardson struct rte_table_hash {
3399a2dd95SBruce Richardson 	struct rte_table_stats stats;
3499a2dd95SBruce Richardson 
3599a2dd95SBruce Richardson 	/* Input parameters */
3699a2dd95SBruce Richardson 	uint32_t key_size;
3799a2dd95SBruce Richardson 	uint32_t entry_size;
3899a2dd95SBruce Richardson 	uint32_t n_keys;
3999a2dd95SBruce Richardson 	rte_hash_function f_hash;
4099a2dd95SBruce Richardson 	uint32_t seed;
4199a2dd95SBruce Richardson 	uint32_t key_offset;
4299a2dd95SBruce Richardson 
4399a2dd95SBruce Richardson 	/* cuckoo hash table object */
4499a2dd95SBruce Richardson 	struct rte_hash *h_table;
4599a2dd95SBruce Richardson 
4699a2dd95SBruce Richardson 	/* Lookup table */
47*85e32708STyler Retzlaff 	alignas(RTE_CACHE_LINE_SIZE) uint8_t memory[];
4899a2dd95SBruce Richardson };
4999a2dd95SBruce Richardson 
5099a2dd95SBruce Richardson static int
check_params_create_hash_cuckoo(struct rte_table_hash_cuckoo_params * params)5199a2dd95SBruce Richardson check_params_create_hash_cuckoo(struct rte_table_hash_cuckoo_params *params)
5299a2dd95SBruce Richardson {
5399a2dd95SBruce Richardson 	if (params == NULL) {
54ae67895bSDavid Marchand 		TABLE_LOG(ERR, "NULL Input Parameters.");
5599a2dd95SBruce Richardson 		return -EINVAL;
5699a2dd95SBruce Richardson 	}
5799a2dd95SBruce Richardson 
5899a2dd95SBruce Richardson 	if (params->name == NULL) {
59ae67895bSDavid Marchand 		TABLE_LOG(ERR, "Table name is NULL.");
6099a2dd95SBruce Richardson 		return -EINVAL;
6199a2dd95SBruce Richardson 	}
6299a2dd95SBruce Richardson 
6399a2dd95SBruce Richardson 	if (params->key_size == 0) {
64ae67895bSDavid Marchand 		TABLE_LOG(ERR, "Invalid key_size.");
6599a2dd95SBruce Richardson 		return -EINVAL;
6699a2dd95SBruce Richardson 	}
6799a2dd95SBruce Richardson 
6899a2dd95SBruce Richardson 	if (params->n_keys == 0) {
69ae67895bSDavid Marchand 		TABLE_LOG(ERR, "Invalid n_keys.");
7099a2dd95SBruce Richardson 		return -EINVAL;
7199a2dd95SBruce Richardson 	}
7299a2dd95SBruce Richardson 
7399a2dd95SBruce Richardson 	if (params->f_hash == NULL) {
74ae67895bSDavid Marchand 		TABLE_LOG(ERR, "f_hash is NULL.");
7599a2dd95SBruce Richardson 		return -EINVAL;
7699a2dd95SBruce Richardson 	}
7799a2dd95SBruce Richardson 
7899a2dd95SBruce Richardson 	return 0;
7999a2dd95SBruce Richardson }
8099a2dd95SBruce Richardson 
8199a2dd95SBruce Richardson static void *
rte_table_hash_cuckoo_create(void * params,int socket_id,uint32_t entry_size)8299a2dd95SBruce Richardson rte_table_hash_cuckoo_create(void *params,
8399a2dd95SBruce Richardson 			int socket_id,
8499a2dd95SBruce Richardson 			uint32_t entry_size)
8599a2dd95SBruce Richardson {
8699a2dd95SBruce Richardson 	struct rte_table_hash_cuckoo_params *p = params;
8799a2dd95SBruce Richardson 	struct rte_hash *h_table;
8899a2dd95SBruce Richardson 	struct rte_table_hash *t;
8999a2dd95SBruce Richardson 	uint32_t total_size;
9099a2dd95SBruce Richardson 
9199a2dd95SBruce Richardson 	/* Check input parameters */
9299a2dd95SBruce Richardson 	if (check_params_create_hash_cuckoo(params))
9399a2dd95SBruce Richardson 		return NULL;
9499a2dd95SBruce Richardson 
9599a2dd95SBruce Richardson 	/* Memory allocation */
9699a2dd95SBruce Richardson 	total_size = sizeof(struct rte_table_hash) +
9799a2dd95SBruce Richardson 		RTE_CACHE_LINE_ROUNDUP(p->n_keys * entry_size);
9899a2dd95SBruce Richardson 
9999a2dd95SBruce Richardson 	t = rte_zmalloc_socket(p->name, total_size, RTE_CACHE_LINE_SIZE, socket_id);
10099a2dd95SBruce Richardson 	if (t == NULL) {
101ae67895bSDavid Marchand 		TABLE_LOG(ERR,
102ae67895bSDavid Marchand 			"%s: Cannot allocate %u bytes for cuckoo hash table %s",
10399a2dd95SBruce Richardson 			__func__, total_size, p->name);
10499a2dd95SBruce Richardson 		return NULL;
10599a2dd95SBruce Richardson 	}
10699a2dd95SBruce Richardson 
10799a2dd95SBruce Richardson 	/* Create cuckoo hash table */
10899a2dd95SBruce Richardson 	struct rte_hash_parameters hash_cuckoo_params = {
10999a2dd95SBruce Richardson 		.entries = p->n_keys,
11099a2dd95SBruce Richardson 		.key_len = p->key_size,
11199a2dd95SBruce Richardson 		.hash_func = p->f_hash,
11299a2dd95SBruce Richardson 		.hash_func_init_val = p->seed,
11399a2dd95SBruce Richardson 		.socket_id = socket_id,
11499a2dd95SBruce Richardson 		.name = p->name
11599a2dd95SBruce Richardson 	};
11699a2dd95SBruce Richardson 
11799a2dd95SBruce Richardson 	h_table = rte_hash_find_existing(p->name);
11899a2dd95SBruce Richardson 	if (h_table == NULL) {
11999a2dd95SBruce Richardson 		h_table = rte_hash_create(&hash_cuckoo_params);
12099a2dd95SBruce Richardson 		if (h_table == NULL) {
121ae67895bSDavid Marchand 			TABLE_LOG(ERR,
122ae67895bSDavid Marchand 				"%s: failed to create cuckoo hash table %s",
12399a2dd95SBruce Richardson 				__func__, p->name);
12499a2dd95SBruce Richardson 			rte_free(t);
12599a2dd95SBruce Richardson 			return NULL;
12699a2dd95SBruce Richardson 		}
12799a2dd95SBruce Richardson 	}
12899a2dd95SBruce Richardson 
12999a2dd95SBruce Richardson 	/* initialize the cuckoo hash parameters */
13099a2dd95SBruce Richardson 	t->key_size = p->key_size;
13199a2dd95SBruce Richardson 	t->entry_size = entry_size;
13299a2dd95SBruce Richardson 	t->n_keys = p->n_keys;
13399a2dd95SBruce Richardson 	t->f_hash = p->f_hash;
13499a2dd95SBruce Richardson 	t->seed = p->seed;
13599a2dd95SBruce Richardson 	t->key_offset = p->key_offset;
13699a2dd95SBruce Richardson 	t->h_table = h_table;
13799a2dd95SBruce Richardson 
138ae67895bSDavid Marchand 	TABLE_LOG(INFO,
139ae67895bSDavid Marchand 		"%s: Cuckoo hash table %s memory footprint is %u bytes",
14099a2dd95SBruce Richardson 		__func__, p->name, total_size);
14199a2dd95SBruce Richardson 	return t;
14299a2dd95SBruce Richardson }
14399a2dd95SBruce Richardson 
14499a2dd95SBruce Richardson static int
rte_table_hash_cuckoo_free(void * table)14599a2dd95SBruce Richardson rte_table_hash_cuckoo_free(void *table) {
14699a2dd95SBruce Richardson 	struct rte_table_hash *t = table;
14799a2dd95SBruce Richardson 
14899a2dd95SBruce Richardson 	if (table == NULL)
14999a2dd95SBruce Richardson 		return -EINVAL;
15099a2dd95SBruce Richardson 
15199a2dd95SBruce Richardson 	rte_hash_free(t->h_table);
15299a2dd95SBruce Richardson 	rte_free(t);
15399a2dd95SBruce Richardson 
15499a2dd95SBruce Richardson 	return 0;
15599a2dd95SBruce Richardson }
15699a2dd95SBruce Richardson 
15799a2dd95SBruce Richardson static int
rte_table_hash_cuckoo_entry_add(void * table,void * key,void * entry,int * key_found,void ** entry_ptr)15899a2dd95SBruce Richardson rte_table_hash_cuckoo_entry_add(void *table, void *key, void *entry,
15999a2dd95SBruce Richardson 	int *key_found, void **entry_ptr)
16099a2dd95SBruce Richardson {
16199a2dd95SBruce Richardson 	struct rte_table_hash *t = table;
16299a2dd95SBruce Richardson 	int pos = 0;
16399a2dd95SBruce Richardson 
16499a2dd95SBruce Richardson 	/* Check input parameters */
16599a2dd95SBruce Richardson 	if ((table == NULL) ||
16699a2dd95SBruce Richardson 		(key == NULL) ||
16799a2dd95SBruce Richardson 		(entry == NULL) ||
16899a2dd95SBruce Richardson 		(key_found == NULL) ||
16999a2dd95SBruce Richardson 		(entry_ptr == NULL))
17099a2dd95SBruce Richardson 		return -EINVAL;
17199a2dd95SBruce Richardson 
17299a2dd95SBruce Richardson 	/*  Find Existing entries */
17399a2dd95SBruce Richardson 	pos = rte_hash_lookup(t->h_table, key);
17499a2dd95SBruce Richardson 	if (pos >= 0) {
17599a2dd95SBruce Richardson 		uint8_t *existing_entry;
17699a2dd95SBruce Richardson 
17799a2dd95SBruce Richardson 		*key_found = 1;
17899a2dd95SBruce Richardson 		existing_entry = &t->memory[pos * t->entry_size];
17999a2dd95SBruce Richardson 		memcpy(existing_entry, entry, t->entry_size);
18099a2dd95SBruce Richardson 		*entry_ptr = existing_entry;
18199a2dd95SBruce Richardson 
18299a2dd95SBruce Richardson 		return 0;
18399a2dd95SBruce Richardson 	}
18499a2dd95SBruce Richardson 
18599a2dd95SBruce Richardson 	if (pos == -ENOENT) {
18699a2dd95SBruce Richardson 		/* Entry not found. Adding new entry */
18799a2dd95SBruce Richardson 		uint8_t *new_entry;
18899a2dd95SBruce Richardson 
18999a2dd95SBruce Richardson 		pos = rte_hash_add_key(t->h_table, key);
19099a2dd95SBruce Richardson 		if (pos < 0)
19199a2dd95SBruce Richardson 			return pos;
19299a2dd95SBruce Richardson 
19399a2dd95SBruce Richardson 		new_entry = &t->memory[pos * t->entry_size];
19499a2dd95SBruce Richardson 		memcpy(new_entry, entry, t->entry_size);
19599a2dd95SBruce Richardson 
19699a2dd95SBruce Richardson 		*key_found = 0;
19799a2dd95SBruce Richardson 		*entry_ptr = new_entry;
19899a2dd95SBruce Richardson 		return 0;
19999a2dd95SBruce Richardson 	}
20099a2dd95SBruce Richardson 
20199a2dd95SBruce Richardson 	return pos;
20299a2dd95SBruce Richardson }
20399a2dd95SBruce Richardson 
20499a2dd95SBruce Richardson static int
rte_table_hash_cuckoo_entry_delete(void * table,void * key,int * key_found,void * entry)20599a2dd95SBruce Richardson rte_table_hash_cuckoo_entry_delete(void *table, void *key,
20699a2dd95SBruce Richardson 	int *key_found, void *entry)
20799a2dd95SBruce Richardson {
20899a2dd95SBruce Richardson 	struct rte_table_hash *t = table;
20999a2dd95SBruce Richardson 	int pos = 0;
21099a2dd95SBruce Richardson 
21199a2dd95SBruce Richardson 	/* Check input parameters */
21299a2dd95SBruce Richardson 	if ((table == NULL) ||
21399a2dd95SBruce Richardson 		(key == NULL) ||
21499a2dd95SBruce Richardson 		(key_found == NULL))
21599a2dd95SBruce Richardson 		return -EINVAL;
21699a2dd95SBruce Richardson 
21799a2dd95SBruce Richardson 	pos = rte_hash_del_key(t->h_table, key);
21899a2dd95SBruce Richardson 	if (pos >= 0) {
21999a2dd95SBruce Richardson 		*key_found = 1;
22099a2dd95SBruce Richardson 		uint8_t *entry_ptr = &t->memory[pos * t->entry_size];
22199a2dd95SBruce Richardson 
22299a2dd95SBruce Richardson 		if (entry)
22399a2dd95SBruce Richardson 			memcpy(entry, entry_ptr, t->entry_size);
22499a2dd95SBruce Richardson 
22599a2dd95SBruce Richardson 		memset(&t->memory[pos * t->entry_size], 0, t->entry_size);
22699a2dd95SBruce Richardson 		return 0;
22799a2dd95SBruce Richardson 	}
22899a2dd95SBruce Richardson 
22999a2dd95SBruce Richardson 	*key_found = 0;
23099a2dd95SBruce Richardson 	return pos;
23199a2dd95SBruce Richardson }
23299a2dd95SBruce Richardson 
23399a2dd95SBruce Richardson static int
rte_table_hash_cuckoo_lookup(void * table,struct rte_mbuf ** pkts,uint64_t pkts_mask,uint64_t * lookup_hit_mask,void ** entries)23499a2dd95SBruce Richardson rte_table_hash_cuckoo_lookup(void *table,
23599a2dd95SBruce Richardson 	struct rte_mbuf **pkts,
23699a2dd95SBruce Richardson 	uint64_t pkts_mask,
23799a2dd95SBruce Richardson 	uint64_t *lookup_hit_mask,
23899a2dd95SBruce Richardson 	void **entries)
23999a2dd95SBruce Richardson {
24099a2dd95SBruce Richardson 	struct rte_table_hash *t = table;
24199a2dd95SBruce Richardson 	uint64_t pkts_mask_out = 0;
24299a2dd95SBruce Richardson 	uint32_t i;
24399a2dd95SBruce Richardson 
2443d4e27fdSDavid Marchand 	__rte_unused uint32_t n_pkts_in = rte_popcount64(pkts_mask);
24599a2dd95SBruce Richardson 
24699a2dd95SBruce Richardson 	RTE_TABLE_HASH_CUCKOO_STATS_PKTS_IN_ADD(t, n_pkts_in);
24799a2dd95SBruce Richardson 
24899a2dd95SBruce Richardson 	if ((pkts_mask & (pkts_mask + 1)) == 0) {
24999a2dd95SBruce Richardson 		const uint8_t *keys[RTE_PORT_IN_BURST_SIZE_MAX];
25099a2dd95SBruce Richardson 		int32_t positions[RTE_PORT_IN_BURST_SIZE_MAX], status;
25199a2dd95SBruce Richardson 
25299a2dd95SBruce Richardson 		/* Keys for bulk lookup */
25399a2dd95SBruce Richardson 		for (i = 0; i < n_pkts_in; i++)
25499a2dd95SBruce Richardson 			keys[i] = RTE_MBUF_METADATA_UINT8_PTR(pkts[i],
25599a2dd95SBruce Richardson 				t->key_offset);
25699a2dd95SBruce Richardson 
25799a2dd95SBruce Richardson 		/* Bulk Lookup */
25899a2dd95SBruce Richardson 		status = rte_hash_lookup_bulk(t->h_table,
25999a2dd95SBruce Richardson 				(const void **) keys,
26099a2dd95SBruce Richardson 				n_pkts_in,
26199a2dd95SBruce Richardson 				positions);
26299a2dd95SBruce Richardson 		if (status == 0) {
26399a2dd95SBruce Richardson 			for (i = 0; i < n_pkts_in; i++) {
26499a2dd95SBruce Richardson 				if (likely(positions[i] >= 0)) {
26599a2dd95SBruce Richardson 					uint64_t pkt_mask = 1LLU << i;
26699a2dd95SBruce Richardson 
26799a2dd95SBruce Richardson 					entries[i] = &t->memory[positions[i]
26899a2dd95SBruce Richardson 						* t->entry_size];
26999a2dd95SBruce Richardson 					pkts_mask_out |= pkt_mask;
27099a2dd95SBruce Richardson 				}
27199a2dd95SBruce Richardson 			}
27299a2dd95SBruce Richardson 		}
27399a2dd95SBruce Richardson 	} else
27499a2dd95SBruce Richardson 		for (i = 0; i < (uint32_t)(RTE_PORT_IN_BURST_SIZE_MAX
2753d4e27fdSDavid Marchand 					- rte_clz64(pkts_mask)); i++) {
27699a2dd95SBruce Richardson 			uint64_t pkt_mask = 1LLU << i;
27799a2dd95SBruce Richardson 
27899a2dd95SBruce Richardson 			if (pkt_mask & pkts_mask) {
27999a2dd95SBruce Richardson 				struct rte_mbuf *pkt = pkts[i];
28099a2dd95SBruce Richardson 				uint8_t *key = RTE_MBUF_METADATA_UINT8_PTR(pkt,
28199a2dd95SBruce Richardson 						t->key_offset);
28299a2dd95SBruce Richardson 				int pos;
28399a2dd95SBruce Richardson 
28499a2dd95SBruce Richardson 				pos = rte_hash_lookup(t->h_table, key);
28599a2dd95SBruce Richardson 				if (likely(pos >= 0)) {
28699a2dd95SBruce Richardson 					entries[i] = &t->memory[pos
28799a2dd95SBruce Richardson 						* t->entry_size];
28899a2dd95SBruce Richardson 					pkts_mask_out |= pkt_mask;
28999a2dd95SBruce Richardson 				}
29099a2dd95SBruce Richardson 			}
29199a2dd95SBruce Richardson 		}
29299a2dd95SBruce Richardson 
29399a2dd95SBruce Richardson 	*lookup_hit_mask = pkts_mask_out;
29499a2dd95SBruce Richardson 	RTE_TABLE_HASH_CUCKOO_STATS_PKTS_LOOKUP_MISS(t,
2953d4e27fdSDavid Marchand 			n_pkts_in - rte_popcount64(pkts_mask_out));
29699a2dd95SBruce Richardson 
29799a2dd95SBruce Richardson 	return 0;
29899a2dd95SBruce Richardson 
29999a2dd95SBruce Richardson }
30099a2dd95SBruce Richardson 
30199a2dd95SBruce Richardson static int
rte_table_hash_cuckoo_stats_read(void * table,struct rte_table_stats * stats,int clear)30299a2dd95SBruce Richardson rte_table_hash_cuckoo_stats_read(void *table, struct rte_table_stats *stats,
30399a2dd95SBruce Richardson 	int clear)
30499a2dd95SBruce Richardson {
30599a2dd95SBruce Richardson 	struct rte_table_hash *t = table;
30699a2dd95SBruce Richardson 
30799a2dd95SBruce Richardson 	if (stats != NULL)
30899a2dd95SBruce Richardson 		memcpy(stats, &t->stats, sizeof(t->stats));
30999a2dd95SBruce Richardson 
31099a2dd95SBruce Richardson 	if (clear)
31199a2dd95SBruce Richardson 		memset(&t->stats, 0, sizeof(t->stats));
31299a2dd95SBruce Richardson 
31399a2dd95SBruce Richardson 	return 0;
31499a2dd95SBruce Richardson }
31599a2dd95SBruce Richardson 
31699a2dd95SBruce Richardson struct rte_table_ops rte_table_hash_cuckoo_ops = {
31799a2dd95SBruce Richardson 	.f_create = rte_table_hash_cuckoo_create,
31899a2dd95SBruce Richardson 	.f_free = rte_table_hash_cuckoo_free,
31999a2dd95SBruce Richardson 	.f_add = rte_table_hash_cuckoo_entry_add,
32099a2dd95SBruce Richardson 	.f_delete = rte_table_hash_cuckoo_entry_delete,
32199a2dd95SBruce Richardson 	.f_add_bulk = NULL,
32299a2dd95SBruce Richardson 	.f_delete_bulk = NULL,
32399a2dd95SBruce Richardson 	.f_lookup = rte_table_hash_cuckoo_lookup,
32499a2dd95SBruce Richardson 	.f_stats = rte_table_hash_cuckoo_stats_read,
32599a2dd95SBruce Richardson };
326