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