diff options
| author | Bruce Richardson <[email protected]> | 2021-04-20 11:22:30 +0100 |
|---|---|---|
| committer | Thomas Monjalon <[email protected]> | 2021-04-21 14:04:09 +0200 |
| commit | 99a2dd955fba6e4cc23b77d590a033650ced9c45 (patch) | |
| tree | dfe74706c3149112b190413097ff86bdc53db58f /lib/table | |
| parent | f2cdd95f2d3f09ed84d33ba62d275e590b10fd67 (diff) | |
lib: remove librte_ prefix from directory names
There is no reason for the DPDK libraries to all have 'librte_' prefix on
the directory names. This prefix makes the directory names longer and also
makes it awkward to add features referring to individual libraries in the
build - should the lib names be specified with or without the prefix.
Therefore, we can just remove the library prefix and use the library's
unique name as the directory name, i.e. 'eal' rather than 'librte_eal'
Signed-off-by: Bruce Richardson <[email protected]>
Diffstat (limited to 'lib/table')
31 files changed, 10731 insertions, 0 deletions
diff --git a/lib/table/meson.build b/lib/table/meson.build new file mode 100644 index 0000000000..230f21ea8e --- /dev/null +++ b/lib/table/meson.build @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +sources = files( + 'rte_swx_table_em.c', + 'rte_swx_table_wm.c', + 'rte_table_acl.c', + 'rte_table_array.c', + 'rte_table_hash_cuckoo.c', + 'rte_table_hash_ext.c', + 'rte_table_hash_key8.c', + 'rte_table_hash_key16.c', + 'rte_table_hash_key32.c', + 'rte_table_hash_lru.c', + 'rte_table_lpm.c', + 'rte_table_lpm_ipv6.c', + 'rte_table_stub.c', +) +headers = files( + 'rte_lru.h', + 'rte_swx_table.h', + 'rte_swx_table_em.h', + 'rte_swx_table_wm.h', + 'rte_table.h', + 'rte_table_acl.h', + 'rte_table_array.h', + 'rte_table_hash.h', + 'rte_table_hash_cuckoo.h', + 'rte_table_hash_func.h', + 'rte_table_lpm.h', + 'rte_table_lpm_ipv6.h', + 'rte_table_stub.h', +) +deps += ['mbuf', 'port', 'lpm', 'hash', 'acl'] + +indirect_headers += files( + 'rte_lru_arm64.h', + 'rte_lru_x86.h', + 'rte_table_hash_func_arm64.h' +) diff --git a/lib/table/rte_lru.h b/lib/table/rte_lru.h new file mode 100644 index 0000000000..88229d8632 --- /dev/null +++ b/lib/table/rte_lru.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_LRU_H__ +#define __INCLUDE_RTE_LRU_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_config.h> +#ifdef RTE_ARCH_X86_64 +#include "rte_lru_x86.h" +#elif defined(RTE_ARCH_ARM64) +#include "rte_lru_arm64.h" +#else +#undef RTE_TABLE_HASH_LRU_STRATEGY +#define RTE_TABLE_HASH_LRU_STRATEGY 1 +#endif + +#if RTE_TABLE_HASH_LRU_STRATEGY == 0 + +#define lru_init(bucket) \ +do \ + bucket = bucket; \ +while (0) + +#define lru_pos(bucket) (bucket->lru_list & 0xFFFFLLU) + +#define lru_update(bucket, mru_val) \ +do { \ + bucket = bucket; \ + mru_val = mru_val; \ +} while (0) + +#elif RTE_TABLE_HASH_LRU_STRATEGY == 1 + +#define lru_init(bucket) \ +do \ + bucket->lru_list = 0x0000000100020003LLU; \ +while (0) + +#define lru_pos(bucket) (bucket->lru_list & 0xFFFFLLU) + +#define lru_update(bucket, mru_val) \ +do { \ + uint64_t x, pos, x0, x1, x2, mask; \ + \ + x = bucket->lru_list; \ + \ + pos = 4; \ + if ((x >> 48) == ((uint64_t) mru_val)) \ + pos = 3; \ + \ + if (((x >> 32) & 0xFFFFLLU) == ((uint64_t) mru_val)) \ + pos = 2; \ + \ + if (((x >> 16) & 0xFFFFLLU) == ((uint64_t) mru_val)) \ + pos = 1; \ + \ + if ((x & 0xFFFFLLU) == ((uint64_t) mru_val)) \ + pos = 0; \ + \ + \ + pos <<= 4; \ + mask = (~0LLU) << pos; \ + x0 = x & (~mask); \ + x1 = (x >> 16) & mask; \ + x2 = (x << (48 - pos)) & (0xFFFFLLU << 48); \ + x = x0 | x1 | x2; \ + \ + if (pos != 64) \ + bucket->lru_list = x; \ +} while (0) + +#elif (RTE_TABLE_HASH_LRU_STRATEGY == 2) || (RTE_TABLE_HASH_LRU_STRATEGY == 3) + +/** + * These strategies are implemented in architecture specific header files. + */ + +#else + +#error "Incorrect value for RTE_TABLE_HASH_LRU_STRATEGY" + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_lru_arm64.h b/lib/table/rte_lru_arm64.h new file mode 100644 index 0000000000..add889a57e --- /dev/null +++ b/lib/table/rte_lru_arm64.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Cavium, Inc + */ + +#ifndef __RTE_LRU_ARM64_H__ +#define __RTE_LRU_ARM64_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <rte_vect.h> + +#ifndef RTE_TABLE_HASH_LRU_STRATEGY +#ifdef __ARM_NEON +#define RTE_TABLE_HASH_LRU_STRATEGY 3 +#else /* if no NEON, use simple scalar version */ +#define RTE_TABLE_HASH_LRU_STRATEGY 1 +#endif +#endif + +#if RTE_TABLE_HASH_LRU_STRATEGY == 3 + +#define lru_init(bucket) \ + { bucket->lru_list = ~0LLU; } + +static inline int +f_lru_pos(uint64_t lru_list) +{ + /* Compare the vector to zero vector */ + uint16x4_t lru_vec = vld1_u16((uint16_t *)&lru_list); + uint16x4_t min_vec = vmov_n_u16(vminv_u16(lru_vec)); + uint64_t mask = vget_lane_u64(vreinterpret_u64_u16( + vceq_u16(min_vec, lru_vec)), 0); + return __builtin_clzl(mask) >> 4; +} +#define lru_pos(bucket) f_lru_pos(bucket->lru_list) + +#define lru_update(bucket, mru_val) \ +do { \ + const uint64_t orvals[] = {0xFFFFLLU, 0xFFFFLLU << 16, \ + 0xFFFFLLU << 32, 0xFFFFLLU << 48, 0LLU}; \ + const uint64_t decs[] = {0x1000100010001LLU, 0}; \ + uint64x1_t lru = vdup_n_u64(bucket->lru_list); \ + uint64x1_t vdec = vdup_n_u64(decs[mru_val>>2]); \ + bucket->lru_list = vget_lane_u64(vreinterpret_u64_u16( \ + vsub_u16(vreinterpret_u16_u64(lru), \ + vreinterpret_u16_u64(vdec))), \ + 0); \ + bucket->lru_list |= orvals[mru_val]; \ +} while (0) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_lru_x86.h b/lib/table/rte_lru_x86.h new file mode 100644 index 0000000000..38476d956e --- /dev/null +++ b/lib/table/rte_lru_x86.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_LRU_X86_H__ +#define __INCLUDE_RTE_LRU_X86_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +#include <rte_config.h> +#include <rte_common.h> + +#ifndef RTE_TABLE_HASH_LRU_STRATEGY +#define RTE_TABLE_HASH_LRU_STRATEGY 2 +#endif + +#if RTE_TABLE_HASH_LRU_STRATEGY == 2 + +#if RTE_CC_IS_GNU && (GCC_VERSION > 40306) +#include <x86intrin.h> +#else +#include <emmintrin.h> +#include <smmintrin.h> +#include <xmmintrin.h> +#endif + +#define lru_init(bucket) \ + { bucket->lru_list = 0x0000000100020003LLU; } + +#define lru_pos(bucket) (bucket->lru_list & 0xFFFFLLU) + +#define lru_update(bucket, mru_val) \ +do { \ + /* set up the masks for all possible shuffles, depends on pos */\ + static uint64_t masks[10] = { \ + /* Shuffle order; Make Zero (see _mm_shuffle_epi8 manual) */\ + 0x0100070605040302, 0x8080808080808080, \ + 0x0302070605040100, 0x8080808080808080, \ + 0x0504070603020100, 0x8080808080808080, \ + 0x0706050403020100, 0x8080808080808080, \ + 0x0706050403020100, 0x8080808080808080}; \ + /* load up one register with repeats of mru-val */ \ + uint64_t mru2 = mru_val; \ + uint64_t mru3 = mru2 | (mru2 << 16); \ + uint64_t lru = bucket->lru_list; \ + /* XOR to cause the word we're looking for to go to zero */ \ + uint64_t mru = lru ^ ((mru3 << 32) | mru3); \ + __m128i c = _mm_cvtsi64_si128(mru); \ + __m128i b = _mm_cvtsi64_si128(lru); \ + /* Find the minimum value (first zero word, if it's in there) */\ + __m128i d = _mm_minpos_epu16(c); \ + /* Second word is the index to found word (first word is the value) */\ + unsigned int pos = _mm_extract_epi16(d, 1); \ + /* move the recently used location to top of list */ \ + __m128i k = _mm_shuffle_epi8(b, *((__m128i *) &masks[2 * pos]));\ + /* Finally, update the original list with the reordered data */ \ + bucket->lru_list = _mm_extract_epi64(k, 0); \ + /* Phwew! */ \ +} while (0) + +#elif RTE_TABLE_HASH_LRU_STRATEGY == 3 + +#if RTE_CC_IS_GNU && (GCC_VERSION > 40306) +#include <x86intrin.h> +#else +#include <emmintrin.h> +#include <smmintrin.h> +#include <xmmintrin.h> +#endif + +#define lru_init(bucket) \ + { bucket->lru_list = ~0LLU; } + +static inline int +f_lru_pos(uint64_t lru_list) +{ + __m128i lst = _mm_set_epi64x((uint64_t)-1, lru_list); + __m128i min = _mm_minpos_epu16(lst); + return _mm_extract_epi16(min, 1); +} +#define lru_pos(bucket) f_lru_pos(bucket->lru_list) + +#define lru_update(bucket, mru_val) \ +do { \ + const uint64_t orvals[] = {0xFFFFLLU, 0xFFFFLLU << 16, \ + 0xFFFFLLU << 32, 0xFFFFLLU << 48, 0LLU}; \ + const uint64_t decs[] = {0x1000100010001LLU, 0}; \ + __m128i lru = _mm_cvtsi64_si128(bucket->lru_list); \ + __m128i vdec = _mm_cvtsi64_si128(decs[mru_val>>2]); \ + lru = _mm_subs_epu16(lru, vdec); \ + bucket->lru_list = _mm_extract_epi64(lru, 0) | orvals[mru_val]; \ +} while (0) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_swx_table.h b/lib/table/rte_swx_table.h new file mode 100644 index 0000000000..e23f2304c6 --- /dev/null +++ b/lib/table/rte_swx_table.h @@ -0,0 +1,299 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ +#ifndef __INCLUDE_RTE_SWX_TABLE_H__ +#define __INCLUDE_RTE_SWX_TABLE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE SWX Table + * + * Table interface. + */ + +#include <stdint.h> +#include <sys/queue.h> + +/** Match type. */ +enum rte_swx_table_match_type { + /** Wildcard Match (WM). */ + RTE_SWX_TABLE_MATCH_WILDCARD, + + /** Longest Prefix Match (LPM). */ + RTE_SWX_TABLE_MATCH_LPM, + + /** Exact Match (EM). */ + RTE_SWX_TABLE_MATCH_EXACT, +}; + +/** Table creation parameters. */ +struct rte_swx_table_params { + /** Table match type. */ + enum rte_swx_table_match_type match_type; + + /** Key size in bytes. */ + uint32_t key_size; + + /** Offset of the first byte of the key within the key buffer. */ + uint32_t key_offset; + + /** Mask of *key_size* bytes logically laid over the bytes at positions + * *key_offset* .. (*key_offset* + *key_size* - 1) of the key buffer in + * order to specify which bits from the key buffer are part of the key + * and which ones are not. A bit value of 1 in the *key_mask0* means the + * respective bit in the key buffer is part of the key, while a bit + * value of 0 means the opposite. A NULL value means that all the bits + * are part of the key, i.e. the *key_mask0* is an all-ones mask. + */ + uint8_t *key_mask0; + + /** Maximum size (in bytes) of the action data. The data stored in the + * table for each entry is equal to *action_data_size* plus 8 bytes, + * which are used to store the action ID. + */ + uint32_t action_data_size; + + /** Maximum number of keys to be stored in the table together with their + * associated data. + */ + uint32_t n_keys_max; +}; + +/** Table entry. */ +struct rte_swx_table_entry { + /** Used to facilitate the membership of this table entry to a + * linked list. + */ + TAILQ_ENTRY(rte_swx_table_entry) node; + + /** Key value for the current entry. Array of *key_size* bytes or NULL + * if the *key_size* for the current table is 0. + */ + uint8_t *key; + + /** Key mask for the current entry. Array of *key_size* bytes that is + * logically and'ed with *key_mask0* of the current table. A NULL value + * means that all the key bits already enabled by *key_mask0* are part + * of the key of the current entry. + */ + uint8_t *key_mask; + + /** Placeholder for a possible compressed version of the *key* and + * *key_mask* of the current entry. Typically a hash signature, its main + * purpose is to the linked list search operation. Should be ignored by + * the API functions below. + */ + uint64_t key_signature; + + /** Key priority for the current entry. Useful for wildcard match (as + * match rules are commonly overlapping with other rules), ignored for + * exact match (as match rules never overlap, hence all rules have the + * same match priority) and for LPM (match priority is driven by the + * prefix length, with non-overlapping prefixes essentially having the + * same match priority). Value 0 indicates the highest match priority. + */ + uint32_t key_priority; + + /** Action ID for the current entry. */ + uint64_t action_id; + + /** Action data for the current entry. Considering S as the action data + * size of the *action_id* action, which must be less than or equal to + * the table *action_data_size*, the *action_data* field must point to + * an array of S bytes when S is non-zero. The *action_data* field is + * ignored when S is zero. + */ + uint8_t *action_data; +}; + +/** List of table entries. */ +TAILQ_HEAD(rte_swx_table_entry_list, rte_swx_table_entry); + +/** + * Table memory footprint get + * + * @param[in] params + * Table create parameters. + * @param[in] entries + * Table entries. + * @param[in] args + * Any additional table create arguments. It may be NULL. + * @return + * Table memory footprint in bytes, if successful, or zero, on error. + */ +typedef uint64_t +(*rte_swx_table_footprint_get_t)(struct rte_swx_table_params *params, + struct rte_swx_table_entry_list *entries, + const char *args); + +/** + * Table mailbox size get + * + * The mailbox is used to store the context of a lookup operation that is in + * progress and it is passed as a parameter to the lookup operation. This allows + * for multiple concurrent lookup operations into the same table. + * + * @return + * Table memory footprint in bytes, on success, or zero, on error. + */ +typedef uint64_t +(*rte_swx_table_mailbox_size_get_t)(void); + +/** + * Table create + * + * @param[in] params + * Table creation parameters. + * @param[in] entries + * Entries to be added to the table at creation time. + * @param[in] args + * Any additional table create arguments. It may be NULL. + * @param[in] numa_node + * Non-Uniform Memory Access (NUMA) node. + * @return + * Table handle, on success, or NULL, on error. + */ +typedef void * +(*rte_swx_table_create_t)(struct rte_swx_table_params *params, + struct rte_swx_table_entry_list *entries, + const char *args, + int numa_node); + +/** + * Table entry add + * + * @param[in] table + * Table handle. + * @param[in] entry + * Entry to be added to the table. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid table handle, entry or entry field; + * -ENOSPC: Table full. + */ +typedef int +(*rte_swx_table_add_t)(void *table, + struct rte_swx_table_entry *entry); + +/** + * Table entry delete + * + * @param[in] table + * Table handle. + * @param[in] entry + * Entry to be deleted from the table. The entry *action_id* and *action_data* + * fields are ignored. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid table handle, entry or entry field; + * -ENOSPC: Table full. + */ +typedef int +(*rte_swx_table_delete_t)(void *table, + struct rte_swx_table_entry *entry); + +/** + * Table lookup + * + * The table lookup operation searches a given key in the table and upon its + * completion it returns an indication of whether the key is found in the table + * (lookup hit) or not (lookup miss). In case of lookup hit, the action_id and + * the action_data associated with the key are also returned. + * + * Multiple invocations of this function may be required in order to complete a + * single table lookup operation for a given table and a given lookup key. The + * completion of the table lookup operation is flagged by a return value of 1; + * in case of a return value of 0, the function must be invoked again with + * exactly the same arguments. + * + * The mailbox argument is used to store the context of an on-going table lookup + * operation. The mailbox mechanism allows for multiple concurrent table lookup + * operations into the same table. + * + * The typical reason an implementation may choose to split the table lookup + * operation into multiple steps is to hide the latency of the inherrent memory + * read operations: before a read operation with the source data likely not in + * the CPU cache, the source data prefetch is issued and the table lookup + * operation is postponed in favor of some other unrelated work, which the CPU + * executes in parallel with the source data being fetched into the CPU cache; + * later on, the table lookup operation is resumed, this time with the source + * data likely to be read from the CPU cache with no CPU pipeline stall, which + * significantly improves the table lookup performance. + * + * @param[in] table + * Table handle. + * @param[in] mailbox + * Mailbox for the current table lookup operation. + * @param[in] key + * Lookup key. Its size mult be equal to the table *key_size*. If the latter + * is zero, then the lookup key must be NULL. + * @param[out] action_id + * ID of the action associated with the *key*. Must point to a valid 64-bit + * variable. Only valid when the function returns 1 and *hit* is set to true. + * @param[out] action_data + * Action data for the *action_id* action. Must point to a valid array of + * table *action_data_size* bytes. Only valid when the function returns 1 and + * *hit* is set to true. + * @param[out] hit + * Only valid when the function returns 1. Set to non-zero (true) on table + * lookup hit and to zero (false) on table lookup miss. + * @return + * 0 when the table lookup operation is not yet completed, and 1 when the + * table lookup operation is completed. No other return values are allowed. + */ +typedef int +(*rte_swx_table_lookup_t)(void *table, + void *mailbox, + uint8_t **key, + uint64_t *action_id, + uint8_t **action_data, + int *hit); + +/** + * Table free + * + * @param[in] table + * Table handle. + */ +typedef void +(*rte_swx_table_free_t)(void *table); + +/** Table operations. */ +struct rte_swx_table_ops { + /** Table memory footprint get. Set to NULL when not supported. */ + rte_swx_table_footprint_get_t footprint_get; + + /** Table mailbox size get. When NULL, the mailbox size is 0. */ + rte_swx_table_mailbox_size_get_t mailbox_size_get; + + /** Table create. Must be non-NULL. */ + rte_swx_table_create_t create; + + /** Incremental table entry add. Set to NULL when not supported, in + * which case the existing table has to be destroyed and a new table + * built from scratch with the new entry included. + */ + rte_swx_table_add_t add; + + /** Incremental table entry delete. Set to NULL when not supported, in + * which case the existing table has to be destroyed and a new table + * built from scratch with the entry excluded. + */ + rte_swx_table_delete_t del; + + /** Table lookup. Must be non-NULL. */ + rte_swx_table_lookup_t lkp; + + /** Table free. Must be non-NULL. */ + rte_swx_table_free_t free; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_swx_table_em.c b/lib/table/rte_swx_table_em.c new file mode 100644 index 0000000000..788e25f6b9 --- /dev/null +++ b/lib/table/rte_swx_table_em.c @@ -0,0 +1,849 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include <rte_common.h> +#include <rte_prefetch.h> + +#include "rte_swx_table_em.h" + +#define CHECK(condition, err_code) \ +do { \ + if (!(condition)) \ + return -(err_code); \ +} while (0) + +#ifndef RTE_SWX_TABLE_EM_USE_HUGE_PAGES +#define RTE_SWX_TABLE_EM_USE_HUGE_PAGES 1 +#endif + +#if RTE_SWX_TABLE_EM_USE_HUGE_PAGES + +#include <rte_malloc.h> + +static void * +env_malloc(size_t size, size_t alignment, int numa_node) +{ + return rte_zmalloc_socket(NULL, size, alignment, numa_node); +} + +static void +env_free(void *start, size_t size __rte_unused) +{ + rte_free(start); +} + +#else + +#include <numa.h> + +static void * +env_malloc(size_t size, size_t alignment __rte_unused, int numa_node) +{ + return numa_alloc_onnode(size, numa_node); +} + +static void +env_free(void *start, size_t size) +{ + numa_free(start, size); +} + +#endif + +#if defined(RTE_ARCH_X86_64) + +#include <x86intrin.h> + +#define crc32_u64(crc, v) _mm_crc32_u64(crc, v) + +#else + +static inline uint64_t +crc32_u64_generic(uint64_t crc, uint64_t value) +{ + int i; + + crc = (crc & 0xFFFFFFFFLLU) ^ value; + for (i = 63; i >= 0; i--) { + uint64_t mask; + + mask = -(crc & 1LLU); + crc = (crc >> 1LLU) ^ (0x82F63B78LLU & mask); + } + + return crc; +} + +#define crc32_u64(crc, v) crc32_u64_generic(crc, v) + +#endif + +/* Key size needs to be one of: 8, 16, 32 or 64. */ +static inline uint32_t +hash(void *key, void *key_mask, uint32_t key_size, uint32_t seed) +{ + uint64_t *k = key; + uint64_t *m = key_mask; + uint64_t k0, k2, k5, crc0, crc1, crc2, crc3, crc4, crc5; + + switch (key_size) { + case 8: + crc0 = crc32_u64(seed, k[0] & m[0]); + return crc0; + + case 16: + k0 = k[0] & m[0]; + + crc0 = crc32_u64(k0, seed); + crc1 = crc32_u64(k0 >> 32, k[1] & m[1]); + + crc0 ^= crc1; + + return crc0; + + case 32: + k0 = k[0] & m[0]; + k2 = k[2] & m[2]; + + crc0 = crc32_u64(k0, seed); + crc1 = crc32_u64(k0 >> 32, k[1] & m[1]); + + crc2 = crc32_u64(k2, k[3] & m[3]); + crc3 = k2 >> 32; + + crc0 = crc32_u64(crc0, crc1); + crc1 = crc32_u64(crc2, crc3); + + crc0 ^= crc1; + + return crc0; + + case 64: + k0 = k[0] & m[0]; + k2 = k[2] & m[2]; + k5 = k[5] & m[5]; + + crc0 = crc32_u64(k0, seed); + crc1 = crc32_u64(k0 >> 32, k[1] & m[1]); + + crc2 = crc32_u64(k2, k[3] & m[3]); + crc3 = crc32_u64(k2 >> 32, k[4] & m[4]); + + crc4 = crc32_u64(k5, k[6] & m[6]); + crc5 = crc32_u64(k5 >> 32, k[7] & m[7]); + + crc0 = crc32_u64(crc0, (crc1 << 32) ^ crc2); + crc1 = crc32_u64(crc3, (crc4 << 32) ^ crc5); + + crc0 ^= crc1; + + return crc0; + + default: + crc0 = 0; + return crc0; + } +} + +/* n_bytes needs to be a multiple of 8 bytes. */ +static void +keycpy(void *dst, void *src, void *src_mask, uint32_t n_bytes) +{ + uint64_t *dst64 = dst, *src64 = src, *src_mask64 = src_mask; + uint32_t i; + + for (i = 0; i < n_bytes / sizeof(uint64_t); i++) + dst64[i] = src64[i] & src_mask64[i]; +} + +/* + * Return: 0 = Keys are NOT equal; 1 = Keys are equal. + */ +static inline uint32_t +keycmp(void *a, void *b, void *b_mask, uint32_t n_bytes) +{ + uint64_t *a64 = a, *b64 = b, *b_mask64 = b_mask; + + switch (n_bytes) { + case 8: { + uint64_t xor0 = a64[0] ^ (b64[0] & b_mask64[0]); + uint32_t result = 1; + + if (xor0) + result = 0; + return result; + } + + case 16: { + uint64_t xor0 = a64[0] ^ (b64[0] & b_mask64[0]); + uint64_t xor1 = a64[1] ^ (b64[1] & b_mask64[1]); + uint64_t or = xor0 | xor1; + uint32_t result = 1; + + if (or) + result = 0; + return result; + } + + case 32: { + uint64_t xor0 = a64[0] ^ (b64[0] & b_mask64[0]); + uint64_t xor1 = a64[1] ^ (b64[1] & b_mask64[1]); + uint64_t xor2 = a64[2] ^ (b64[2] & b_mask64[2]); + uint64_t xor3 = a64[3] ^ (b64[3] & b_mask64[3]); + uint64_t or = (xor0 | xor1) | (xor2 | xor3); + uint32_t result = 1; + + if (or) + result = 0; + return result; + } + + case 64: { + uint64_t xor0 = a64[0] ^ (b64[0] & b_mask64[0]); + uint64_t xor1 = a64[1] ^ (b64[1] & b_mask64[1]); + uint64_t xor2 = a64[2] ^ (b64[2] & b_mask64[2]); + uint64_t xor3 = a64[3] ^ (b64[3] & b_mask64[3]); + uint64_t xor4 = a64[4] ^ (b64[4] & b_mask64[4]); + uint64_t xor5 = a64[5] ^ (b64[5] & b_mask64[5]); + uint64_t xor6 = a64[6] ^ (b64[6] & b_mask64[6]); + uint64_t xor7 = a64[7] ^ (b64[7] & b_mask64[7]); + uint64_t or = ((xor0 | xor1) | (xor2 | xor3)) | + ((xor4 | xor5) | (xor6 | xor7)); + uint32_t result = 1; + + if (or) + result = 0; + return result; + } + + default: { + uint32_t i; + + for (i = 0; i < n_bytes / sizeof(uint64_t); i++) + if (a64[i] != (b64[i] & b_mask64[i])) + return 0; + return 1; + } + } +} + +#define KEYS_PER_BUCKET 4 + +struct bucket_extension { + struct bucket_extension *next; + uint16_t sig[KEYS_PER_BUCKET]; + uint32_t key_id[KEYS_PER_BUCKET]; +}; + +struct table { + /* Input parameters */ + struct rte_swx_table_params params; + + /* Internal. */ + uint32_t key_size; + uint32_t data_size; + uint32_t key_size_shl; + uint32_t data_size_shl; + uint32_t n_buckets; + uint32_t n_buckets_ext; + uint32_t key_stack_tos; + uint32_t bkt_ext_stack_tos; + uint64_t total_size; + + /* Memory arrays. */ + uint8_t *key_mask; + struct bucket_extension *buckets; + struct bucket_extension *buckets_ext; + uint8_t *keys; + uint32_t *key_stack; + uint32_t *bkt_ext_stack; + uint8_t *data; +}; + +static inline uint8_t * +table_key(struct table *t, uint32_t key_id) +{ + return &t->keys[(uint64_t)key_id << t->key_size_shl]; +} + +static inline uint64_t * +table_key_data(struct table *t, uint32_t key_id) +{ + return (uint64_t *)&t->data[(uint64_t)key_id << t->data_size_shl]; +} + +static inline int +bkt_is_empty(struct bucket_extension *bkt) +{ + return (!bkt->sig[0] && !bkt->sig[1] && !bkt->sig[2] && !bkt->sig[2]) ? + 1 : 0; +} + +/* Return: + * 0 = Bucket key position is NOT empty; + * 1 = Bucket key position is empty. + */ +static inline int +bkt_key_is_empty(struct bucket_extension *bkt, uint32_t bkt_pos) +{ + return bkt->sig[bkt_pos] ? 0 : 1; +} + +/* Return: 0 = Keys are NOT equal; 1 = Keys are equal. */ +static inline int +bkt_keycmp(struct table *t, + struct bucket_extension *bkt, + uint8_t *input_key, + uint32_t bkt_pos, + uint32_t input_sig) +{ + uint32_t bkt_key_id; + uint8_t *bkt_key; + + /* Key signature comparison. */ + if (input_sig != bkt->sig[bkt_pos]) + return 0; + + /* Key comparison. */ + bkt_key_id = bkt->key_id[bkt_pos]; + bkt_key = table_key(t, bkt_key_id); + return keycmp(bkt_key, input_key, t->key_mask, t->key_size); +} + +static inline void +bkt_key_install(struct table *t, + struct bucket_extension *bkt, + struct rte_swx_table_entry *input, + uint32_t bkt_pos, + uint32_t bkt_key_id, + uint32_t input_sig) +{ + uint8_t *bkt_key; + uint64_t *bkt_data; + + /* Key signature. */ + bkt->sig[bkt_pos] = (uint16_t)input_sig; + + /* Key. */ + bkt->key_id[bkt_pos] = bkt_key_id; + bkt_key = table_key(t, bkt_key_id); + keycpy(bkt_key, input->key, t->key_mask, t->key_size); + + /* Key data. */ + bkt_data = table_key_data(t, bkt_key_id); + bkt_data[0] = input->action_id; + if (t->params.action_data_size && input->action_data) + memcpy(&bkt_data[1], + input->action_data, + t->params.action_data_size); +} + +static inline void +bkt_key_data_update(struct table *t, + struct bucket_extension *bkt, + struct rte_swx_table_entry *input, + uint32_t bkt_pos) +{ + uint32_t bkt_key_id; + uint64_t *bkt_data; + + /* Key. */ + bkt_key_id = bkt->key_id[bkt_pos]; + + /* Key data. */ + bkt_data = table_key_data(t, bkt_key_id); + bkt_data[0] = input->action_id; + if (t->params.action_data_size && input->action_data) + memcpy(&bkt_data[1], + input->action_data, + t->params.action_data_size); +} + +#define CL RTE_CACHE_LINE_ROUNDUP + +static int +__table_create(struct table **table, + uint64_t *memory_footprint, + struct rte_swx_table_params *params, + const char *args __rte_unused, + int numa_node) +{ + struct table *t; + uint8_t *memory; + size_t table_meta_sz, key_mask_sz, bucket_sz, bucket_ext_sz, key_sz, + key_stack_sz, bkt_ext_stack_sz, data_sz, total_size; + size_t key_mask_offset, bucket_offset, bucket_ext_offset, key_offset, + key_stack_offset, bkt_ext_stack_offset, data_offset; + uint32_t key_size, key_data_size, n_buckets, n_buckets_ext, i; + + /* Check input arguments. */ + CHECK(params, EINVAL); + CHECK(params->match_type == RTE_SWX_TABLE_MATCH_EXACT, EINVAL); + CHECK(params->key_size, EINVAL); + CHECK(params->key_size <= 64, EINVAL); + CHECK(params->n_keys_max, EINVAL); + + /* Memory allocation. */ + key_size = rte_align64pow2(params->key_size); + if (key_size < 8) + key_size = 8; + key_data_size = rte_align64pow2(params->action_data_size + 8); + n_buckets = params->n_keys_max / KEYS_PER_BUCKET; + n_buckets_ext = params->n_keys_max / KEYS_PER_BUCKET; + + table_meta_sz = CL(sizeof(struct table)); + key_mask_sz = CL(key_size); + bucket_sz = CL(n_buckets * sizeof(struct bucket_extension)); + bucket_ext_sz = CL(n_buckets_ext * sizeof(struct bucket_extension)); + key_sz = CL(params->n_keys_max * key_size); + key_stack_sz = CL(params->n_keys_max * sizeof(uint32_t)); + bkt_ext_stack_sz = CL(n_buckets_ext * sizeof(uint32_t)); + data_sz = CL(params->n_keys_max * key_data_size); + total_size = table_meta_sz + key_mask_sz + bucket_sz + bucket_ext_sz + + key_sz + key_stack_sz + bkt_ext_stack_sz + data_sz; + + key_mask_offset = table_meta_sz; + bucket_offset = key_mask_offset + key_mask_sz; + bucket_ext_offset = bucket_offset + bucket_sz; + key_offset = bucket_ext_offset + bucket_ext_sz; + key_stack_offset = key_offset + key_sz; + bkt_ext_stack_offset = key_stack_offset + key_stack_sz; + data_offset = bkt_ext_stack_offset + bkt_ext_stack_sz; + + if (!table) { + if (memory_footprint) + *memory_footprint = total_size; + return 0; + } + + memory = env_malloc(total_size, RTE_CACHE_LINE_SIZE, numa_node); + CHECK(memory, ENOMEM); + memset(memory, 0, total_size); + + /* Initialization. */ + t = (struct table *)memory; + memcpy(&t->params, params, sizeof(*params)); + + t->key_size = key_size; + t->data_size = key_data_size; + t->key_size_shl = __builtin_ctzl(key_size); + t->data_size_shl = __builtin_ctzl(key_data_size); + t->n_buckets = n_buckets; + t->n_buckets_ext = n_buckets_ext; + t->total_size = total_size; + + t->key_mask = &memory[key_mask_offset]; + t->buckets = (struct bucket_extension *)&memory[bucket_offset]; + t->buckets_ext = (struct bucket_extension *)&memory[bucket_ext_offset]; + t->keys = &memory[key_offset]; + t->key_stack = (uint32_t *)&memory[key_stack_offset]; + t->bkt_ext_stack = (uint32_t *)&memory[bkt_ext_stack_offset]; + t->data = &memory[data_offset]; + + t->params.key_mask0 = t->key_mask; + + if (!params->key_mask0) + memset(t->key_mask, 0xFF, params->key_size); + else + memcpy(t->key_mask, params->key_mask0, params->key_size); + + for (i = 0; i < t->params.n_keys_max; i++) + t->key_stack[i] = t->params.n_keys_max - 1 - i; + t->key_stack_tos = t->params.n_keys_max; + + for (i = 0; i < n_buckets_ext; i++) + t->bkt_ext_stack[i] = n_buckets_ext - 1 - i; + t->bkt_ext_stack_tos = n_buckets_ext; + + *table = t; + return 0; +} + +static void +table_free(void *table) +{ + struct table *t = table; + + if (!t) + return; + + env_free(t, t->total_size); +} + +static int +table_add(void *table, struct rte_swx_table_entry *entry) +{ + struct table *t = table; + struct bucket_extension *bkt0, *bkt, *bkt_prev; + uint32_t input_sig, bkt_id, i; + + CHECK(t, EINVAL); + CHECK(entry, EINVAL); + CHECK(entry->key, EINVAL); + + input_sig = hash(entry->key, t->key_mask, t->key_size, 0); + bkt_id = input_sig & (t->n_buckets - 1); + bkt0 = &t->buckets[bkt_id]; + input_sig = (input_sig >> 16) | 1; + + /* Key is present in the bucket. */ + for (bkt = bkt0; bkt; bkt = bkt->next) + for (i = 0; i < KEYS_PER_BUCKET; i++) + if (bkt_keycmp(t, bkt, entry->key, i, input_sig)) { + bkt_key_data_update(t, bkt, entry, i); + return 0; + } + + /* Key is not present in the bucket. Bucket not full. */ + for (bkt = bkt0, bkt_prev = NULL; bkt; bkt_prev = bkt, bkt = bkt->next) + for (i = 0; i < KEYS_PER_BUCKET; i++) + if (bkt_key_is_empty(bkt, i)) { + uint32_t new_bkt_key_id; + + /* Allocate new key & install. */ + CHECK(t->key_stack_tos, ENOSPC); + new_bkt_key_id = + t->key_stack[--t->key_stack_tos]; + bkt_key_install(t, bkt, entry, i, + new_bkt_key_id, input_sig); + return 0; + } + + /* Bucket full: extend bucket. */ + if (t->bkt_ext_stack_tos && t->key_stack_tos) { + struct bucket_extension *new_bkt; + uint32_t new_bkt_id, new_bkt_key_id; + + /* Allocate new bucket extension & install. */ + new_bkt_id = t->bkt_ext_stack[--t->bkt_ext_stack_tos]; + new_bkt = &t->buckets_ext[new_bkt_id]; + memset(new_bkt, 0, sizeof(*new_bkt)); + bkt_prev->next = new_bkt; + + /* Allocate new key & install. */ + new_bkt_key_id = t->key_stack[--t->key_stack_tos]; + bkt_key_install(t, new_bkt, entry, 0, + new_bkt_key_id, input_sig); + return 0; + } + + CHECK(0, ENOSPC); +} + +static int +table_del(void *table, struct rte_swx_table_entry *entry) +{ + struct table *t = table; + struct bucket_extension *bkt0, *bkt, *bkt_prev; + uint32_t input_sig, bkt_id, i; + + CHECK(t, EINVAL); + CHECK(entry, EINVAL); + CHECK(entry->key, EINVAL); + + input_sig = hash(entry->key, t->key_mask, t->key_size, 0); + bkt_id = input_sig & (t->n_buckets - 1); + bkt0 = &t->buckets[bkt_id]; + input_sig = (input_sig >> 16) | 1; + + /* Key is present in the bucket. */ + for (bkt = bkt0, bkt_prev = NULL; bkt; bkt_prev = bkt, bkt = bkt->next) + for (i = 0; i < KEYS_PER_BUCKET; i++) + if (bkt_keycmp(t, bkt, entry->key, i, input_sig)) { + /* Key free. */ + bkt->sig[i] = 0; + t->key_stack[t->key_stack_tos++] = + bkt->key_id[i]; + + /* Bucket extension free if empty and not the + * 1st in bucket. + */ + if (bkt_prev && bkt_is_empty(bkt)) { + bkt_prev->next = bkt->next; + bkt_id = bkt - t->buckets_ext; + t->bkt_ext_stack[t->bkt_ext_stack_tos++] + = bkt_id; + } + + return 0; + } + + return 0; +} + +static uint64_t +table_mailbox_size_get_unoptimized(void) +{ + return 0; +} + +static int +table_lookup_unoptimized(void *table, + void *mailbox __rte_unused, + uint8_t **key, + uint64_t *action_id, + uint8_t **action_data, + int *hit) +{ + struct table *t = table; + struct bucket_extension *bkt0, *bkt; + uint8_t *input_key; + uint32_t input_sig, bkt_id, i; + + input_key = &(*key)[t->params.key_offset]; + + input_sig = hash(input_key, t->key_mask, t->key_size, 0); + bkt_id = input_sig & (t->n_buckets - 1); + bkt0 = &t->buckets[bkt_id]; + input_sig = (input_sig >> 16) | 1; + + /* Key is present in the bucket. */ + for (bkt = bkt0; bkt; bkt = bkt->next) + for (i = 0; i < KEYS_PER_BUCKET; i++) + if (bkt_keycmp(t, bkt, input_key, i, input_sig)) { + uint32_t bkt_key_id; + uint64_t *bkt_data; + + /* Key. */ + bkt_key_id = bkt->key_id[i]; + + /* Key data. */ + bkt_data = table_key_data(t, bkt_key_id); + *action_id = bkt_data[0]; + *action_data = (uint8_t *)&bkt_data[1]; + *hit = 1; + return 1; + } + + *hit = 0; + return 1; +} + +struct mailbox { + struct bucket_extension *bkt; + uint32_t input_sig; + uint32_t bkt_key_id; + uint32_t sig_match; + uint32_t sig_match_many; + int state; +}; + +static uint64_t +table_mailbox_size_get(void) +{ + return sizeof(struct mailbox); +} + +/* + * mask = match bitmask + * match = at least one match + * match_many = more than one match + * match_pos = position of first match + * + *+------+-------+------------+-----------+ + *| mask | match | match_many | match_pos | + *+------+-------+------------+-----------+ + *| 0000 | 0 | 0 | 00 | + *| 0001 | 1 | 0 | 00 | + *| 0010 | 1 | 0 | 01 | + *| 0011 | 1 | 1 | 00 | + *+------+-------+------------+-----------+ + *| 0100 | 1 | 0 | 10 | + *| 0101 | 1 | 1 | 00 | + *| 0110 | 1 | 1 | 01 | + *| 0111 | 1 | 1 | 00 | + *+------+-------+------------+-----------+ + *| 1000 | 1 | 0 | 11 | + *| 1001 | 1 | 1 | 00 | + *| 1010 | 1 | 1 | 01 | + *| 1011 | 1 | 1 | 00 | + *+------+-------+------------+-----------+ + *| 1100 | 1 | 1 | 10 | + *| 1101 | 1 | 1 | 00 | + *| 1110 | 1 | 1 | 01 | + *| 1111 | 1 | 1 | 00 | + *+------+-------+------------+-----------+ + * + * match = 1111_1111_1111_1110 = 0xFFFE + * match_many = 1111_1110_1110_1000 = 0xFEE8 + * match_pos = 0001_0010_0001_0011__0001_0010_0001_0000 = 0x12131210 + * + */ + +#define LUT_MATCH 0xFFFE +#define LUT_MATCH_MANY 0xFEE8 +#define LUT_MATCH_POS 0x12131210 + +static int +table_lookup(void *table, + void *mailbox, + uint8_t **key, + uint64_t *action_id, + uint8_t **action_data, + int *hit) +{ + struct table *t = table; + struct mailbox *m = mailbox; + + switch (m->state) { + case 0: { + uint8_t *input_key = &(*key)[t->params.key_offset]; + struct bucket_extension *bkt; + uint32_t input_sig, bkt_id; + + input_sig = hash(input_key, t->key_mask, t->key_size, 0); + bkt_id = input_sig & (t->n_buckets - 1); + bkt = &t->buckets[bkt_id]; + rte_prefetch0(bkt); + + m->bkt = bkt; + m->input_sig = (input_sig >> 16) | 1; + m->state++; + return 0; + } + + case 1: { + struct bucket_extension *bkt = m->bkt; + uint32_t input_sig = m->input_sig; + uint32_t bkt_sig0, bkt_sig1, bkt_sig2, bkt_sig3; + uint32_t mask0 = 0, mask1 = 0, mask2 = 0, mask3 = 0, mask_all; + uint32_t sig_match = LUT_MATCH; + uint32_t sig_match_many = LUT_MATCH_MANY; + uint32_t sig_match_pos = LUT_MATCH_POS; + uint32_t bkt_key_id; + + bkt_sig0 = input_sig ^ bkt->sig[0]; + if (!bkt_sig0) + mask0 = 1 << 0; + + bkt_sig1 = input_sig ^ bkt->sig[1]; + if (!bkt_sig1) + mask1 = 1 << 1; + + bkt_sig2 = input_sig ^ bkt->sig[2]; + if (!bkt_sig2) + mask2 = 1 << 2; + + bkt_sig3 = input_sig ^ bkt->sig[3]; + if (!bkt_sig3) + mask3 = 1 << 3; + + mask_all = (mask0 | mask1) | (mask2 | mask3); + sig_match = (sig_match >> mask_all) & 1; + sig_match_many = (sig_match_many >> mask_all) & 1; + sig_match_pos = (sig_match_pos >> (mask_all << 1)) & 3; + + bkt_key_id = bkt->key_id[sig_match_pos]; + rte_prefetch0(table_key(t, bkt_key_id)); + rte_prefetch0(table_key_data(t, bkt_key_id)); + + m->bkt_key_id = bkt_key_id; + m->sig_match = sig_match; + m->sig_match_many = sig_match_many; + m->state++; + return 0; + } + + case 2: { + uint8_t *input_key = &(*key)[t->params.key_offset]; + struct bucket_extension *bkt = m->bkt; + uint32_t bkt_key_id = m->bkt_key_id; + uint8_t *bkt_key = table_key(t, bkt_key_id); + uint64_t *bkt_data = table_key_data(t, bkt_key_id); + uint32_t lkp_hit; + + lkp_hit = keycmp(bkt_key, input_key, t->key_mask, t->key_size); + lkp_hit &= m->sig_match; + *action_id = bkt_data[0]; + *action_data = (uint8_t *)&bkt_data[1]; + *hit = lkp_hit; + + m->state = 0; + + if (!lkp_hit && (m->sig_match_many || bkt->next)) + return table_lookup_unoptimized(t, + m, + key, + action_id, + action_data, + hit); + + return 1; + } + + default: + return 0; + } +} + +static void * +table_create(struct rte_swx_table_params *params, + struct rte_swx_table_entry_list *entries, + const char *args, + int numa_node) +{ + struct table *t; + struct rte_swx_table_entry *entry; + int status; + + /* Table create. */ + status = __table_create(&t, NULL, params, args, numa_node); + if (status) + return NULL; + + /* Table add entries. */ + if (!entries) + return t; + + TAILQ_FOREACH(entry, entries, node) { + int status; + + status = table_add(t, entry); + if (status) { + table_free(t); + return NULL; + } + } + + return t; +} + +static uint64_t +table_footprint(struct rte_swx_table_params *params, + struct rte_swx_table_entry_list *entries __rte_unused, + const char *args) +{ + uint64_t memory_footprint; + int status; + + status = __table_create(NULL, &memory_footprint, params, args, 0); + if (status) + return 0; + + return memory_footprint; +} + +struct rte_swx_table_ops rte_swx_table_exact_match_unoptimized_ops = { + .footprint_get = table_footprint, + .mailbox_size_get = table_mailbox_size_get_unoptimized, + .create = table_create, + .add = table_add, + .del = table_del, + .lkp = table_lookup_unoptimized, + .free = table_free, +}; + +struct rte_swx_table_ops rte_swx_table_exact_match_ops = { + .footprint_get = table_footprint, + .mailbox_size_get = table_mailbox_size_get, + .create = table_create, + .add = table_add, + .del = table_del, + .lkp = table_lookup, + .free = table_free, +}; diff --git a/lib/table/rte_swx_table_em.h b/lib/table/rte_swx_table_em.h new file mode 100644 index 0000000000..909ada483b --- /dev/null +++ b/lib/table/rte_swx_table_em.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ +#ifndef __INCLUDE_RTE_SWX_TABLE_EM_H__ +#define __INCLUDE_RTE_SWX_TABLE_EM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE SWX Exact Match Table + */ + +#include <stdint.h> + +#include <rte_swx_table.h> + +/** Exact match table operations - unoptimized. */ +extern struct rte_swx_table_ops rte_swx_table_exact_match_unoptimized_ops; + +/** Exact match table operations. */ +extern struct rte_swx_table_ops rte_swx_table_exact_match_ops; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_swx_table_wm.c b/lib/table/rte_swx_table_wm.c new file mode 100644 index 0000000000..e260be1062 --- /dev/null +++ b/lib/table/rte_swx_table_wm.c @@ -0,0 +1,469 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include <rte_common.h> +#include <rte_prefetch.h> +#include <rte_cycles.h> +#include <rte_acl.h> + +#include "rte_swx_table_wm.h" + +#ifndef RTE_SWX_TABLE_EM_USE_HUGE_PAGES +#define RTE_SWX_TABLE_EM_USE_HUGE_PAGES 1 +#endif + +#if RTE_SWX_TABLE_EM_USE_HUGE_PAGES + +#include <rte_malloc.h> + +static void * +env_malloc(size_t size, size_t alignment, int numa_node) +{ + return rte_zmalloc_socket(NULL, size, alignment, numa_node); +} + +static void +env_free(void *start, size_t size __rte_unused) +{ + rte_free(start); +} + +#else + +#include <numa.h> + +static void * +env_malloc(size_t size, size_t alignment __rte_unused, int numa_node) +{ + return numa_alloc_onnode(size, numa_node); +} + +static void +env_free(void *start, size_t size) +{ + numa_free(start, size); +} + +#endif + +static char *get_unique_name(void) +{ + uint64_t tsc = rte_get_tsc_cycles(); + size_t size = sizeof(uint64_t) * 2 + 1; + char *name = calloc(1, size); + + if (!name) + return NULL; + + snprintf(name, size, "%016" PRIx64, tsc); + return name; +} + +static uint32_t +count_entries(struct rte_swx_table_entry_list *entries) +{ + struct rte_swx_table_entry *entry; + uint32_t n_entries = 0; + + if (!entries) + return 0; + + TAILQ_FOREACH(entry, entries, node) + n_entries++; + + return n_entries; +} + +static int +acl_table_cfg_get(struct rte_acl_config *cfg, struct rte_swx_table_params *p) +{ + uint32_t byte_id = 0, field_id = 0; + + /* cfg->num_categories. */ + cfg->num_categories = 1; + + /* cfg->defs and cfg->num_fields. */ + for (byte_id = 0; byte_id < p->key_size; ) { + uint32_t field_size = field_id ? 4 : 1; + uint8_t byte = p->key_mask0 ? p->key_mask0[byte_id] : 0xFF; + + if (!byte) { + byte_id++; + continue; + } + + if (field_id == RTE_ACL_MAX_FIELDS) + return -1; + + cfg->defs[field_id].type = RTE_ACL_FIELD_TYPE_BITMASK; + cfg->defs[field_id].size = field_size; + cfg->defs[field_id].field_index = field_id; + cfg->defs[field_id].input_index = field_id; + cfg->defs[field_id].offset = p->key_offset + byte_id; + + field_id++; + byte_id += field_size; + } + + if (!field_id) + return -1; + + cfg->num_fields = field_id; + + /* cfg->max_size. */ + cfg->max_size = 0; + + return 0; +} + +static void +acl_table_rule_field8(uint8_t *value, + uint8_t *mask, + uint8_t *key_mask0, + uint8_t *key_mask, + uint8_t *key, + uint32_t offset) +{ + uint8_t km0, km; + + km0 = key_mask0 ? key_mask0[offset] : 0xFF; + km = key_mask ? key_mask[offset] : 0xFF; + + *value = key[offset]; + *mask = km0 & km; +} + +static void +acl_table_rule_field32(uint32_t *value, + uint32_t *mask, + uint8_t *key_mask0, + uint8_t *key_mask, + uint8_t *key, + uint32_t key_size, + uint32_t offset) +{ + uint32_t km0[4], km[4], k[4]; + uint32_t byte_id; + + /* Byte 0 = MSB, byte 3 = LSB. */ + for (byte_id = 0; byte_id < 4; byte_id++) { + if (offset + byte_id >= key_size) { + km0[byte_id] = 0; + km[byte_id] = 0; + k[byte_id] = 0; + continue; + } + + km0[byte_id] = key_mask0 ? key_mask0[offset + byte_id] : 0xFF; + km[byte_id] = key_mask ? key_mask[offset + byte_id] : 0xFF; + k[byte_id] = key[offset + byte_id]; + } + + *value = (k[0] << 24) | + (k[1] << 16) | + (k[2] << 8) | + k[3]; + + *mask = ((km[0] & km0[0]) << 24) | + ((km[1] & km0[1]) << 16) | + ((km[2] & km0[2]) << 8) | + (km[3] & km0[3]); +} + +RTE_ACL_RULE_DEF(acl_rule, RTE_ACL_MAX_FIELDS); + +static struct rte_acl_rule * +acl_table_rules_get(struct rte_acl_config *acl_cfg, + struct rte_swx_table_params *p, + struct rte_swx_table_entry_list *entries, + uint32_t n_entries) +{ + struct rte_swx_table_entry *entry; + uint8_t *memory; + uint32_t acl_rule_size = RTE_ACL_RULE_SZ(acl_cfg->num_fields); + uint32_t n_fields = acl_cfg->num_fields; + uint32_t rule_id; + + if (!n_entries) + return NULL; + + memory = malloc(n_entries * acl_rule_size); + if (!memory) + return NULL; + + rule_id = 0; + TAILQ_FOREACH(entry, entries, node) { + uint8_t *m = &memory[rule_id * acl_rule_size]; + struct acl_rule *acl_rule = (struct acl_rule *)m; + uint32_t field_id; + + acl_rule->data.category_mask = 1; + acl_rule->data.priority = RTE_ACL_MAX_PRIORITY - + entry->key_priority; + acl_rule->data.userdata = rule_id + 1; + + for (field_id = 0; field_id < n_fields; field_id++) { + struct rte_acl_field *f = &acl_rule->field[field_id]; + uint32_t size = acl_cfg->defs[field_id].size; + uint32_t offset = acl_cfg->defs[field_id].offset - + p->key_offset; + + if (size == 1) { + uint8_t value, mask; + + acl_table_rule_field8(&value, + &mask, + p->key_mask0, + entry->key_mask, + entry->key, + offset); + + f->value.u8 = value; + f->mask_range.u8 = mask; + } else { + uint32_t value, mask; + + acl_table_rule_field32(&value, + &mask, + p->key_mask0, + entry->key_mask, + entry->key, + p->key_size, + offset); + + f->value.u32 = value; + f->mask_range.u32 = mask; + } + } + + rule_id++; + } + + return (struct rte_acl_rule *)memory; +} + +/* When the table to be created has no rules, the expected behavior is to always + * get lookup miss for any input key. To achieve this, we add a single bogus + * rule to the table with the rule user data set to 0, i.e. the value returned + * when lookup miss takes place. Whether lookup hit (the bogus rule is hit) or + * miss, a user data of 0 is returned, which for the ACL library is equivalent + * to lookup miss. + */ +static struct rte_acl_rule * +acl_table_rules_default_get(struct rte_acl_config *acl_cfg) +{ + struct rte_acl_rule *acl_rule; + uint32_t acl_rule_size = RTE_ACL_RULE_SZ(acl_cfg->num_fields); + + acl_rule = calloc(1, acl_rule_size); + if (!acl_rule) + return NULL; + + acl_rule->data.category_mask = 1; + acl_rule->data.priority = RTE_ACL_MAX_PRIORITY; + acl_rule->data.userdata = 0; + + memset(&acl_rule[1], 0xFF, acl_rule_size - sizeof(struct rte_acl_rule)); + + return acl_rule; +} + +static struct rte_acl_ctx * +acl_table_create(struct rte_swx_table_params *params, + struct rte_swx_table_entry_list *entries, + uint32_t n_entries, + int numa_node) +{ + struct rte_acl_param acl_params = {0}; + struct rte_acl_config acl_cfg = {0}; + struct rte_acl_ctx *acl_ctx = NULL; + struct rte_acl_rule *acl_rules = NULL; + char *name = NULL; + int status = 0; + + /* ACL config data structures. */ + name = get_unique_name(); + if (!name) { + status = -1; + goto free_resources; + } + + status = acl_table_cfg_get(&acl_cfg, params); + if (status) + goto free_resources; + + acl_rules = n_entries ? + acl_table_rules_get(&acl_cfg, params, entries, n_entries) : + acl_table_rules_default_get(&acl_cfg); + if (!acl_rules) { + status = -1; + goto free_resources; + } + + n_entries = n_entries ? n_entries : 1; + + /* ACL create. */ + acl_params.name = name; + acl_params.socket_id = numa_node; + acl_params.rule_size = RTE_ACL_RULE_SZ(acl_cfg.num_fields); + acl_params.max_rule_num = n_entries; + + acl_ctx = rte_acl_create(&acl_params); + if (!acl_ctx) { + status = -1; + goto free_resources; + } + + /* ACL add rules. */ + status = rte_acl_add_rules(acl_ctx, acl_rules, n_entries); + if (status) + goto free_resources; + + /* ACL build. */ + status = rte_acl_build(acl_ctx, &acl_cfg); + +free_resources: + if (status && acl_ctx) + rte_acl_free(acl_ctx); + + free(acl_rules); + + free(name); + + return status ? NULL : acl_ctx; +} + +static void +entry_data_copy(uint8_t *data, + struct rte_swx_table_entry_list *entries, + uint32_t n_entries, + uint32_t entry_data_size) +{ + struct rte_swx_table_entry *entry; + uint32_t i = 0; + + if (!n_entries) + return; + + TAILQ_FOREACH(entry, entries, node) { + uint64_t *d = (uint64_t *)&data[i * entry_data_size]; + + d[0] = entry->action_id; + memcpy(&d[1], entry->action_data, entry_data_size - 8); + + i++; + } +} + +struct table { + struct rte_acl_ctx *acl_ctx; + uint8_t *data; + size_t total_size; + uint32_t entry_data_size; +}; + +static void +table_free(void *table) +{ + struct table *t = table; + + if (!t) + return; + + if (t->acl_ctx) + rte_acl_free(t->acl_ctx); + env_free(t, t->total_size); +} + +static void * +table_create(struct rte_swx_table_params *params, + struct rte_swx_table_entry_list *entries, + const char *args __rte_unused, + int numa_node) +{ + struct table *t = NULL; + size_t meta_sz, data_sz, total_size; + uint32_t entry_data_size; + uint32_t n_entries = count_entries(entries); + + /* Check input arguments. */ + if (!params || !params->key_size) + goto error; + + /* Memory allocation and initialization. */ + entry_data_size = 8 + params->action_data_size; + meta_sz = sizeof(struct table); + data_sz = n_entries * entry_data_size; + total_size = meta_sz + data_sz; + + t = env_malloc(total_size, RTE_CACHE_LINE_SIZE, numa_node); + if (!t) + goto error; + + memset(t, 0, total_size); + t->entry_data_size = entry_data_size; + t->total_size = total_size; + t->data = (uint8_t *)&t[1]; + + t->acl_ctx = acl_table_create(params, entries, n_entries, numa_node); + if (!t->acl_ctx) + goto error; + + entry_data_copy(t->data, entries, n_entries, entry_data_size); + + return t; + +error: + table_free(t); + return NULL; +} + +struct mailbox { + +}; + +static uint64_t +table_mailbox_size_get(void) +{ + return sizeof(struct mailbox); +} + +static int +table_lookup(void *table, + void *mailbox __rte_unused, + const uint8_t **key, + uint64_t *action_id, + uint8_t **action_data, + int *hit) +{ + struct table *t = table; + uint8_t *data; + uint32_t user_data; + + rte_acl_classify(t->acl_ctx, key, &user_data, 1, 1); + if (!user_data) { + *hit = 0; + return 1; + } + + data = &t->data[(user_data - 1) * t->entry_data_size]; + *action_id = ((uint64_t *)data)[0]; + *action_data = &data[8]; + *hit = 1; + return 1; +} + +struct rte_swx_table_ops rte_swx_table_wildcard_match_ops = { + .footprint_get = NULL, + .mailbox_size_get = table_mailbox_size_get, + .create = table_create, + .add = NULL, + .del = NULL, + .lkp = (rte_swx_table_lookup_t)table_lookup, + .free = table_free, +}; diff --git a/lib/table/rte_swx_table_wm.h b/lib/table/rte_swx_table_wm.h new file mode 100644 index 0000000000..9e228f971c --- /dev/null +++ b/lib/table/rte_swx_table_wm.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2021 Intel Corporation + */ +#ifndef __INCLUDE_RTE_SWX_TABLE_WM_H__ +#define __INCLUDE_RTE_SWX_TABLE_WM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE SWX Wildcard Match Table + */ + +#include <stdint.h> + +#include <rte_swx_table.h> + +/** Wildcard match table operations. */ +extern struct rte_swx_table_ops rte_swx_table_wildcard_match_ops; + +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_RTE_SWX_TABLE_WM_H__ */ diff --git a/lib/table/rte_table.h b/lib/table/rte_table.h new file mode 100644 index 0000000000..096ab8a7c8 --- /dev/null +++ b/lib/table/rte_table.h @@ -0,0 +1,272 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_TABLE_H__ +#define __INCLUDE_RTE_TABLE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE Table + * + * This tool is part of the DPDK Packet Framework tool suite and provides + * a standard interface to implement different types of lookup tables for data + * plane processing. + * + * Virtually any search algorithm that can uniquely associate data to a lookup + * key can be fitted under this lookup table abstraction. For the flow table + * use-case, the lookup key is an n-tuple of packet fields that uniquely + * identifies a traffic flow, while data represents actions and action + * meta-data associated with the same traffic flow. + * + ***/ + +#include <stdint.h> +#include <rte_port.h> + +struct rte_mbuf; + +/** Lookup table statistics */ +struct rte_table_stats { + uint64_t n_pkts_in; + uint64_t n_pkts_lookup_miss; +}; + +/** + * Lookup table create + * + * @param params + * Parameters for lookup table creation. The underlying data structure is + * different for each lookup table type. + * @param socket_id + * CPU socket ID (e.g. for memory allocation purpose) + * @param entry_size + * Data size of each lookup table entry (measured in bytes) + * @return + * Handle to lookup table instance + */ +typedef void* (*rte_table_op_create)(void *params, int socket_id, + uint32_t entry_size); + +/** + * Lookup table free + * + * @param table + * Handle to lookup table instance + * @return + * 0 on success, error code otherwise + */ +typedef int (*rte_table_op_free)(void *table); + +/** + * Lookup table entry add + * + * @param table + * Handle to lookup table instance + * @param key + * Lookup key + * @param entry + * Data to be associated with the current key. This parameter has to point to + * a valid memory buffer where the first entry_size bytes (table create + * parameter) are populated with the data. + * @param key_found + * After successful invocation, *key_found is set to a value different than 0 + * if the current key is already present in the table and to 0 if not. This + * pointer has to be set to a valid memory location before the table entry add + * function is called. + * @param entry_ptr + * After successful invocation, *entry_ptr stores the handle to the table + * entry containing the data associated with the current key. This handle can + * be used to perform further read-write accesses to this entry. This handle + * is valid until the key is deleted from the table or the same key is + * re-added to the table, typically to associate it with different data. This + * pointer has to be set to a valid memory location before the function is + * called. + * @return + * 0 on success, error code otherwise + */ +typedef int (*rte_table_op_entry_add)( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr); + +/** + * Lookup table entry delete + * + * @param table + * Handle to lookup table instance + * @param key + * Lookup key + * @param key_found + * After successful invocation, *key_found is set to a value different than 0 + * if the current key was present in the table before the delete operation + * was performed and to 0 if not. This pointer has to be set to a valid + * memory location before the table entry delete function is called. + * @param entry + * After successful invocation, if the key is found in the table (*key found + * is different than 0 after function call is completed) and entry points to + * a valid buffer (entry is set to a value different than NULL before the + * function is called), then the first entry_size bytes (table create + * parameter) in *entry store a copy of table entry that contained the data + * associated with the current key before the key was deleted. + * @return + * 0 on success, error code otherwise + */ +typedef int (*rte_table_op_entry_delete)( + void *table, + void *key, + int *key_found, + void *entry); + +/** + * Lookup table entry add bulk + * + * @param table + * Handle to lookup table instance + * @param keys + * Array containing lookup keys + * @param entries + * Array containing data to be associated with each key. Every item in the + * array has to point to a valid memory buffer where the first entry_size + * bytes (table create parameter) are populated with the data. + * @param n_keys + * Number of keys to add + * @param key_found + * After successful invocation, key_found for every item in the array is set + * to a value different than 0 if the current key is already present in the + * table and to 0 if not. This pointer has to be set to a valid memory + * location before the table entry add function is called. + * @param entries_ptr + * After successful invocation, array *entries_ptr stores the handle to the + * table entry containing the data associated with every key. This handle can + * be used to perform further read-write accesses to this entry. This handle + * is valid until the key is deleted from the table or the same key is + * re-added to the table, typically to associate it with different data. This + * pointer has to be set to a valid memory location before the function is + * called. + * @return + * 0 on success, error code otherwise + */ +typedef int (*rte_table_op_entry_add_bulk)( + void *table, + void **keys, + void **entries, + uint32_t n_keys, + int *key_found, + void **entries_ptr); + +/** + * Lookup table entry delete bulk + * + * @param table + * Handle to lookup table instance + * @param keys + * Array containing lookup keys + * @param n_keys + * Number of keys to delete + * @param key_found + * After successful invocation, key_found for every item in the array is set + * to a value different than 0if the current key was present in the table + * before the delete operation was performed and to 0 if not. This pointer + * has to be set to a valid memory location before the table entry delete + * function is called. + * @param entries + * If entries pointer is NULL, this pointer is ignored for every entry found. + * Else, after successful invocation, if specific key is found in the table + * (key_found is different than 0 for this item after function call is + * completed) and item of entry array points to a valid buffer (entry is set + * to a value different than NULL before the function is called), then the + * first entry_size bytes (table create parameter) in *entry store a copy of + * table entry that contained the data associated with the current key before + * the key was deleted. + * @return + * 0 on success, error code otherwise + */ +typedef int (*rte_table_op_entry_delete_bulk)( + void *table, + void **keys, + uint32_t n_keys, + int *key_found, + void **entries); + +/** + * Lookup table lookup + * + * @param table + * Handle to lookup table instance + * @param pkts + * Burst of input packets specified as array of up to 64 pointers to struct + * rte_mbuf + * @param pkts_mask + * 64-bit bitmask specifying which packets in the input burst are valid. When + * pkts_mask bit n is set, then element n of pkts array is pointing to a + * valid packet. Otherwise, element n of pkts array does not point to a valid + * packet, therefore it will not be accessed. + * @param lookup_hit_mask + * Once the table lookup operation is completed, this 64-bit bitmask + * specifies which of the valid packets in the input burst resulted in lookup + * hit. For each valid input packet (pkts_mask bit n is set), the following + * are true on lookup hit: lookup_hit_mask bit n is set, element n of entries + * array is valid and it points to the lookup table entry that was hit. For + * each valid input packet (pkts_mask bit n is set), the following are true + * on lookup miss: lookup_hit_mask bit n is not set and element n of entries + * array is not valid. + * @param entries + * Once the table lookup operation is completed, this array provides the + * lookup table entries that were hit, as described above. It is required + * that this array is always pre-allocated by the caller of this function + * with exactly 64 elements. The implementation is allowed to speculatively + * modify the elements of this array, so elements marked as invalid in + * lookup_hit_mask once the table lookup operation is completed might have + * been modified by this function. + * @return + * 0 on success, error code otherwise + */ +typedef int (*rte_table_op_lookup)( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries); + +/** + * Lookup table stats read + * + * @param table + * Handle to lookup table instance + * @param stats + * Handle to table stats struct to copy data + * @param clear + * Flag indicating that stats should be cleared after read + * + * @return + * Error code or 0 on success. + */ +typedef int (*rte_table_op_stats_read)( + void *table, + struct rte_table_stats *stats, + int clear); + +/** Lookup table interface defining the lookup table operation */ +struct rte_table_ops { + rte_table_op_create f_create; /**< Create */ + rte_table_op_free f_free; /**< Free */ + rte_table_op_entry_add f_add; /**< Entry add */ + rte_table_op_entry_delete f_delete; /**< Entry delete */ + rte_table_op_entry_add_bulk f_add_bulk; /**< Add entry bulk */ + rte_table_op_entry_delete_bulk f_delete_bulk; /**< Delete entry bulk */ + rte_table_op_lookup f_lookup; /**< Lookup */ + rte_table_op_stats_read f_stats; /**< Stats */ +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_table_acl.c b/lib/table/rte_table_acl.c new file mode 100644 index 0000000000..14d54019f0 --- /dev/null +++ b/lib/table/rte_table_acl.c @@ -0,0 +1,798 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#include <string.h> +#include <stdio.h> + +#include <rte_common.h> +#include <rte_mbuf.h> +#include <rte_memory.h> +#include <rte_malloc.h> +#include <rte_log.h> + +#include "rte_table_acl.h" +#include <rte_ether.h> + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_ACL_STATS_PKTS_IN_ADD(table, val) \ + table->stats.n_pkts_in += val +#define RTE_TABLE_ACL_STATS_PKTS_LOOKUP_MISS(table, val) \ + table->stats.n_pkts_lookup_miss += val + +#else + +#define RTE_TABLE_ACL_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_ACL_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + +struct rte_table_acl { + struct rte_table_stats stats; + + /* Low-level ACL table */ + char name[2][RTE_ACL_NAMESIZE]; + struct rte_acl_param acl_params; /* for creating low level acl table */ + struct rte_acl_config cfg; /* Holds the field definitions (metadata) */ + struct rte_acl_ctx *ctx; + uint32_t name_id; + + /* Input parameters */ + uint32_t n_rules; + uint32_t entry_size; + + /* Internal tables */ + uint8_t *action_table; + struct rte_acl_rule **acl_rule_list; /* Array of pointers to rules */ + uint8_t *acl_rule_memory; /* Memory to store the rules */ + + /* Memory to store the action table and stack of free entries */ + uint8_t memory[0] __rte_cache_aligned; +}; + + +static void * +rte_table_acl_create( + void *params, + int socket_id, + uint32_t entry_size) +{ + struct rte_table_acl_params *p = params; + struct rte_table_acl *acl; + uint32_t action_table_size, acl_rule_list_size, acl_rule_memory_size; + uint32_t total_size; + + RTE_BUILD_BUG_ON(((sizeof(struct rte_table_acl) % RTE_CACHE_LINE_SIZE) + != 0)); + + /* Check input parameters */ + if (p == NULL) { + RTE_LOG(ERR, TABLE, "%s: Invalid value for params\n", __func__); + return NULL; + } + if (p->name == NULL) { + RTE_LOG(ERR, TABLE, "%s: Invalid value for name\n", __func__); + return NULL; + } + if (p->n_rules == 0) { + RTE_LOG(ERR, TABLE, "%s: Invalid value for n_rules\n", + __func__); + return NULL; + } + if ((p->n_rule_fields == 0) || + (p->n_rule_fields > RTE_ACL_MAX_FIELDS)) { + RTE_LOG(ERR, TABLE, "%s: Invalid value for n_rule_fields\n", + __func__); + return NULL; + } + + entry_size = RTE_ALIGN(entry_size, sizeof(uint64_t)); + + /* Memory allocation */ + action_table_size = RTE_CACHE_LINE_ROUNDUP(p->n_rules * entry_size); + acl_rule_list_size = + RTE_CACHE_LINE_ROUNDUP(p->n_rules * sizeof(struct rte_acl_rule *)); + acl_rule_memory_size = RTE_CACHE_LINE_ROUNDUP(p->n_rules * + RTE_ACL_RULE_SZ(p->n_rule_fields)); + total_size = sizeof(struct rte_table_acl) + action_table_size + + acl_rule_list_size + acl_rule_memory_size; + + acl = rte_zmalloc_socket("TABLE", total_size, RTE_CACHE_LINE_SIZE, + socket_id); + if (acl == NULL) { + RTE_LOG(ERR, TABLE, + "%s: Cannot allocate %u bytes for ACL table\n", + __func__, total_size); + return NULL; + } + + acl->action_table = &acl->memory[0]; + acl->acl_rule_list = + (struct rte_acl_rule **) &acl->memory[action_table_size]; + acl->acl_rule_memory = (uint8_t *) + &acl->memory[action_table_size + acl_rule_list_size]; + + /* Initialization of internal fields */ + snprintf(acl->name[0], RTE_ACL_NAMESIZE, "%s_a", p->name); + snprintf(acl->name[1], RTE_ACL_NAMESIZE, "%s_b", p->name); + acl->name_id = 1; + + acl->acl_params.name = acl->name[acl->name_id]; + acl->acl_params.socket_id = socket_id; + acl->acl_params.rule_size = RTE_ACL_RULE_SZ(p->n_rule_fields); + acl->acl_params.max_rule_num = p->n_rules; + + acl->cfg.num_categories = 1; + acl->cfg.num_fields = p->n_rule_fields; + memcpy(&acl->cfg.defs[0], &p->field_format[0], + p->n_rule_fields * sizeof(struct rte_acl_field_def)); + + acl->ctx = NULL; + + acl->n_rules = p->n_rules; + acl->entry_size = entry_size; + + return acl; +} + +static int +rte_table_acl_free(void *table) +{ + struct rte_table_acl *acl = table; + + /* Check input parameters */ + if (table == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + + /* Free previously allocated resources */ + if (acl->ctx != NULL) + rte_acl_free(acl->ctx); + + rte_free(acl); + + return 0; +} + +RTE_ACL_RULE_DEF(rte_pipeline_acl_rule, RTE_ACL_MAX_FIELDS); + +static int +rte_table_acl_build(struct rte_table_acl *acl, struct rte_acl_ctx **acl_ctx) +{ + struct rte_acl_ctx *ctx = NULL; + uint32_t n_rules, i; + int status; + + /* Create low level ACL table */ + ctx = rte_acl_create(&acl->acl_params); + if (ctx == NULL) { + RTE_LOG(ERR, TABLE, "%s: Cannot create low level ACL table\n", + __func__); + return -1; + } + + /* Add rules to low level ACL table */ + n_rules = 0; + for (i = 1; i < acl->n_rules; i++) { + if (acl->acl_rule_list[i] != NULL) { + status = rte_acl_add_rules(ctx, acl->acl_rule_list[i], + 1); + if (status != 0) { + RTE_LOG(ERR, TABLE, + "%s: Cannot add rule to low level ACL table\n", + __func__); + rte_acl_free(ctx); + return -1; + } + + n_rules++; + } + } + + if (n_rules == 0) { + rte_acl_free(ctx); + *acl_ctx = NULL; + return 0; + } + + /* Build low level ACl table */ + status = rte_acl_build(ctx, &acl->cfg); + if (status != 0) { + RTE_LOG(ERR, TABLE, + "%s: Cannot build the low level ACL table\n", + __func__); + rte_acl_free(ctx); + return -1; + } + + *acl_ctx = ctx; + return 0; +} + +static int +rte_table_acl_entry_add( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr) +{ + struct rte_table_acl *acl = table; + struct rte_table_acl_rule_add_params *rule = + key; + struct rte_pipeline_acl_rule acl_rule; + struct rte_acl_rule *rule_location; + struct rte_acl_ctx *ctx; + uint32_t free_pos, free_pos_valid, i; + int status; + + /* Check input parameters */ + if (table == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + if (key == NULL) { + RTE_LOG(ERR, TABLE, "%s: key parameter is NULL\n", __func__); + return -EINVAL; + } + if (entry == NULL) { + RTE_LOG(ERR, TABLE, "%s: entry parameter is NULL\n", __func__); + return -EINVAL; + } + if (key_found == NULL) { + RTE_LOG(ERR, TABLE, "%s: key_found parameter is NULL\n", + __func__); + return -EINVAL; + } + if (entry_ptr == NULL) { + RTE_LOG(ERR, TABLE, "%s: entry_ptr parameter is NULL\n", + __func__); + return -EINVAL; + } + if (rule->priority > RTE_ACL_MAX_PRIORITY) { + RTE_LOG(ERR, TABLE, "%s: Priority is too high\n", __func__); + return -EINVAL; + } + + /* Setup rule data structure */ + memset(&acl_rule, 0, sizeof(acl_rule)); + acl_rule.data.category_mask = 1; + acl_rule.data.priority = RTE_ACL_MAX_PRIORITY - rule->priority; + acl_rule.data.userdata = 0; /* To be set up later */ + memcpy(&acl_rule.field[0], + &rule->field_value[0], + acl->cfg.num_fields * sizeof(struct rte_acl_field)); + + /* Look to see if the rule exists already in the table */ + free_pos = 0; + free_pos_valid = 0; + for (i = 1; i < acl->n_rules; i++) { + if (acl->acl_rule_list[i] == NULL) { + if (free_pos_valid == 0) { + free_pos = i; + free_pos_valid = 1; + } + + continue; + } + + /* Compare the key fields */ + status = memcmp(&acl->acl_rule_list[i]->field[0], + &rule->field_value[0], + acl->cfg.num_fields * sizeof(struct rte_acl_field)); + + /* Rule found: update data associated with the rule */ + if (status == 0) { + *key_found = 1; + *entry_ptr = &acl->memory[i * acl->entry_size]; + memcpy(*entry_ptr, entry, acl->entry_size); + + return 0; + } + } + + /* Return if max rules */ + if (free_pos_valid == 0) { + RTE_LOG(ERR, TABLE, "%s: Max number of rules reached\n", + __func__); + return -ENOSPC; + } + + /* Add the new rule to the rule set */ + acl_rule.data.userdata = free_pos; + rule_location = (struct rte_acl_rule *) + &acl->acl_rule_memory[free_pos * acl->acl_params.rule_size]; + memcpy(rule_location, &acl_rule, acl->acl_params.rule_size); + acl->acl_rule_list[free_pos] = rule_location; + + /* Build low level ACL table */ + acl->name_id ^= 1; + acl->acl_params.name = acl->name[acl->name_id]; + status = rte_table_acl_build(acl, &ctx); + if (status != 0) { + /* Roll back changes */ + acl->acl_rule_list[free_pos] = NULL; + acl->name_id ^= 1; + + return -EINVAL; + } + + /* Commit changes */ + if (acl->ctx != NULL) + rte_acl_free(acl->ctx); + acl->ctx = ctx; + *key_found = 0; + *entry_ptr = &acl->memory[free_pos * acl->entry_size]; + memcpy(*entry_ptr, entry, acl->entry_size); + + return 0; +} + +static int +rte_table_acl_entry_delete( + void *table, + void *key, + int *key_found, + void *entry) +{ + struct rte_table_acl *acl = table; + struct rte_table_acl_rule_delete_params *rule = + key; + struct rte_acl_rule *deleted_rule = NULL; + struct rte_acl_ctx *ctx; + uint32_t pos, pos_valid, i; + int status; + + /* Check input parameters */ + if (table == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + if (key == NULL) { + RTE_LOG(ERR, TABLE, "%s: key parameter is NULL\n", __func__); + return -EINVAL; + } + if (key_found == NULL) { + RTE_LOG(ERR, TABLE, "%s: key_found parameter is NULL\n", + __func__); + return -EINVAL; + } + + /* Look for the rule in the table */ + pos = 0; + pos_valid = 0; + for (i = 1; i < acl->n_rules; i++) { + if (acl->acl_rule_list[i] != NULL) { + /* Compare the key fields */ + status = memcmp(&acl->acl_rule_list[i]->field[0], + &rule->field_value[0], acl->cfg.num_fields * + sizeof(struct rte_acl_field)); + + /* Rule found: remove from table */ + if (status == 0) { + pos = i; + pos_valid = 1; + + deleted_rule = acl->acl_rule_list[i]; + acl->acl_rule_list[i] = NULL; + } + } + } + + /* Return if rule not found */ + if (pos_valid == 0) { + *key_found = 0; + return 0; + } + + /* Build low level ACL table */ + acl->name_id ^= 1; + acl->acl_params.name = acl->name[acl->name_id]; + status = rte_table_acl_build(acl, &ctx); + if (status != 0) { + /* Roll back changes */ + acl->acl_rule_list[pos] = deleted_rule; + acl->name_id ^= 1; + + return -EINVAL; + } + + /* Commit changes */ + if (acl->ctx != NULL) + rte_acl_free(acl->ctx); + + acl->ctx = ctx; + *key_found = 1; + if (entry != NULL) + memcpy(entry, &acl->memory[pos * acl->entry_size], + acl->entry_size); + + return 0; +} + +static int +rte_table_acl_entry_add_bulk( + void *table, + void **keys, + void **entries, + uint32_t n_keys, + int *key_found, + void **entries_ptr) +{ + struct rte_table_acl *acl = table; + struct rte_acl_ctx *ctx; + uint32_t rule_pos[n_keys]; + uint32_t i; + int err = 0, build = 0; + int status; + + /* Check input parameters */ + if (table == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + if (keys == NULL) { + RTE_LOG(ERR, TABLE, "%s: keys parameter is NULL\n", __func__); + return -EINVAL; + } + if (entries == NULL) { + RTE_LOG(ERR, TABLE, "%s: entries parameter is NULL\n", __func__); + return -EINVAL; + } + if (n_keys == 0) { + RTE_LOG(ERR, TABLE, "%s: 0 rules to add\n", __func__); + return -EINVAL; + } + if (key_found == NULL) { + RTE_LOG(ERR, TABLE, "%s: key_found parameter is NULL\n", + __func__); + return -EINVAL; + } + if (entries_ptr == NULL) { + RTE_LOG(ERR, TABLE, "%s: entries_ptr parameter is NULL\n", + __func__); + return -EINVAL; + } + + /* Check input parameters in arrays */ + for (i = 0; i < n_keys; i++) { + struct rte_table_acl_rule_add_params *rule; + + if (keys[i] == NULL) { + RTE_LOG(ERR, TABLE, "%s: keys[%" PRIu32 "] parameter is NULL\n", + __func__, i); + return -EINVAL; + } + + if (entries[i] == NULL) { + RTE_LOG(ERR, TABLE, "%s: entries[%" PRIu32 "] parameter is NULL\n", + __func__, i); + return -EINVAL; + } + + rule = keys[i]; + if (rule->priority > RTE_ACL_MAX_PRIORITY) { + RTE_LOG(ERR, TABLE, "%s: Priority is too high\n", __func__); + return -EINVAL; + } + } + + memset(rule_pos, 0, n_keys * sizeof(uint32_t)); + memset(key_found, 0, n_keys * sizeof(int)); + for (i = 0; i < n_keys; i++) { + struct rte_table_acl_rule_add_params *rule = + keys[i]; + struct rte_pipeline_acl_rule acl_rule; + struct rte_acl_rule *rule_location; + uint32_t free_pos, free_pos_valid, j; + + /* Setup rule data structure */ + memset(&acl_rule, 0, sizeof(acl_rule)); + acl_rule.data.category_mask = 1; + acl_rule.data.priority = RTE_ACL_MAX_PRIORITY - rule->priority; + acl_rule.data.userdata = 0; /* To be set up later */ + memcpy(&acl_rule.field[0], + &rule->field_value[0], + acl->cfg.num_fields * sizeof(struct rte_acl_field)); + + /* Look to see if the rule exists already in the table */ + free_pos = 0; + free_pos_valid = 0; + for (j = 1; j < acl->n_rules; j++) { + if (acl->acl_rule_list[j] == NULL) { + if (free_pos_valid == 0) { + free_pos = j; + free_pos_valid = 1; + } + + continue; + } + + /* Compare the key fields */ + status = memcmp(&acl->acl_rule_list[j]->field[0], + &rule->field_value[0], + acl->cfg.num_fields * sizeof(struct rte_acl_field)); + + /* Rule found: update data associated with the rule */ + if (status == 0) { + key_found[i] = 1; + entries_ptr[i] = &acl->memory[j * acl->entry_size]; + memcpy(entries_ptr[i], entries[i], acl->entry_size); + + break; + } + } + + /* Key already in the table */ + if (key_found[i] != 0) + continue; + + /* Maximum number of rules reached */ + if (free_pos_valid == 0) { + err = 1; + break; + } + + /* Add the new rule to the rule set */ + acl_rule.data.userdata = free_pos; + rule_location = (struct rte_acl_rule *) + &acl->acl_rule_memory[free_pos * acl->acl_params.rule_size]; + memcpy(rule_location, &acl_rule, acl->acl_params.rule_size); + acl->acl_rule_list[free_pos] = rule_location; + rule_pos[i] = free_pos; + build = 1; + } + + if (err != 0) { + for (i = 0; i < n_keys; i++) { + if (rule_pos[i] == 0) + continue; + + acl->acl_rule_list[rule_pos[i]] = NULL; + } + + return -ENOSPC; + } + + if (build == 0) + return 0; + + /* Build low level ACL table */ + acl->name_id ^= 1; + acl->acl_params.name = acl->name[acl->name_id]; + status = rte_table_acl_build(acl, &ctx); + if (status != 0) { + /* Roll back changes */ + for (i = 0; i < n_keys; i++) { + if (rule_pos[i] == 0) + continue; + + acl->acl_rule_list[rule_pos[i]] = NULL; + } + acl->name_id ^= 1; + + return -EINVAL; + } + + /* Commit changes */ + if (acl->ctx != NULL) + rte_acl_free(acl->ctx); + acl->ctx = ctx; + + for (i = 0; i < n_keys; i++) { + if (rule_pos[i] == 0) + continue; + + key_found[i] = 0; + entries_ptr[i] = &acl->memory[rule_pos[i] * acl->entry_size]; + memcpy(entries_ptr[i], entries[i], acl->entry_size); + } + + return 0; +} + +static int +rte_table_acl_entry_delete_bulk( + void *table, + void **keys, + uint32_t n_keys, + int *key_found, + void **entries) +{ + struct rte_table_acl *acl = table; + struct rte_acl_rule *deleted_rules[n_keys]; + uint32_t rule_pos[n_keys]; + struct rte_acl_ctx *ctx; + uint32_t i; + int status; + int build = 0; + + /* Check input parameters */ + if (table == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + if (keys == NULL) { + RTE_LOG(ERR, TABLE, "%s: key parameter is NULL\n", __func__); + return -EINVAL; + } + if (n_keys == 0) { + RTE_LOG(ERR, TABLE, "%s: 0 rules to delete\n", __func__); + return -EINVAL; + } + if (key_found == NULL) { + RTE_LOG(ERR, TABLE, "%s: key_found parameter is NULL\n", + __func__); + return -EINVAL; + } + + for (i = 0; i < n_keys; i++) { + if (keys[i] == NULL) { + RTE_LOG(ERR, TABLE, "%s: keys[%" PRIu32 "] parameter is NULL\n", + __func__, i); + return -EINVAL; + } + } + + memset(deleted_rules, 0, n_keys * sizeof(struct rte_acl_rule *)); + memset(rule_pos, 0, n_keys * sizeof(uint32_t)); + for (i = 0; i < n_keys; i++) { + struct rte_table_acl_rule_delete_params *rule = + keys[i]; + uint32_t pos_valid, j; + + /* Look for the rule in the table */ + pos_valid = 0; + for (j = 1; j < acl->n_rules; j++) { + if (acl->acl_rule_list[j] == NULL) + continue; + + /* Compare the key fields */ + status = memcmp(&acl->acl_rule_list[j]->field[0], + &rule->field_value[0], + acl->cfg.num_fields * sizeof(struct rte_acl_field)); + + /* Rule found: remove from table */ + if (status == 0) { + pos_valid = 1; + + deleted_rules[i] = acl->acl_rule_list[j]; + acl->acl_rule_list[j] = NULL; + rule_pos[i] = j; + + build = 1; + } + } + + if (pos_valid == 0) { + key_found[i] = 0; + continue; + } + } + + /* Return if no changes to acl table */ + if (build == 0) { + return 0; + } + + /* Build low level ACL table */ + acl->name_id ^= 1; + acl->acl_params.name = acl->name[acl->name_id]; + status = rte_table_acl_build(acl, &ctx); + if (status != 0) { + /* Roll back changes */ + for (i = 0; i < n_keys; i++) { + if (rule_pos[i] == 0) + continue; + + acl->acl_rule_list[rule_pos[i]] = deleted_rules[i]; + } + + acl->name_id ^= 1; + + return -EINVAL; + } + + /* Commit changes */ + if (acl->ctx != NULL) + rte_acl_free(acl->ctx); + + acl->ctx = ctx; + for (i = 0; i < n_keys; i++) { + if (rule_pos[i] == 0) + continue; + + key_found[i] = 1; + if (entries != NULL && entries[i] != NULL) + memcpy(entries[i], &acl->memory[rule_pos[i] * acl->entry_size], + acl->entry_size); + } + + return 0; +} + +static int +rte_table_acl_lookup( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_acl *acl = (struct rte_table_acl *) table; + const uint8_t *pkts_data[RTE_PORT_IN_BURST_SIZE_MAX]; + uint32_t results[RTE_PORT_IN_BURST_SIZE_MAX]; + uint64_t pkts_out_mask; + uint32_t n_pkts, i, j; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_ACL_STATS_PKTS_IN_ADD(acl, n_pkts_in); + + /* Input conversion */ + for (i = 0, j = 0; i < (uint32_t)(RTE_PORT_IN_BURST_SIZE_MAX - + __builtin_clzll(pkts_mask)); i++) { + uint64_t pkt_mask = 1LLU << i; + + if (pkt_mask & pkts_mask) { + pkts_data[j] = rte_pktmbuf_mtod(pkts[i], uint8_t *); + j++; + } + } + n_pkts = j; + + /* Low-level ACL table lookup */ + if (acl->ctx != NULL) + rte_acl_classify(acl->ctx, pkts_data, results, n_pkts, 1); + else + n_pkts = 0; + + /* Output conversion */ + pkts_out_mask = 0; + for (i = 0; i < n_pkts; i++) { + uint32_t action_table_pos = results[i]; + uint32_t pkt_pos = __builtin_ctzll(pkts_mask); + uint64_t pkt_mask = 1LLU << pkt_pos; + + pkts_mask &= ~pkt_mask; + + if (action_table_pos != 0) { + pkts_out_mask |= pkt_mask; + entries[pkt_pos] = (void *) + &acl->memory[action_table_pos * + acl->entry_size]; + rte_prefetch0(entries[pkt_pos]); + } + } + + *lookup_hit_mask = pkts_out_mask; + RTE_TABLE_ACL_STATS_PKTS_LOOKUP_MISS(acl, n_pkts_in - __builtin_popcountll(pkts_out_mask)); + + return 0; +} + +static int +rte_table_acl_stats_read(void *table, struct rte_table_stats *stats, int clear) +{ + struct rte_table_acl *acl = table; + + if (stats != NULL) + memcpy(stats, &acl->stats, sizeof(acl->stats)); + + if (clear) + memset(&acl->stats, 0, sizeof(acl->stats)); + + return 0; +} + +struct rte_table_ops rte_table_acl_ops = { + .f_create = rte_table_acl_create, + .f_free = rte_table_acl_free, + .f_add = rte_table_acl_entry_add, + .f_delete = rte_table_acl_entry_delete, + .f_add_bulk = rte_table_acl_entry_add_bulk, + .f_delete_bulk = rte_table_acl_entry_delete_bulk, + .f_lookup = rte_table_acl_lookup, + .f_stats = rte_table_acl_stats_read, +}; diff --git a/lib/table/rte_table_acl.h b/lib/table/rte_table_acl.h new file mode 100644 index 0000000000..516819213c --- /dev/null +++ b/lib/table/rte_table_acl.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_TABLE_ACL_H__ +#define __INCLUDE_RTE_TABLE_ACL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE Table ACL + * + * This table uses the Access Control List (ACL) algorithm to uniquely + * associate data to lookup keys. + * + * Use-cases: Firewall rule database, etc. + * + ***/ + +#include <stdint.h> + +#include "rte_acl.h" + +#include "rte_table.h" + +/** ACL table parameters */ +struct rte_table_acl_params { + /** Name */ + const char *name; + + /** Maximum number of ACL rules in the table */ + uint32_t n_rules; + + /** Number of fields in the ACL rule specification */ + uint32_t n_rule_fields; + + /** Format specification of the fields of the ACL rule */ + struct rte_acl_field_def field_format[RTE_ACL_MAX_FIELDS]; +}; + +/** ACL rule specification for entry add operation */ +struct rte_table_acl_rule_add_params { + /** ACL rule priority, with 0 as the highest priority */ + int32_t priority; + + /** Values for the fields of the ACL rule to be added to the table */ + struct rte_acl_field field_value[RTE_ACL_MAX_FIELDS]; +}; + +/** ACL rule specification for entry delete operation */ +struct rte_table_acl_rule_delete_params { + /** Values for the fields of the ACL rule to be deleted from table */ + struct rte_acl_field field_value[RTE_ACL_MAX_FIELDS]; +}; + +/** ACL table operations */ +extern struct rte_table_ops rte_table_acl_ops; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_table_array.c b/lib/table/rte_table_array.c new file mode 100644 index 0000000000..8264c50c27 --- /dev/null +++ b/lib/table/rte_table_array.c @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#include <string.h> +#include <stdio.h> + +#include <rte_common.h> +#include <rte_mbuf.h> +#include <rte_memory.h> +#include <rte_malloc.h> +#include <rte_log.h> + +#include "rte_table_array.h" + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_ARRAY_STATS_PKTS_IN_ADD(table, val) \ + table->stats.n_pkts_in += val +#define RTE_TABLE_ARRAY_STATS_PKTS_LOOKUP_MISS(table, val) \ + table->stats.n_pkts_lookup_miss += val + +#else + +#define RTE_TABLE_ARRAY_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_ARRAY_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + +struct rte_table_array { + struct rte_table_stats stats; + + /* Input parameters */ + uint32_t entry_size; + uint32_t n_entries; + uint32_t offset; + + /* Internal fields */ + uint32_t entry_pos_mask; + + /* Internal table */ + uint8_t array[0] __rte_cache_aligned; +} __rte_cache_aligned; + +static void * +rte_table_array_create(void *params, int socket_id, uint32_t entry_size) +{ + struct rte_table_array_params *p = params; + struct rte_table_array *t; + uint32_t total_cl_size, total_size; + + /* Check input parameters */ + if ((p == NULL) || + (p->n_entries == 0) || + (!rte_is_power_of_2(p->n_entries))) + return NULL; + + /* Memory allocation */ + total_cl_size = (sizeof(struct rte_table_array) + + RTE_CACHE_LINE_SIZE) / RTE_CACHE_LINE_SIZE; + total_cl_size += (p->n_entries * entry_size + + RTE_CACHE_LINE_SIZE) / RTE_CACHE_LINE_SIZE; + total_size = total_cl_size * RTE_CACHE_LINE_SIZE; + t = rte_zmalloc_socket("TABLE", total_size, RTE_CACHE_LINE_SIZE, socket_id); + if (t == NULL) { + RTE_LOG(ERR, TABLE, + "%s: Cannot allocate %u bytes for array table\n", + __func__, total_size); + return NULL; + } + + /* Memory initialization */ + t->entry_size = entry_size; + t->n_entries = p->n_entries; + t->offset = p->offset; + t->entry_pos_mask = t->n_entries - 1; + + return t; +} + +static int +rte_table_array_free(void *table) +{ + struct rte_table_array *t = table; + + /* Check input parameters */ + if (t == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + + /* Free previously allocated resources */ + rte_free(t); + + return 0; +} + +static int +rte_table_array_entry_add( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr) +{ + struct rte_table_array *t = table; + struct rte_table_array_key *k = key; + uint8_t *table_entry; + + /* Check input parameters */ + if (table == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + if (key == NULL) { + RTE_LOG(ERR, TABLE, "%s: key parameter is NULL\n", __func__); + return -EINVAL; + } + if (entry == NULL) { + RTE_LOG(ERR, TABLE, "%s: entry parameter is NULL\n", __func__); + return -EINVAL; + } + if (key_found == NULL) { + RTE_LOG(ERR, TABLE, "%s: key_found parameter is NULL\n", + __func__); + return -EINVAL; + } + if (entry_ptr == NULL) { + RTE_LOG(ERR, TABLE, "%s: entry_ptr parameter is NULL\n", + __func__); + return -EINVAL; + } + + table_entry = &t->array[k->pos * t->entry_size]; + memcpy(table_entry, entry, t->entry_size); + *key_found = 1; + *entry_ptr = (void *) table_entry; + + return 0; +} + +static int +rte_table_array_lookup( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_array *t = (struct rte_table_array *) table; + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_ARRAY_STATS_PKTS_IN_ADD(t, n_pkts_in); + *lookup_hit_mask = pkts_mask; + + if ((pkts_mask & (pkts_mask + 1)) == 0) { + uint64_t n_pkts = __builtin_popcountll(pkts_mask); + uint32_t i; + + for (i = 0; i < n_pkts; i++) { + struct rte_mbuf *pkt = pkts[i]; + uint32_t entry_pos = RTE_MBUF_METADATA_UINT32(pkt, + t->offset) & t->entry_pos_mask; + + entries[i] = (void *) &t->array[entry_pos * + t->entry_size]; + } + } else { + for ( ; pkts_mask; ) { + uint32_t pkt_index = __builtin_ctzll(pkts_mask); + uint64_t pkt_mask = 1LLU << pkt_index; + struct rte_mbuf *pkt = pkts[pkt_index]; + uint32_t entry_pos = RTE_MBUF_METADATA_UINT32(pkt, + t->offset) & t->entry_pos_mask; + + entries[pkt_index] = (void *) &t->array[entry_pos * + t->entry_size]; + pkts_mask &= ~pkt_mask; + } + } + + return 0; +} + +static int +rte_table_array_stats_read(void *table, struct rte_table_stats *stats, int clear) +{ + struct rte_table_array *array = table; + + if (stats != NULL) + memcpy(stats, &array->stats, sizeof(array->stats)); + + if (clear) + memset(&array->stats, 0, sizeof(array->stats)); + + return 0; +} + +struct rte_table_ops rte_table_array_ops = { + .f_create = rte_table_array_create, + .f_free = rte_table_array_free, + .f_add = rte_table_array_entry_add, + .f_delete = NULL, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_array_lookup, + .f_stats = rte_table_array_stats_read, +}; diff --git a/lib/table/rte_table_array.h b/lib/table/rte_table_array.h new file mode 100644 index 0000000000..b16c5dfe5b --- /dev/null +++ b/lib/table/rte_table_array.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_TABLE_ARRAY_H__ +#define __INCLUDE_RTE_TABLE_ARRAY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE Table Array + * + * Simple array indexing. Lookup key is the array entry index. + * + ***/ + +#include <stdint.h> + +#include "rte_table.h" + +/** Array table parameters */ +struct rte_table_array_params { + /** Number of array entries. Has to be a power of two. */ + uint32_t n_entries; + + /** Byte offset within input packet meta-data where lookup key (i.e. the + array entry index) is located. */ + uint32_t offset; +}; + +/** Array table key format */ +struct rte_table_array_key { + /** Array entry index */ + uint32_t pos; +}; + +/** Array table operations */ +extern struct rte_table_ops rte_table_array_ops; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_table_hash.h b/lib/table/rte_table_hash.h new file mode 100644 index 0000000000..61a0eed6c5 --- /dev/null +++ b/lib/table/rte_table_hash.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2017 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_TABLE_HASH_H__ +#define __INCLUDE_RTE_TABLE_HASH_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE Table Hash + * + * These tables use the exact match criterion to uniquely associate data to + * lookup keys. + * + * Hash table types: + * 1. Entry add strategy on bucket full: + * a. Least Recently Used (LRU): One of the existing keys in the bucket is + * deleted and the new key is added in its place. The number of keys in + * each bucket never grows bigger than 4. The logic to pick the key to + * be dropped from the bucket is LRU. The hash table lookup operation + * maintains the order in which the keys in the same bucket are hit, so + * every time a key is hit, it becomes the new Most Recently Used (MRU) + * key, i.e. the most unlikely candidate for drop. When a key is added + * to the bucket, it also becomes the new MRU key. When a key needs to + * be picked and dropped, the most likely candidate for drop, i.e. the + * current LRU key, is always picked. The LRU logic requires maintaining + * specific data structures per each bucket. Use-cases: flow cache, etc. + * b. Extendable bucket (ext): The bucket is extended with space for 4 more + * keys. This is done by allocating additional memory at table init time, + * which is used to create a pool of free keys (the size of this pool is + * configurable and always a multiple of 4). On key add operation, the + * allocation of a group of 4 keys only happens successfully within the + * limit of free keys, otherwise the key add operation fails. On key + * delete operation, a group of 4 keys is freed back to the pool of free + * keys when the key to be deleted is the only key that was used within + * its group of 4 keys at that time. On key lookup operation, if the + * current bucket is in extended state and a match is not found in the + * first group of 4 keys, the search continues beyond the first group of + * 4 keys, potentially until all keys in this bucket are examined. The + * extendable bucket logic requires maintaining specific data structures + * per table and per each bucket. Use-cases: flow table, etc. + * 2. Key size: + * a. Configurable key size + * b. Single key size (8-byte, 16-byte or 32-byte key size) + * + ***/ +#include <stdint.h> + +#include "rte_table.h" + +/** Hash function */ +typedef uint64_t (*rte_table_hash_op_hash)( + void *key, + void *key_mask, + uint32_t key_size, + uint64_t seed); + +/** Hash table parameters */ +struct rte_table_hash_params { + /** Name */ + const char *name; + + /** Key size (number of bytes) */ + uint32_t key_size; + + /** Byte offset within packet meta-data where the key is located */ + uint32_t key_offset; + + /** Key mask */ + uint8_t *key_mask; + + /** Number of keys */ + uint32_t n_keys; + + /** Number of buckets */ + uint32_t n_buckets; + + /** Hash function */ + rte_table_hash_op_hash f_hash; + + /** Seed value for the hash function */ + uint64_t seed; +}; + +/** Extendable bucket hash table operations */ +extern struct rte_table_ops rte_table_hash_ext_ops; +extern struct rte_table_ops rte_table_hash_key8_ext_ops; +extern struct rte_table_ops rte_table_hash_key16_ext_ops; +extern struct rte_table_ops rte_table_hash_key32_ext_ops; + +/** LRU hash table operations */ +extern struct rte_table_ops rte_table_hash_lru_ops; + +extern struct rte_table_ops rte_table_hash_key8_lru_ops; +extern struct rte_table_ops rte_table_hash_key16_lru_ops; +extern struct rte_table_ops rte_table_hash_key32_lru_ops; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_table_hash_cuckoo.c b/lib/table/rte_table_hash_cuckoo.c new file mode 100644 index 0000000000..f024303330 --- /dev/null +++ b/lib/table/rte_table_hash_cuckoo.c @@ -0,0 +1,323 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2017 Intel Corporation + */ +#include <string.h> +#include <stdio.h> + +#include <rte_common.h> +#include <rte_mbuf.h> +#include <rte_memory.h> +#include <rte_malloc.h> +#include <rte_log.h> + +#include "rte_table_hash_cuckoo.h" + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_HASH_CUCKOO_STATS_PKTS_IN_ADD(table, val) \ + (table->stats.n_pkts_in += val) +#define RTE_TABLE_HASH_CUCKOO_STATS_PKTS_LOOKUP_MISS(table, val) \ + (table->stats.n_pkts_lookup_miss += val) + +#else + +#define RTE_TABLE_HASH_CUCKOO_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_HASH_CUCKOO_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + + +struct rte_table_hash { + struct rte_table_stats stats; + + /* Input parameters */ + uint32_t key_size; + uint32_t entry_size; + uint32_t n_keys; + rte_hash_function f_hash; + uint32_t seed; + uint32_t key_offset; + + /* cuckoo hash table object */ + struct rte_hash *h_table; + + /* Lookup table */ + uint8_t memory[0] __rte_cache_aligned; +}; + +static int +check_params_create_hash_cuckoo(struct rte_table_hash_cuckoo_params *params) +{ + if (params == NULL) { + RTE_LOG(ERR, TABLE, "NULL Input Parameters.\n"); + return -EINVAL; + } + + if (params->name == NULL) { + RTE_LOG(ERR, TABLE, "Table name is NULL.\n"); + return -EINVAL; + } + + if (params->key_size == 0) { + RTE_LOG(ERR, TABLE, "Invalid key_size.\n"); + return -EINVAL; + } + + if (params->n_keys == 0) { + RTE_LOG(ERR, TABLE, "Invalid n_keys.\n"); + return -EINVAL; + } + + if (params->f_hash == NULL) { + RTE_LOG(ERR, TABLE, "f_hash is NULL.\n"); + return -EINVAL; + } + + return 0; +} + +static void * +rte_table_hash_cuckoo_create(void *params, + int socket_id, + uint32_t entry_size) +{ + struct rte_table_hash_cuckoo_params *p = params; + struct rte_hash *h_table; + struct rte_table_hash *t; + uint32_t total_size; + + /* Check input parameters */ + if (check_params_create_hash_cuckoo(params)) + return NULL; + + /* Memory allocation */ + total_size = sizeof(struct rte_table_hash) + + RTE_CACHE_LINE_ROUNDUP(p->n_keys * entry_size); + + t = rte_zmalloc_socket(p->name, total_size, RTE_CACHE_LINE_SIZE, socket_id); + if (t == NULL) { + RTE_LOG(ERR, TABLE, + "%s: Cannot allocate %u bytes for cuckoo hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + + /* Create cuckoo hash table */ + struct rte_hash_parameters hash_cuckoo_params = { + .entries = p->n_keys, + .key_len = p->key_size, + .hash_func = p->f_hash, + .hash_func_init_val = p->seed, + .socket_id = socket_id, + .name = p->name + }; + + h_table = rte_hash_find_existing(p->name); + if (h_table == NULL) { + h_table = rte_hash_create(&hash_cuckoo_params); + if (h_table == NULL) { + RTE_LOG(ERR, TABLE, + "%s: failed to create cuckoo hash table %s\n", + __func__, p->name); + rte_free(t); + return NULL; + } + } + + /* initialize the cuckoo hash parameters */ + t->key_size = p->key_size; + t->entry_size = entry_size; + t->n_keys = p->n_keys; + t->f_hash = p->f_hash; + t->seed = p->seed; + t->key_offset = p->key_offset; + t->h_table = h_table; + + RTE_LOG(INFO, TABLE, + "%s: Cuckoo hash table %s memory footprint is %u bytes\n", + __func__, p->name, total_size); + return t; +} + +static int +rte_table_hash_cuckoo_free(void *table) { + struct rte_table_hash *t = table; + + if (table == NULL) + return -EINVAL; + + rte_hash_free(t->h_table); + rte_free(t); + + return 0; +} + +static int +rte_table_hash_cuckoo_entry_add(void *table, void *key, void *entry, + int *key_found, void **entry_ptr) +{ + struct rte_table_hash *t = table; + int pos = 0; + + /* Check input parameters */ + if ((table == NULL) || + (key == NULL) || + (entry == NULL) || + (key_found == NULL) || + (entry_ptr == NULL)) + return -EINVAL; + + /* Find Existing entries */ + pos = rte_hash_lookup(t->h_table, key); + if (pos >= 0) { + uint8_t *existing_entry; + + *key_found = 1; + existing_entry = &t->memory[pos * t->entry_size]; + memcpy(existing_entry, entry, t->entry_size); + *entry_ptr = existing_entry; + + return 0; + } + + if (pos == -ENOENT) { + /* Entry not found. Adding new entry */ + uint8_t *new_entry; + + pos = rte_hash_add_key(t->h_table, key); + if (pos < 0) + return pos; + + new_entry = &t->memory[pos * t->entry_size]; + memcpy(new_entry, entry, t->entry_size); + + *key_found = 0; + *entry_ptr = new_entry; + return 0; + } + + return pos; +} + +static int +rte_table_hash_cuckoo_entry_delete(void *table, void *key, + int *key_found, void *entry) +{ + struct rte_table_hash *t = table; + int pos = 0; + + /* Check input parameters */ + if ((table == NULL) || + (key == NULL) || + (key_found == NULL)) + return -EINVAL; + + pos = rte_hash_del_key(t->h_table, key); + if (pos >= 0) { + *key_found = 1; + uint8_t *entry_ptr = &t->memory[pos * t->entry_size]; + + if (entry) + memcpy(entry, entry_ptr, t->entry_size); + + memset(&t->memory[pos * t->entry_size], 0, t->entry_size); + return 0; + } + + *key_found = 0; + return pos; +} + +static int +rte_table_hash_cuckoo_lookup(void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *t = table; + uint64_t pkts_mask_out = 0; + uint32_t i; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + + RTE_TABLE_HASH_CUCKOO_STATS_PKTS_IN_ADD(t, n_pkts_in); + + if ((pkts_mask & (pkts_mask + 1)) == 0) { + const uint8_t *keys[RTE_PORT_IN_BURST_SIZE_MAX]; + int32_t positions[RTE_PORT_IN_BURST_SIZE_MAX], status; + + /* Keys for bulk lookup */ + for (i = 0; i < n_pkts_in; i++) + keys[i] = RTE_MBUF_METADATA_UINT8_PTR(pkts[i], + t->key_offset); + + /* Bulk Lookup */ + status = rte_hash_lookup_bulk(t->h_table, + (const void **) keys, + n_pkts_in, + positions); + if (status == 0) { + for (i = 0; i < n_pkts_in; i++) { + if (likely(positions[i] >= 0)) { + uint64_t pkt_mask = 1LLU << i; + + entries[i] = &t->memory[positions[i] + * t->entry_size]; + pkts_mask_out |= pkt_mask; + } + } + } + } else + for (i = 0; i < (uint32_t)(RTE_PORT_IN_BURST_SIZE_MAX + - __builtin_clzll(pkts_mask)); i++) { + uint64_t pkt_mask = 1LLU << i; + + if (pkt_mask & pkts_mask) { + struct rte_mbuf *pkt = pkts[i]; + uint8_t *key = RTE_MBUF_METADATA_UINT8_PTR(pkt, + t->key_offset); + int pos; + + pos = rte_hash_lookup(t->h_table, key); + if (likely(pos >= 0)) { + entries[i] = &t->memory[pos + * t->entry_size]; + pkts_mask_out |= pkt_mask; + } + } + } + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_CUCKOO_STATS_PKTS_LOOKUP_MISS(t, + n_pkts_in - __builtin_popcountll(pkts_mask_out)); + + return 0; + +} + +static int +rte_table_hash_cuckoo_stats_read(void *table, struct rte_table_stats *stats, + int clear) +{ + struct rte_table_hash *t = table; + + if (stats != NULL) + memcpy(stats, &t->stats, sizeof(t->stats)); + + if (clear) + memset(&t->stats, 0, sizeof(t->stats)); + + return 0; +} + +struct rte_table_ops rte_table_hash_cuckoo_ops = { + .f_create = rte_table_hash_cuckoo_create, + .f_free = rte_table_hash_cuckoo_free, + .f_add = rte_table_hash_cuckoo_entry_add, + .f_delete = rte_table_hash_cuckoo_entry_delete, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_hash_cuckoo_lookup, + .f_stats = rte_table_hash_cuckoo_stats_read, +}; diff --git a/lib/table/rte_table_hash_cuckoo.h b/lib/table/rte_table_hash_cuckoo.h new file mode 100644 index 0000000000..d9d4312190 --- /dev/null +++ b/lib/table/rte_table_hash_cuckoo.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_TABLE_HASH_CUCKOO_H__ +#define __INCLUDE_RTE_TABLE_HASH_CUCKOO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE Table Hash Cuckoo + * + ***/ +#include <stdint.h> + +#include <rte_hash.h> + +#include "rte_table.h" + +/** Hash table parameters */ +struct rte_table_hash_cuckoo_params { + /** Name */ + const char *name; + + /** Key size (number of bytes) */ + uint32_t key_size; + + /** Byte offset within packet meta-data where the key is located */ + uint32_t key_offset; + + /** Key mask */ + uint8_t *key_mask; + + /** Number of keys */ + uint32_t n_keys; + + /** Number of buckets */ + uint32_t n_buckets; + + /** Hash function */ + rte_hash_function f_hash; + + /** Seed value for the hash function */ + uint32_t seed; +}; + +/** Cuckoo hash table operations */ +extern struct rte_table_ops rte_table_hash_cuckoo_ops; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_table_hash_ext.c b/lib/table/rte_table_hash_ext.c new file mode 100644 index 0000000000..802a24fe0f --- /dev/null +++ b/lib/table/rte_table_hash_ext.c @@ -0,0 +1,1011 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2017 Intel Corporation + */ + +#include <string.h> +#include <stdio.h> + +#include <rte_common.h> +#include <rte_mbuf.h> +#include <rte_memory.h> +#include <rte_malloc.h> +#include <rte_log.h> + +#include "rte_table_hash.h" + +#define KEYS_PER_BUCKET 4 + +struct bucket { + union { + uintptr_t next; + uint64_t lru_list; + }; + uint16_t sig[KEYS_PER_BUCKET]; + uint32_t key_pos[KEYS_PER_BUCKET]; +}; + +#define BUCKET_NEXT(bucket) \ + ((void *) ((bucket)->next & (~1LU))) + +#define BUCKET_NEXT_VALID(bucket) \ + ((bucket)->next & 1LU) + +#define BUCKET_NEXT_SET(bucket, bucket_next) \ +do \ + (bucket)->next = (((uintptr_t) ((void *) (bucket_next))) | 1LU);\ +while (0) + +#define BUCKET_NEXT_SET_NULL(bucket) \ +do \ + (bucket)->next = 0; \ +while (0) + +#define BUCKET_NEXT_COPY(bucket, bucket2) \ +do \ + (bucket)->next = (bucket2)->next; \ +while (0) + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_HASH_EXT_STATS_PKTS_IN_ADD(table, val) \ + table->stats.n_pkts_in += val +#define RTE_TABLE_HASH_EXT_STATS_PKTS_LOOKUP_MISS(table, val) \ + table->stats.n_pkts_lookup_miss += val + +#else + +#define RTE_TABLE_HASH_EXT_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_HASH_EXT_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + +struct grinder { + struct bucket *bkt; + uint64_t sig; + uint64_t match; + uint32_t key_index; +}; + +struct rte_table_hash { + struct rte_table_stats stats; + + /* Input parameters */ + uint32_t key_size; + uint32_t entry_size; + uint32_t n_keys; + uint32_t n_buckets; + uint32_t n_buckets_ext; + rte_table_hash_op_hash f_hash; + uint64_t seed; + uint32_t key_offset; + + /* Internal */ + uint64_t bucket_mask; + uint32_t key_size_shl; + uint32_t data_size_shl; + uint32_t key_stack_tos; + uint32_t bkt_ext_stack_tos; + + /* Grinder */ + struct grinder grinders[RTE_PORT_IN_BURST_SIZE_MAX]; + + /* Tables */ + uint64_t *key_mask; + struct bucket *buckets; + struct bucket *buckets_ext; + uint8_t *key_mem; + uint8_t *data_mem; + uint32_t *key_stack; + uint32_t *bkt_ext_stack; + + /* Table memory */ + uint8_t memory[0] __rte_cache_aligned; +}; + +static int +keycmp(void *a, void *b, void *b_mask, uint32_t n_bytes) +{ + uint64_t *a64 = a, *b64 = b, *b_mask64 = b_mask; + uint32_t i; + + for (i = 0; i < n_bytes / sizeof(uint64_t); i++) + if (a64[i] != (b64[i] & b_mask64[i])) + return 1; + + return 0; +} + +static void +keycpy(void *dst, void *src, void *src_mask, uint32_t n_bytes) +{ + uint64_t *dst64 = dst, *src64 = src, *src_mask64 = src_mask; + uint32_t i; + + for (i = 0; i < n_bytes / sizeof(uint64_t); i++) + dst64[i] = src64[i] & src_mask64[i]; +} + +static int +check_params_create(struct rte_table_hash_params *params) +{ + /* name */ + if (params->name == NULL) { + RTE_LOG(ERR, TABLE, "%s: name invalid value\n", __func__); + return -EINVAL; + } + + /* key_size */ + if ((params->key_size < sizeof(uint64_t)) || + (!rte_is_power_of_2(params->key_size))) { + RTE_LOG(ERR, TABLE, "%s: key_size invalid value\n", __func__); + return -EINVAL; + } + + /* n_keys */ + if (params->n_keys == 0) { + RTE_LOG(ERR, TABLE, "%s: n_keys invalid value\n", __func__); + return -EINVAL; + } + + /* n_buckets */ + if ((params->n_buckets == 0) || + (!rte_is_power_of_2(params->n_buckets))) { + RTE_LOG(ERR, TABLE, "%s: n_buckets invalid value\n", __func__); + return -EINVAL; + } + + /* f_hash */ + if (params->f_hash == NULL) { + RTE_LOG(ERR, TABLE, "%s: f_hash invalid value\n", __func__); + return -EINVAL; + } + + return 0; +} + +static void * +rte_table_hash_ext_create(void *params, int socket_id, uint32_t entry_size) +{ + struct rte_table_hash_params *p = params; + struct rte_table_hash *t; + uint64_t table_meta_sz, key_mask_sz, bucket_sz, bucket_ext_sz, key_sz; + uint64_t key_stack_sz, bkt_ext_stack_sz, data_sz, total_size; + uint64_t key_mask_offset, bucket_offset, bucket_ext_offset, key_offset; + uint64_t key_stack_offset, bkt_ext_stack_offset, data_offset; + uint32_t n_buckets_ext, i; + + /* Check input parameters */ + if ((check_params_create(p) != 0) || + (!rte_is_power_of_2(entry_size)) || + ((sizeof(struct rte_table_hash) % RTE_CACHE_LINE_SIZE) != 0) || + (sizeof(struct bucket) != (RTE_CACHE_LINE_SIZE / 2))) + return NULL; + + /* + * Table dimensioning + * + * Objective: Pick the number of bucket extensions (n_buckets_ext) so that + * it is guaranteed that n_keys keys can be stored in the table at any time. + * + * The worst case scenario takes place when all the n_keys keys fall into + * the same bucket. Actually, due to the KEYS_PER_BUCKET scheme, the worst + * case takes place when (n_keys - KEYS_PER_BUCKET + 1) keys fall into the + * same bucket, while the remaining (KEYS_PER_BUCKET - 1) keys each fall + * into a different bucket. This case defeats the purpose of the hash table. + * It indicates unsuitable f_hash or n_keys to n_buckets ratio. + * + * n_buckets_ext = n_keys / KEYS_PER_BUCKET + KEYS_PER_BUCKET - 1 + */ + n_buckets_ext = p->n_keys / KEYS_PER_BUCKET + KEYS_PER_BUCKET - 1; + + /* Memory allocation */ + table_meta_sz = RTE_CACHE_LINE_ROUNDUP(sizeof(struct rte_table_hash)); + key_mask_sz = RTE_CACHE_LINE_ROUNDUP(p->key_size); + bucket_sz = RTE_CACHE_LINE_ROUNDUP(p->n_buckets * sizeof(struct bucket)); + bucket_ext_sz = + RTE_CACHE_LINE_ROUNDUP(n_buckets_ext * sizeof(struct bucket)); + key_sz = RTE_CACHE_LINE_ROUNDUP(p->n_keys * p->key_size); + key_stack_sz = RTE_CACHE_LINE_ROUNDUP(p->n_keys * sizeof(uint32_t)); + bkt_ext_stack_sz = + RTE_CACHE_LINE_ROUNDUP(n_buckets_ext * sizeof(uint32_t)); + data_sz = RTE_CACHE_LINE_ROUNDUP(p->n_keys * entry_size); + total_size = table_meta_sz + key_mask_sz + bucket_sz + bucket_ext_sz + + key_sz + key_stack_sz + bkt_ext_stack_sz + data_sz; + + if (total_size > SIZE_MAX) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes" + " for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + + t = rte_zmalloc_socket(p->name, + (size_t)total_size, + RTE_CACHE_LINE_SIZE, + socket_id); + if (t == NULL) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes" + " for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + RTE_LOG(INFO, TABLE, "%s (%u-byte key): Hash table %s memory " + "footprint is %" PRIu64 " bytes\n", + __func__, p->key_size, p->name, total_size); + + /* Memory initialization */ + t->key_size = p->key_size; + t->entry_size = entry_size; + t->n_keys = p->n_keys; + t->n_buckets = p->n_buckets; + t->n_buckets_ext = n_buckets_ext; + t->f_hash = p->f_hash; + t->seed = p->seed; + t->key_offset = p->key_offset; + + /* Internal */ + t->bucket_mask = t->n_buckets - 1; + t->key_size_shl = __builtin_ctzl(p->key_size); + t->data_size_shl = __builtin_ctzl(entry_size); + + /* Tables */ + key_mask_offset = 0; + bucket_offset = key_mask_offset + key_mask_sz; + bucket_ext_offset = bucket_offset + bucket_sz; + key_offset = bucket_ext_offset + bucket_ext_sz; + key_stack_offset = key_offset + key_sz; + bkt_ext_stack_offset = key_stack_offset + key_stack_sz; + data_offset = bkt_ext_stack_offset + bkt_ext_stack_sz; + + t->key_mask = (uint64_t *) &t->memory[key_mask_offset]; + t->buckets = (struct bucket *) &t->memory[bucket_offset]; + t->buckets_ext = (struct bucket *) &t->memory[bucket_ext_offset]; + t->key_mem = &t->memory[key_offset]; + t->key_stack = (uint32_t *) &t->memory[key_stack_offset]; + t->bkt_ext_stack = (uint32_t *) &t->memory[bkt_ext_stack_offset]; + t->data_mem = &t->memory[data_offset]; + + /* Key mask */ + if (p->key_mask == NULL) + memset(t->key_mask, 0xFF, p->key_size); + else + memcpy(t->key_mask, p->key_mask, p->key_size); + + /* Key stack */ + for (i = 0; i < t->n_keys; i++) + t->key_stack[i] = t->n_keys - 1 - i; + t->key_stack_tos = t->n_keys; + + /* Bucket ext stack */ + for (i = 0; i < t->n_buckets_ext; i++) + t->bkt_ext_stack[i] = t->n_buckets_ext - 1 - i; + t->bkt_ext_stack_tos = t->n_buckets_ext; + + return t; +} + +static int +rte_table_hash_ext_free(void *table) +{ + struct rte_table_hash *t = table; + + /* Check input parameters */ + if (t == NULL) + return -EINVAL; + + rte_free(t); + return 0; +} + +static int +rte_table_hash_ext_entry_add(void *table, void *key, void *entry, + int *key_found, void **entry_ptr) +{ + struct rte_table_hash *t = table; + struct bucket *bkt0, *bkt, *bkt_prev; + uint64_t sig; + uint32_t bkt_index, i; + + sig = t->f_hash(key, t->key_mask, t->key_size, t->seed); + bkt_index = sig & t->bucket_mask; + bkt0 = &t->buckets[bkt_index]; + sig = (sig >> 16) | 1LLU; + + /* Key is present in the bucket */ + for (bkt = bkt0; bkt != NULL; bkt = BUCKET_NEXT(bkt)) + for (i = 0; i < KEYS_PER_BUCKET; i++) { + uint64_t bkt_sig = (uint64_t) bkt->sig[i]; + uint32_t bkt_key_index = bkt->key_pos[i]; + uint8_t *bkt_key = + &t->key_mem[bkt_key_index << t->key_size_shl]; + + if ((sig == bkt_sig) && (keycmp(bkt_key, key, t->key_mask, + t->key_size) == 0)) { + uint8_t *data = &t->data_mem[bkt_key_index << + t->data_size_shl]; + + memcpy(data, entry, t->entry_size); + *key_found = 1; + *entry_ptr = (void *) data; + return 0; + } + } + + /* Key is not present in the bucket */ + for (bkt_prev = NULL, bkt = bkt0; bkt != NULL; bkt_prev = bkt, + bkt = BUCKET_NEXT(bkt)) + for (i = 0; i < KEYS_PER_BUCKET; i++) { + uint64_t bkt_sig = (uint64_t) bkt->sig[i]; + + if (bkt_sig == 0) { + uint32_t bkt_key_index; + uint8_t *bkt_key, *data; + + /* Allocate new key */ + if (t->key_stack_tos == 0) /* No free keys */ + return -ENOSPC; + + bkt_key_index = t->key_stack[ + --t->key_stack_tos]; + + /* Install new key */ + bkt_key = &t->key_mem[bkt_key_index << + t->key_size_shl]; + data = &t->data_mem[bkt_key_index << + t->data_size_shl]; + + bkt->sig[i] = (uint16_t) sig; + bkt->key_pos[i] = bkt_key_index; + keycpy(bkt_key, key, t->key_mask, t->key_size); + memcpy(data, entry, t->entry_size); + + *key_found = 0; + *entry_ptr = (void *) data; + return 0; + } + } + + /* Bucket full: extend bucket */ + if ((t->bkt_ext_stack_tos > 0) && (t->key_stack_tos > 0)) { + uint32_t bkt_key_index; + uint8_t *bkt_key, *data; + + /* Allocate new bucket ext */ + bkt_index = t->bkt_ext_stack[--t->bkt_ext_stack_tos]; + bkt = &t->buckets_ext[bkt_index]; + + /* Chain the new bucket ext */ + BUCKET_NEXT_SET(bkt_prev, bkt); + BUCKET_NEXT_SET_NULL(bkt); + + /* Allocate new key */ + bkt_key_index = t->key_stack[--t->key_stack_tos]; + bkt_key = &t->key_mem[bkt_key_index << t->key_size_shl]; + + data = &t->data_mem[bkt_key_index << t->data_size_shl]; + + /* Install new key into bucket */ + bkt->sig[0] = (uint16_t) sig; + bkt->key_pos[0] = bkt_key_index; + keycpy(bkt_key, key, t->key_mask, t->key_size); + memcpy(data, entry, t->entry_size); + + *key_found = 0; + *entry_ptr = (void *) data; + return 0; + } + + return -ENOSPC; +} + +static int +rte_table_hash_ext_entry_delete(void *table, void *key, int *key_found, +void *entry) +{ + struct rte_table_hash *t = table; + struct bucket *bkt0, *bkt, *bkt_prev; + uint64_t sig; + uint32_t bkt_index, i; + + sig = t->f_hash(key, t->key_mask, t->key_size, t->seed); + bkt_index = sig & t->bucket_mask; + bkt0 = &t->buckets[bkt_index]; + sig = (sig >> 16) | 1LLU; + + /* Key is present in the bucket */ + for (bkt_prev = NULL, bkt = bkt0; bkt != NULL; bkt_prev = bkt, + bkt = BUCKET_NEXT(bkt)) + for (i = 0; i < KEYS_PER_BUCKET; i++) { + uint64_t bkt_sig = (uint64_t) bkt->sig[i]; + uint32_t bkt_key_index = bkt->key_pos[i]; + uint8_t *bkt_key = &t->key_mem[bkt_key_index << + t->key_size_shl]; + + if ((sig == bkt_sig) && (keycmp(bkt_key, key, t->key_mask, + t->key_size) == 0)) { + uint8_t *data = &t->data_mem[bkt_key_index << + t->data_size_shl]; + + /* Uninstall key from bucket */ + bkt->sig[i] = 0; + *key_found = 1; + if (entry) + memcpy(entry, data, t->entry_size); + + /* Free key */ + t->key_stack[t->key_stack_tos++] = + bkt_key_index; + + /*Check if bucket is unused */ + if ((bkt_prev != NULL) && + (bkt->sig[0] == 0) && (bkt->sig[1] == 0) && + (bkt->sig[2] == 0) && (bkt->sig[3] == 0)) { + /* Unchain bucket */ + BUCKET_NEXT_COPY(bkt_prev, bkt); + + /* Clear bucket */ + memset(bkt, 0, sizeof(struct bucket)); + + /* Free bucket back to buckets ext */ + bkt_index = bkt - t->buckets_ext; + t->bkt_ext_stack[t->bkt_ext_stack_tos++] + = bkt_index; + } + + return 0; + } + } + + /* Key is not present in the bucket */ + *key_found = 0; + return 0; +} + +static int rte_table_hash_ext_lookup_unoptimized( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *t = (struct rte_table_hash *) table; + uint64_t pkts_mask_out = 0; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + + for ( ; pkts_mask; ) { + struct bucket *bkt0, *bkt; + struct rte_mbuf *pkt; + uint8_t *key; + uint64_t pkt_mask, sig; + uint32_t pkt_index, bkt_index, i; + + pkt_index = __builtin_ctzll(pkts_mask); + pkt_mask = 1LLU << pkt_index; + pkts_mask &= ~pkt_mask; + + pkt = pkts[pkt_index]; + key = RTE_MBUF_METADATA_UINT8_PTR(pkt, t->key_offset); + sig = (uint64_t) t->f_hash(key, t->key_mask, t->key_size, t->seed); + + bkt_index = sig & t->bucket_mask; + bkt0 = &t->buckets[bkt_index]; + sig = (sig >> 16) | 1LLU; + + /* Key is present in the bucket */ + for (bkt = bkt0; bkt != NULL; bkt = BUCKET_NEXT(bkt)) + for (i = 0; i < KEYS_PER_BUCKET; i++) { + uint64_t bkt_sig = (uint64_t) bkt->sig[i]; + uint32_t bkt_key_index = bkt->key_pos[i]; + uint8_t *bkt_key = &t->key_mem[bkt_key_index << + t->key_size_shl]; + + if ((sig == bkt_sig) && (keycmp(bkt_key, key, + t->key_mask, t->key_size) == 0)) { + uint8_t *data = &t->data_mem[ + bkt_key_index << t->data_size_shl]; + + pkts_mask_out |= pkt_mask; + entries[pkt_index] = (void *) data; + break; + } + } + } + + *lookup_hit_mask = pkts_mask_out; + return 0; +} + +/*** + * + * mask = match bitmask + * match = at least one match + * match_many = more than one match + * match_pos = position of first match + * + *---------------------------------------- + * mask match match_many match_pos + *---------------------------------------- + * 0000 0 0 00 + * 0001 1 0 00 + * 0010 1 0 01 + * 0011 1 1 00 + *---------------------------------------- + * 0100 1 0 10 + * 0101 1 1 00 + * 0110 1 1 01 + * 0111 1 1 00 + *---------------------------------------- + * 1000 1 0 11 + * 1001 1 1 00 + * 1010 1 1 01 + * 1011 1 1 00 + *---------------------------------------- + * 1100 1 1 10 + * 1101 1 1 00 + * 1110 1 1 01 + * 1111 1 1 00 + *---------------------------------------- + * + * match = 1111_1111_1111_1110 + * match_many = 1111_1110_1110_1000 + * match_pos = 0001_0010_0001_0011__0001_0010_0001_0000 + * + * match = 0xFFFELLU + * match_many = 0xFEE8LLU + * match_pos = 0x12131210LLU + * + ***/ + +#define LUT_MATCH 0xFFFELLU +#define LUT_MATCH_MANY 0xFEE8LLU +#define LUT_MATCH_POS 0x12131210LLU + +#define lookup_cmp_sig(mbuf_sig, bucket, match, match_many, match_pos) \ +{ \ + uint64_t bucket_sig[4], mask[4], mask_all; \ + \ + bucket_sig[0] = bucket->sig[0]; \ + bucket_sig[1] = bucket->sig[1]; \ + bucket_sig[2] = bucket->sig[2]; \ + bucket_sig[3] = bucket->sig[3]; \ + \ + bucket_sig[0] ^= mbuf_sig; \ + bucket_sig[1] ^= mbuf_sig; \ + bucket_sig[2] ^= mbuf_sig; \ + bucket_sig[3] ^= mbuf_sig; \ + \ + mask[0] = 0; \ + mask[1] = 0; \ + mask[2] = 0; \ + mask[3] = 0; \ + \ + if (bucket_sig[0] == 0) \ + mask[0] = 1; \ + if (bucket_sig[1] == 0) \ + mask[1] = 2; \ + if (bucket_sig[2] == 0) \ + mask[2] = 4; \ + if (bucket_sig[3] == 0) \ + mask[3] = 8; \ + \ + mask_all = (mask[0] | mask[1]) | (mask[2] | mask[3]); \ + \ + match = (LUT_MATCH >> mask_all) & 1; \ + match_many = (LUT_MATCH_MANY >> mask_all) & 1; \ + match_pos = (LUT_MATCH_POS >> (mask_all << 1)) & 3; \ +} + +#define lookup_cmp_key(mbuf, key, match_key, f) \ +{ \ + uint64_t *pkt_key = RTE_MBUF_METADATA_UINT64_PTR(mbuf, f->key_offset);\ + uint64_t *bkt_key = (uint64_t *) key; \ + uint64_t *key_mask = f->key_mask; \ + \ + switch (f->key_size) { \ + case 8: \ + { \ + uint64_t xor = (pkt_key[0] & key_mask[0]) ^ bkt_key[0]; \ + match_key = 0; \ + if (xor == 0) \ + match_key = 1; \ + } \ + break; \ + \ + case 16: \ + { \ + uint64_t xor[2], or; \ + \ + xor[0] = (pkt_key[0] & key_mask[0]) ^ bkt_key[0]; \ + xor[1] = (pkt_key[1] & key_mask[1]) ^ bkt_key[1]; \ + or = xor[0] | xor[1]; \ + match_key = 0; \ + if (or == 0) \ + match_key = 1; \ + } \ + break; \ + \ + case 32: \ + { \ + uint64_t xor[4], or; \ + \ + xor[0] = (pkt_key[0] & key_mask[0]) ^ bkt_key[0]; \ + xor[1] = (pkt_key[1] & key_mask[1]) ^ bkt_key[1]; \ + xor[2] = (pkt_key[2] & key_mask[2]) ^ bkt_key[2]; \ + xor[3] = (pkt_key[3] & key_mask[3]) ^ bkt_key[3]; \ + or = xor[0] | xor[1] | xor[2] | xor[3]; \ + match_key = 0; \ + if (or == 0) \ + match_key = 1; \ + } \ + break; \ + \ + case 64: \ + { \ + uint64_t xor[8], or; \ + \ + xor[0] = (pkt_key[0] & key_mask[0]) ^ bkt_key[0]; \ + xor[1] = (pkt_key[1] & key_mask[1]) ^ bkt_key[1]; \ + xor[2] = (pkt_key[2] & key_mask[2]) ^ bkt_key[2]; \ + xor[3] = (pkt_key[3] & key_mask[3]) ^ bkt_key[3]; \ + xor[4] = (pkt_key[4] & key_mask[4]) ^ bkt_key[4]; \ + xor[5] = (pkt_key[5] & key_mask[5]) ^ bkt_key[5]; \ + xor[6] = (pkt_key[6] & key_mask[6]) ^ bkt_key[6]; \ + xor[7] = (pkt_key[7] & key_mask[7]) ^ bkt_key[7]; \ + or = xor[0] | xor[1] | xor[2] | xor[3] | \ + xor[4] | xor[5] | xor[6] | xor[7]; \ + match_key = 0; \ + if (or == 0) \ + match_key = 1; \ + } \ + break; \ + \ + default: \ + match_key = 0; \ + if (keycmp(bkt_key, pkt_key, key_mask, f->key_size) == 0) \ + match_key = 1; \ + } \ +} + +#define lookup2_stage0(t, g, pkts, pkts_mask, pkt00_index, pkt01_index) \ +{ \ + uint64_t pkt00_mask, pkt01_mask; \ + struct rte_mbuf *mbuf00, *mbuf01; \ + uint32_t key_offset = t->key_offset; \ + \ + pkt00_index = __builtin_ctzll(pkts_mask); \ + pkt00_mask = 1LLU << pkt00_index; \ + pkts_mask &= ~pkt00_mask; \ + mbuf00 = pkts[pkt00_index]; \ + \ + pkt01_index = __builtin_ctzll(pkts_mask); \ + pkt01_mask = 1LLU << pkt01_index; \ + pkts_mask &= ~pkt01_mask; \ + mbuf01 = pkts[pkt01_index]; \ + \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf00, key_offset));\ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf01, key_offset));\ +} + +#define lookup2_stage0_with_odd_support(t, g, pkts, pkts_mask, pkt00_index, \ + pkt01_index) \ +{ \ + uint64_t pkt00_mask, pkt01_mask; \ + struct rte_mbuf *mbuf00, *mbuf01; \ + uint32_t key_offset = t->key_offset; \ + \ + pkt00_index = __builtin_ctzll(pkts_mask); \ + pkt00_mask = 1LLU << pkt00_index; \ + pkts_mask &= ~pkt00_mask; \ + mbuf00 = pkts[pkt00_index]; \ + \ + pkt01_index = __builtin_ctzll(pkts_mask); \ + if (pkts_mask == 0) \ + pkt01_index = pkt00_index; \ + pkt01_mask = 1LLU << pkt01_index; \ + pkts_mask &= ~pkt01_mask; \ + mbuf01 = pkts[pkt01_index]; \ + \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf00, key_offset));\ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf01, key_offset));\ +} + +#define lookup2_stage1(t, g, pkts, pkt10_index, pkt11_index) \ +{ \ + struct grinder *g10, *g11; \ + uint64_t sig10, sig11, bkt10_index, bkt11_index; \ + struct rte_mbuf *mbuf10, *mbuf11; \ + struct bucket *bkt10, *bkt11, *buckets = t->buckets; \ + uint8_t *key10, *key11; \ + uint64_t bucket_mask = t->bucket_mask; \ + rte_table_hash_op_hash f_hash = t->f_hash; \ + uint64_t seed = t->seed; \ + uint32_t key_size = t->key_size; \ + uint32_t key_offset = t->key_offset; \ + \ + mbuf10 = pkts[pkt10_index]; \ + key10 = RTE_MBUF_METADATA_UINT8_PTR(mbuf10, key_offset); \ + sig10 = (uint64_t) f_hash(key10, t->key_mask, key_size, seed); \ + bkt10_index = sig10 & bucket_mask; \ + bkt10 = &buckets[bkt10_index]; \ + \ + mbuf11 = pkts[pkt11_index]; \ + key11 = RTE_MBUF_METADATA_UINT8_PTR(mbuf11, key_offset); \ + sig11 = (uint64_t) f_hash(key11, t->key_mask, key_size, seed); \ + bkt11_index = sig11 & bucket_mask; \ + bkt11 = &buckets[bkt11_index]; \ + \ + rte_prefetch0(bkt10); \ + rte_prefetch0(bkt11); \ + \ + g10 = &g[pkt10_index]; \ + g10->sig = sig10; \ + g10->bkt = bkt10; \ + \ + g11 = &g[pkt11_index]; \ + g11->sig = sig11; \ + g11->bkt = bkt11; \ +} + +#define lookup2_stage2(t, g, pkt20_index, pkt21_index, pkts_mask_match_many)\ +{ \ + struct grinder *g20, *g21; \ + uint64_t sig20, sig21; \ + struct bucket *bkt20, *bkt21; \ + uint8_t *key20, *key21, *key_mem = t->key_mem; \ + uint64_t match20, match21, match_many20, match_many21; \ + uint64_t match_pos20, match_pos21; \ + uint32_t key20_index, key21_index, key_size_shl = t->key_size_shl;\ + \ + g20 = &g[pkt20_index]; \ + sig20 = g20->sig; \ + bkt20 = g20->bkt; \ + sig20 = (sig20 >> 16) | 1LLU; \ + lookup_cmp_sig(sig20, bkt20, match20, match_many20, match_pos20);\ + match20 <<= pkt20_index; \ + match_many20 |= BUCKET_NEXT_VALID(bkt20); \ + match_many20 <<= pkt20_index; \ + key20_index = bkt20->key_pos[match_pos20]; \ + key20 = &key_mem[key20_index << key_size_shl]; \ + \ + g21 = &g[pkt21_index]; \ + sig21 = g21->sig; \ + bkt21 = g21->bkt; \ + sig21 = (sig21 >> 16) | 1LLU; \ + lookup_cmp_sig(sig21, bkt21, match21, match_many21, match_pos21);\ + match21 <<= pkt21_index; \ + match_many21 |= BUCKET_NEXT_VALID(bkt21); \ + match_many21 <<= pkt21_index; \ + key21_index = bkt21->key_pos[match_pos21]; \ + key21 = &key_mem[key21_index << key_size_shl]; \ + \ + rte_prefetch0(key20); \ + rte_prefetch0(key21); \ + \ + pkts_mask_match_many |= match_many20 | match_many21; \ + \ + g20->match = match20; \ + g20->key_index = key20_index; \ + \ + g21->match = match21; \ + g21->key_index = key21_index; \ +} + +#define lookup2_stage3(t, g, pkts, pkt30_index, pkt31_index, pkts_mask_out, \ + entries) \ +{ \ + struct grinder *g30, *g31; \ + struct rte_mbuf *mbuf30, *mbuf31; \ + uint8_t *key30, *key31, *key_mem = t->key_mem; \ + uint8_t *data30, *data31, *data_mem = t->data_mem; \ + uint64_t match30, match31, match_key30, match_key31, match_keys;\ + uint32_t key30_index, key31_index; \ + uint32_t key_size_shl = t->key_size_shl; \ + uint32_t data_size_shl = t->data_size_shl; \ + \ + mbuf30 = pkts[pkt30_index]; \ + g30 = &g[pkt30_index]; \ + match30 = g30->match; \ + key30_index = g30->key_index; \ + key30 = &key_mem[key30_index << key_size_shl]; \ + lookup_cmp_key(mbuf30, key30, match_key30, t); \ + match_key30 <<= pkt30_index; \ + match_key30 &= match30; \ + data30 = &data_mem[key30_index << data_size_shl]; \ + entries[pkt30_index] = data30; \ + \ + mbuf31 = pkts[pkt31_index]; \ + g31 = &g[pkt31_index]; \ + match31 = g31->match; \ + key31_index = g31->key_index; \ + key31 = &key_mem[key31_index << key_size_shl]; \ + lookup_cmp_key(mbuf31, key31, match_key31, t); \ + match_key31 <<= pkt31_index; \ + match_key31 &= match31; \ + data31 = &data_mem[key31_index << data_size_shl]; \ + entries[pkt31_index] = data31; \ + \ + rte_prefetch0(data30); \ + rte_prefetch0(data31); \ + \ + match_keys = match_key30 | match_key31; \ + pkts_mask_out |= match_keys; \ +} + +/*** +* The lookup function implements a 4-stage pipeline, with each stage processing +* two different packets. The purpose of pipelined implementation is to hide the +* latency of prefetching the data structures and loosen the data dependency +* between instructions. +* +* p00 _______ p10 _______ p20 _______ p30 _______ +*----->| |----->| |----->| |----->| |-----> +* | 0 | | 1 | | 2 | | 3 | +*----->|_______|----->|_______|----->|_______|----->|_______|-----> +* p01 p11 p21 p31 +* +* The naming convention is: +* pXY = packet Y of stage X, X = 0 .. 3, Y = 0 .. 1 +* +***/ +static int rte_table_hash_ext_lookup( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *t = (struct rte_table_hash *) table; + struct grinder *g = t->grinders; + uint64_t pkt00_index, pkt01_index, pkt10_index, pkt11_index; + uint64_t pkt20_index, pkt21_index, pkt30_index, pkt31_index; + uint64_t pkts_mask_out = 0, pkts_mask_match_many = 0; + int status = 0; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_HASH_EXT_STATS_PKTS_IN_ADD(t, n_pkts_in); + + /* Cannot run the pipeline with less than 7 packets */ + if (__builtin_popcountll(pkts_mask) < 7) { + status = rte_table_hash_ext_lookup_unoptimized(table, pkts, + pkts_mask, lookup_hit_mask, entries); + RTE_TABLE_HASH_EXT_STATS_PKTS_LOOKUP_MISS(t, n_pkts_in - + __builtin_popcountll(*lookup_hit_mask)); + return status; + } + + /* Pipeline stage 0 */ + lookup2_stage0(t, g, pkts, pkts_mask, pkt00_index, pkt01_index); + + /* Pipeline feed */ + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0(t, g, pkts, pkts_mask, pkt00_index, pkt01_index); + + /* Pipeline stage 1 */ + lookup2_stage1(t, g, pkts, pkt10_index, pkt11_index); + + /* Pipeline feed */ + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0(t, g, pkts, pkts_mask, pkt00_index, pkt01_index); + + /* Pipeline stage 1 */ + lookup2_stage1(t, g, pkts, pkt10_index, pkt11_index); + + /* Pipeline stage 2 */ + lookup2_stage2(t, g, pkt20_index, pkt21_index, pkts_mask_match_many); + + /* + * Pipeline run + * + */ + for ( ; pkts_mask; ) { + /* Pipeline feed */ + pkt30_index = pkt20_index; + pkt31_index = pkt21_index; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0_with_odd_support(t, g, pkts, pkts_mask, + pkt00_index, pkt01_index); + + /* Pipeline stage 1 */ + lookup2_stage1(t, g, pkts, pkt10_index, pkt11_index); + + /* Pipeline stage 2 */ + lookup2_stage2(t, g, pkt20_index, pkt21_index, + pkts_mask_match_many); + + /* Pipeline stage 3 */ + lookup2_stage3(t, g, pkts, pkt30_index, pkt31_index, + pkts_mask_out, entries); + } + + /* Pipeline feed */ + pkt30_index = pkt20_index; + pkt31_index = pkt21_index; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 1 */ + lookup2_stage1(t, g, pkts, pkt10_index, pkt11_index); + + /* Pipeline stage 2 */ + lookup2_stage2(t, g, pkt20_index, pkt21_index, pkts_mask_match_many); + + /* Pipeline stage 3 */ + lookup2_stage3(t, g, pkts, pkt30_index, pkt31_index, pkts_mask_out, + entries); + + /* Pipeline feed */ + pkt30_index = pkt20_index; + pkt31_index = pkt21_index; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + + /* Pipeline stage 2 */ + lookup2_stage2(t, g, pkt20_index, pkt21_index, pkts_mask_match_many); + + /* Pipeline stage 3 */ + lookup2_stage3(t, g, pkts, pkt30_index, pkt31_index, pkts_mask_out, + entries); + + /* Pipeline feed */ + pkt30_index = pkt20_index; + pkt31_index = pkt21_index; + + /* Pipeline stage 3 */ + lookup2_stage3(t, g, pkts, pkt30_index, pkt31_index, pkts_mask_out, + entries); + + /* Slow path */ + pkts_mask_match_many &= ~pkts_mask_out; + if (pkts_mask_match_many) { + uint64_t pkts_mask_out_slow = 0; + + status = rte_table_hash_ext_lookup_unoptimized(table, pkts, + pkts_mask_match_many, &pkts_mask_out_slow, entries); + pkts_mask_out |= pkts_mask_out_slow; + } + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_EXT_STATS_PKTS_LOOKUP_MISS(t, n_pkts_in - __builtin_popcountll(pkts_mask_out)); + return status; +} + +static int +rte_table_hash_ext_stats_read(void *table, struct rte_table_stats *stats, int clear) +{ + struct rte_table_hash *t = table; + + if (stats != NULL) + memcpy(stats, &t->stats, sizeof(t->stats)); + + if (clear) + memset(&t->stats, 0, sizeof(t->stats)); + + return 0; +} + +struct rte_table_ops rte_table_hash_ext_ops = { + .f_create = rte_table_hash_ext_create, + .f_free = rte_table_hash_ext_free, + .f_add = rte_table_hash_ext_entry_add, + .f_delete = rte_table_hash_ext_entry_delete, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_hash_ext_lookup, + .f_stats = rte_table_hash_ext_stats_read, +}; diff --git a/lib/table/rte_table_hash_func.h b/lib/table/rte_table_hash_func.h new file mode 100644 index 0000000000..c4c35cc06a --- /dev/null +++ b/lib/table/rte_table_hash_func.h @@ -0,0 +1,254 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2018 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_TABLE_HASH_FUNC_H__ +#define __INCLUDE_RTE_TABLE_HASH_FUNC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +#include <rte_compat.h> +#include <rte_common.h> + +__rte_experimental +static inline uint64_t +rte_crc32_u64_generic(uint64_t crc, uint64_t value) +{ + int i; + + crc = (crc & 0xFFFFFFFFLLU) ^ value; + for (i = 63; i >= 0; i--) { + uint64_t mask; + + mask = -(crc & 1LLU); + crc = (crc >> 1LLU) ^ (0x82F63B78LLU & mask); + } + + return crc; +} + +#if defined(RTE_ARCH_X86_64) + +#include <x86intrin.h> + +static inline uint64_t +rte_crc32_u64(uint64_t crc, uint64_t v) +{ + return _mm_crc32_u64(crc, v); +} + +#elif defined(RTE_ARCH_ARM64) && defined(__ARM_FEATURE_CRC32) +#include "rte_table_hash_func_arm64.h" +#else + +static inline uint64_t +rte_crc32_u64(uint64_t crc, uint64_t v) +{ + return rte_crc32_u64_generic(crc, v); +} + +#endif + +__rte_experimental +static inline uint64_t +rte_table_hash_crc_key8(void *key, void *mask, __rte_unused uint32_t key_size, + uint64_t seed) +{ + uint64_t *k = key; + uint64_t *m = mask; + uint64_t crc0; + + crc0 = rte_crc32_u64(seed, k[0] & m[0]); + + return crc0; +} + +__rte_experimental +static inline uint64_t +rte_table_hash_crc_key16(void *key, void *mask, __rte_unused uint32_t key_size, + uint64_t seed) +{ + uint64_t *k = key; + uint64_t *m = mask; + uint64_t k0, crc0, crc1; + + k0 = k[0] & m[0]; + + crc0 = rte_crc32_u64(k0, seed); + crc1 = rte_crc32_u64(k0 >> 32, k[1] & m[1]); + + crc0 ^= crc1; + + return crc0; +} + +__rte_experimental +static inline uint64_t +rte_table_hash_crc_key24(void *key, void *mask, __rte_unused uint32_t key_size, + uint64_t seed) +{ + uint64_t *k = key; + uint64_t *m = mask; + uint64_t k0, k2, crc0, crc1; + + k0 = k[0] & m[0]; + k2 = k[2] & m[2]; + + crc0 = rte_crc32_u64(k0, seed); + crc1 = rte_crc32_u64(k0 >> 32, k[1] & m[1]); + + crc0 = rte_crc32_u64(crc0, k2); + + crc0 ^= crc1; + + return crc0; +} + +__rte_experimental +static inline uint64_t +rte_table_hash_crc_key32(void *key, void *mask, __rte_unused uint32_t key_size, + uint64_t seed) +{ + uint64_t *k = key; + uint64_t *m = mask; + uint64_t k0, k2, crc0, crc1, crc2, crc3; + + k0 = k[0] & m[0]; + k2 = k[2] & m[2]; + + crc0 = rte_crc32_u64(k0, seed); + crc1 = rte_crc32_u64(k0 >> 32, k[1] & m[1]); + + crc2 = rte_crc32_u64(k2, k[3] & m[3]); + crc3 = k2 >> 32; + + crc0 = rte_crc32_u64(crc0, crc1); + crc1 = rte_crc32_u64(crc2, crc3); + + crc0 ^= crc1; + + return crc0; +} + +__rte_experimental +static inline uint64_t +rte_table_hash_crc_key40(void *key, void *mask, __rte_unused uint32_t key_size, + uint64_t seed) +{ + uint64_t *k = key; + uint64_t *m = mask; + uint64_t k0, k2, crc0, crc1, crc2, crc3; + + k0 = k[0] & m[0]; + k2 = k[2] & m[2]; + + crc0 = rte_crc32_u64(k0, seed); + crc1 = rte_crc32_u64(k0 >> 32, k[1] & m[1]); + + crc2 = rte_crc32_u64(k2, k[3] & m[3]); + crc3 = rte_crc32_u64(k2 >> 32, k[4] & m[4]); + + crc0 = rte_crc32_u64(crc0, crc1); + crc1 = rte_crc32_u64(crc2, crc3); + + crc0 ^= crc1; + + return crc0; +} + +__rte_experimental +static inline uint64_t +rte_table_hash_crc_key48(void *key, void *mask, __rte_unused uint32_t key_size, + uint64_t seed) +{ + uint64_t *k = key; + uint64_t *m = mask; + uint64_t k0, k2, k5, crc0, crc1, crc2, crc3; + + k0 = k[0] & m[0]; + k2 = k[2] & m[2]; + k5 = k[5] & m[5]; + + crc0 = rte_crc32_u64(k0, seed); + crc1 = rte_crc32_u64(k0 >> 32, k[1] & m[1]); + + crc2 = rte_crc32_u64(k2, k[3] & m[3]); + crc3 = rte_crc32_u64(k2 >> 32, k[4] & m[4]); + + crc0 = rte_crc32_u64(crc0, (crc1 << 32) ^ crc2); + crc1 = rte_crc32_u64(crc3, k5); + + crc0 ^= crc1; + + return crc0; +} + +__rte_experimental +static inline uint64_t +rte_table_hash_crc_key56(void *key, void *mask, __rte_unused uint32_t key_size, + uint64_t seed) +{ + uint64_t *k = key; + uint64_t *m = mask; + uint64_t k0, k2, k5, crc0, crc1, crc2, crc3, crc4, crc5; + + k0 = k[0] & m[0]; + k2 = k[2] & m[2]; + k5 = k[5] & m[5]; + + crc0 = rte_crc32_u64(k0, seed); + crc1 = rte_crc32_u64(k0 >> 32, k[1] & m[1]); + + crc2 = rte_crc32_u64(k2, k[3] & m[3]); + crc3 = rte_crc32_u64(k2 >> 32, k[4] & m[4]); + + crc4 = rte_crc32_u64(k5, k[6] & m[6]); + crc5 = k5 >> 32; + + crc0 = rte_crc32_u64(crc0, (crc1 << 32) ^ crc2); + crc1 = rte_crc32_u64(crc3, (crc4 << 32) ^ crc5); + + crc0 ^= crc1; + + return crc0; +} + +__rte_experimental +static inline uint64_t +rte_table_hash_crc_key64(void *key, void *mask, __rte_unused uint32_t key_size, + uint64_t seed) +{ + uint64_t *k = key; + uint64_t *m = mask; + uint64_t k0, k2, k5, crc0, crc1, crc2, crc3, crc4, crc5; + + k0 = k[0] & m[0]; + k2 = k[2] & m[2]; + k5 = k[5] & m[5]; + + crc0 = rte_crc32_u64(k0, seed); + crc1 = rte_crc32_u64(k0 >> 32, k[1] & m[1]); + + crc2 = rte_crc32_u64(k2, k[3] & m[3]); + crc3 = rte_crc32_u64(k2 >> 32, k[4] & m[4]); + + crc4 = rte_crc32_u64(k5, k[6] & m[6]); + crc5 = rte_crc32_u64(k5 >> 32, k[7] & m[7]); + + crc0 = rte_crc32_u64(crc0, (crc1 << 32) ^ crc2); + crc1 = rte_crc32_u64(crc3, (crc4 << 32) ^ crc5); + + crc0 ^= crc1; + + return crc0; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_table_hash_func_arm64.h b/lib/table/rte_table_hash_func_arm64.h new file mode 100644 index 0000000000..eb04c1ff54 --- /dev/null +++ b/lib/table/rte_table_hash_func_arm64.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017-2018 Linaro Limited + */ + +#ifndef __INCLUDE_RTE_TABLE_HASH_FUNC_ARM64_H__ +#define __INCLUDE_RTE_TABLE_HASH_FUNC_ARM64_H__ + +#define _CRC32CX(crc, val) \ + __asm__("crc32cx %w[c], %w[c], %x[v]":[c] "+r" (crc):[v] "r" (val)) + +static inline uint64_t +rte_crc32_u64(uint64_t crc, uint64_t v) +{ + uint32_t crc32 = crc; + + _CRC32CX(crc32, v); + + return crc32; +} + +#endif diff --git a/lib/table/rte_table_hash_key16.c b/lib/table/rte_table_hash_key16.c new file mode 100644 index 0000000000..c4384b114d --- /dev/null +++ b/lib/table/rte_table_hash_key16.c @@ -0,0 +1,1187 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2017 Intel Corporation + */ +#include <string.h> +#include <stdio.h> + +#include <rte_common.h> +#include <rte_mbuf.h> +#include <rte_memory.h> +#include <rte_malloc.h> +#include <rte_log.h> + +#include "rte_table_hash.h" +#include "rte_lru.h" + +#define KEY_SIZE 16 + +#define KEYS_PER_BUCKET 4 + +#define RTE_BUCKET_ENTRY_VALID 0x1LLU + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_HASH_KEY16_STATS_PKTS_IN_ADD(table, val) \ + table->stats.n_pkts_in += val +#define RTE_TABLE_HASH_KEY16_STATS_PKTS_LOOKUP_MISS(table, val) \ + table->stats.n_pkts_lookup_miss += val + +#else + +#define RTE_TABLE_HASH_KEY16_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_HASH_KEY16_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + +#ifdef RTE_ARCH_64 +struct rte_bucket_4_16 { + /* Cache line 0 */ + uint64_t signature[4 + 1]; + uint64_t lru_list; + struct rte_bucket_4_16 *next; + uint64_t next_valid; + + /* Cache line 1 */ + uint64_t key[4][2]; + + /* Cache line 2 */ + uint8_t data[0]; +}; +#else +struct rte_bucket_4_16 { + /* Cache line 0 */ + uint64_t signature[4 + 1]; + uint64_t lru_list; + struct rte_bucket_4_16 *next; + uint32_t pad; + uint64_t next_valid; + + /* Cache line 1 */ + uint64_t key[4][2]; + + /* Cache line 2 */ + uint8_t data[0]; +}; +#endif + +struct rte_table_hash { + struct rte_table_stats stats; + + /* Input parameters */ + uint32_t n_buckets; + uint32_t key_size; + uint32_t entry_size; + uint32_t bucket_size; + uint32_t key_offset; + uint64_t key_mask[2]; + rte_table_hash_op_hash f_hash; + uint64_t seed; + + /* Extendible buckets */ + uint32_t n_buckets_ext; + uint32_t stack_pos; + uint32_t *stack; + + /* Lookup table */ + uint8_t memory[0] __rte_cache_aligned; +}; + +static int +keycmp(void *a, void *b, void *b_mask) +{ + uint64_t *a64 = a, *b64 = b, *b_mask64 = b_mask; + + return (a64[0] != (b64[0] & b_mask64[0])) || + (a64[1] != (b64[1] & b_mask64[1])); +} + +static void +keycpy(void *dst, void *src, void *src_mask) +{ + uint64_t *dst64 = dst, *src64 = src, *src_mask64 = src_mask; + + dst64[0] = src64[0] & src_mask64[0]; + dst64[1] = src64[1] & src_mask64[1]; +} + +static int +check_params_create(struct rte_table_hash_params *params) +{ + /* name */ + if (params->name == NULL) { + RTE_LOG(ERR, TABLE, "%s: name invalid value\n", __func__); + return -EINVAL; + } + + /* key_size */ + if (params->key_size != KEY_SIZE) { + RTE_LOG(ERR, TABLE, "%s: key_size invalid value\n", __func__); + return -EINVAL; + } + + /* n_keys */ + if (params->n_keys == 0) { + RTE_LOG(ERR, TABLE, "%s: n_keys is zero\n", __func__); + return -EINVAL; + } + + /* n_buckets */ + if ((params->n_buckets == 0) || + (!rte_is_power_of_2(params->n_buckets))) { + RTE_LOG(ERR, TABLE, "%s: n_buckets invalid value\n", __func__); + return -EINVAL; + } + + /* f_hash */ + if (params->f_hash == NULL) { + RTE_LOG(ERR, TABLE, "%s: f_hash function pointer is NULL\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static void * +rte_table_hash_create_key16_lru(void *params, + int socket_id, + uint32_t entry_size) +{ + struct rte_table_hash_params *p = params; + struct rte_table_hash *f; + uint64_t bucket_size, total_size; + uint32_t n_buckets, i; + + /* Check input parameters */ + if ((check_params_create(p) != 0) || + ((sizeof(struct rte_table_hash) % RTE_CACHE_LINE_SIZE) != 0) || + ((sizeof(struct rte_bucket_4_16) % 64) != 0)) + return NULL; + + /* + * Table dimensioning + * + * Objective: Pick the number of buckets (n_buckets) so that there a chance + * to store n_keys keys in the table. + * + * Note: Since the buckets do not get extended, it is not possible to + * guarantee that n_keys keys can be stored in the table at any time. In the + * worst case scenario when all the n_keys fall into the same bucket, only + * a maximum of KEYS_PER_BUCKET keys will be stored in the table. This case + * defeats the purpose of the hash table. It indicates unsuitable f_hash or + * n_keys to n_buckets ratio. + * + * MIN(n_buckets) = (n_keys + KEYS_PER_BUCKET - 1) / KEYS_PER_BUCKET + */ + n_buckets = rte_align32pow2( + (p->n_keys + KEYS_PER_BUCKET - 1) / KEYS_PER_BUCKET); + n_buckets = RTE_MAX(n_buckets, p->n_buckets); + + /* Memory allocation */ + bucket_size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct rte_bucket_4_16) + + KEYS_PER_BUCKET * entry_size); + total_size = sizeof(struct rte_table_hash) + n_buckets * bucket_size; + + if (total_size > SIZE_MAX) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes " + "for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + + f = rte_zmalloc_socket(p->name, + (size_t)total_size, + RTE_CACHE_LINE_SIZE, + socket_id); + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes " + "for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + RTE_LOG(INFO, TABLE, "%s: Hash table %s memory footprint " + "is %" PRIu64 " bytes\n", + __func__, p->name, total_size); + + /* Memory initialization */ + f->n_buckets = n_buckets; + f->key_size = KEY_SIZE; + f->entry_size = entry_size; + f->bucket_size = bucket_size; + f->key_offset = p->key_offset; + f->f_hash = p->f_hash; + f->seed = p->seed; + + if (p->key_mask != NULL) { + f->key_mask[0] = ((uint64_t *)p->key_mask)[0]; + f->key_mask[1] = ((uint64_t *)p->key_mask)[1]; + } else { + f->key_mask[0] = 0xFFFFFFFFFFFFFFFFLLU; + f->key_mask[1] = 0xFFFFFFFFFFFFFFFFLLU; + } + + for (i = 0; i < n_buckets; i++) { + struct rte_bucket_4_16 *bucket; + + bucket = (struct rte_bucket_4_16 *) &f->memory[i * + f->bucket_size]; + lru_init(bucket); + } + + return f; +} + +static int +rte_table_hash_free_key16_lru(void *table) +{ + struct rte_table_hash *f = table; + + /* Check input parameters */ + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + + rte_free(f); + return 0; +} + +static int +rte_table_hash_entry_add_key16_lru( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_16 *bucket; + uint64_t signature, pos; + uint32_t bucket_index, i; + + signature = f->f_hash(key, f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket = (struct rte_bucket_4_16 *) + &f->memory[bucket_index * f->bucket_size]; + signature |= RTE_BUCKET_ENTRY_VALID; + + /* Key is present in the bucket */ + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if ((bucket_signature == signature) && + (keycmp(bucket_key, key, f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * f->entry_size]; + + memcpy(bucket_data, entry, f->entry_size); + lru_update(bucket, i); + *key_found = 1; + *entry_ptr = (void *) bucket_data; + return 0; + } + } + + /* Key is not present in the bucket */ + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if (bucket_signature == 0) { + uint8_t *bucket_data = &bucket->data[i * f->entry_size]; + + bucket->signature[i] = signature; + keycpy(bucket_key, key, f->key_mask); + memcpy(bucket_data, entry, f->entry_size); + lru_update(bucket, i); + *key_found = 0; + *entry_ptr = (void *) bucket_data; + + return 0; + } + } + + /* Bucket full: replace LRU entry */ + pos = lru_pos(bucket); + bucket->signature[pos] = signature; + keycpy(&bucket->key[pos], key, f->key_mask); + memcpy(&bucket->data[pos * f->entry_size], entry, f->entry_size); + lru_update(bucket, pos); + *key_found = 0; + *entry_ptr = (void *) &bucket->data[pos * f->entry_size]; + + return 0; +} + +static int +rte_table_hash_entry_delete_key16_lru( + void *table, + void *key, + int *key_found, + void *entry) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_16 *bucket; + uint64_t signature; + uint32_t bucket_index, i; + + signature = f->f_hash(key, f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket = (struct rte_bucket_4_16 *) + &f->memory[bucket_index * f->bucket_size]; + signature |= RTE_BUCKET_ENTRY_VALID; + + /* Key is present in the bucket */ + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if ((bucket_signature == signature) && + (keycmp(bucket_key, key, f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * f->entry_size]; + + bucket->signature[i] = 0; + *key_found = 1; + if (entry) + memcpy(entry, bucket_data, f->entry_size); + return 0; + } + } + + /* Key is not present in the bucket */ + *key_found = 0; + return 0; +} + +static void * +rte_table_hash_create_key16_ext(void *params, + int socket_id, + uint32_t entry_size) +{ + struct rte_table_hash_params *p = params; + struct rte_table_hash *f; + uint64_t bucket_size, stack_size, total_size; + uint32_t n_buckets_ext, i; + + /* Check input parameters */ + if ((check_params_create(p) != 0) || + ((sizeof(struct rte_table_hash) % RTE_CACHE_LINE_SIZE) != 0) || + ((sizeof(struct rte_bucket_4_16) % 64) != 0)) + return NULL; + + /* + * Table dimensioning + * + * Objective: Pick the number of bucket extensions (n_buckets_ext) so that + * it is guaranteed that n_keys keys can be stored in the table at any time. + * + * The worst case scenario takes place when all the n_keys keys fall into + * the same bucket. Actually, due to the KEYS_PER_BUCKET scheme, the worst + * case takes place when (n_keys - KEYS_PER_BUCKET + 1) keys fall into the + * same bucket, while the remaining (KEYS_PER_BUCKET - 1) keys each fall + * into a different bucket. This case defeats the purpose of the hash table. + * It indicates unsuitable f_hash or n_keys to n_buckets ratio. + * + * n_buckets_ext = n_keys / KEYS_PER_BUCKET + KEYS_PER_BUCKET - 1 + */ + n_buckets_ext = p->n_keys / KEYS_PER_BUCKET + KEYS_PER_BUCKET - 1; + + /* Memory allocation */ + bucket_size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct rte_bucket_4_16) + + KEYS_PER_BUCKET * entry_size); + stack_size = RTE_CACHE_LINE_ROUNDUP(n_buckets_ext * sizeof(uint32_t)); + total_size = sizeof(struct rte_table_hash) + + (p->n_buckets + n_buckets_ext) * bucket_size + stack_size; + if (total_size > SIZE_MAX) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes " + "for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + + f = rte_zmalloc_socket(p->name, + (size_t)total_size, + RTE_CACHE_LINE_SIZE, + socket_id); + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes " + "for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + RTE_LOG(INFO, TABLE, "%s: Hash table %s memory footprint " + "is %" PRIu64 " bytes\n", + __func__, p->name, total_size); + + /* Memory initialization */ + f->n_buckets = p->n_buckets; + f->key_size = KEY_SIZE; + f->entry_size = entry_size; + f->bucket_size = bucket_size; + f->key_offset = p->key_offset; + f->f_hash = p->f_hash; + f->seed = p->seed; + + f->n_buckets_ext = n_buckets_ext; + f->stack_pos = n_buckets_ext; + f->stack = (uint32_t *) + &f->memory[(p->n_buckets + n_buckets_ext) * f->bucket_size]; + + if (p->key_mask != NULL) { + f->key_mask[0] = (((uint64_t *)p->key_mask)[0]); + f->key_mask[1] = (((uint64_t *)p->key_mask)[1]); + } else { + f->key_mask[0] = 0xFFFFFFFFFFFFFFFFLLU; + f->key_mask[1] = 0xFFFFFFFFFFFFFFFFLLU; + } + + for (i = 0; i < n_buckets_ext; i++) + f->stack[i] = i; + + return f; +} + +static int +rte_table_hash_free_key16_ext(void *table) +{ + struct rte_table_hash *f = table; + + /* Check input parameters */ + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + + rte_free(f); + return 0; +} + +static int +rte_table_hash_entry_add_key16_ext( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_16 *bucket0, *bucket, *bucket_prev; + uint64_t signature; + uint32_t bucket_index, i; + + signature = f->f_hash(key, f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket0 = (struct rte_bucket_4_16 *) + &f->memory[bucket_index * f->bucket_size]; + signature |= RTE_BUCKET_ENTRY_VALID; + + /* Key is present in the bucket */ + for (bucket = bucket0; bucket != NULL; bucket = bucket->next) + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if ((bucket_signature == signature) && + (keycmp(bucket_key, key, f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * + f->entry_size]; + + memcpy(bucket_data, entry, f->entry_size); + *key_found = 1; + *entry_ptr = (void *) bucket_data; + return 0; + } + } + + /* Key is not present in the bucket */ + for (bucket_prev = NULL, bucket = bucket0; bucket != NULL; + bucket_prev = bucket, bucket = bucket->next) + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if (bucket_signature == 0) { + uint8_t *bucket_data = &bucket->data[i * + f->entry_size]; + + bucket->signature[i] = signature; + keycpy(bucket_key, key, f->key_mask); + memcpy(bucket_data, entry, f->entry_size); + *key_found = 0; + *entry_ptr = (void *) bucket_data; + + return 0; + } + } + + /* Bucket full: extend bucket */ + if (f->stack_pos > 0) { + bucket_index = f->stack[--f->stack_pos]; + + bucket = (struct rte_bucket_4_16 *) &f->memory[(f->n_buckets + + bucket_index) * f->bucket_size]; + bucket_prev->next = bucket; + bucket_prev->next_valid = 1; + + bucket->signature[0] = signature; + keycpy(&bucket->key[0], key, f->key_mask); + memcpy(&bucket->data[0], entry, f->entry_size); + *key_found = 0; + *entry_ptr = (void *) &bucket->data[0]; + return 0; + } + + return -ENOSPC; +} + +static int +rte_table_hash_entry_delete_key16_ext( + void *table, + void *key, + int *key_found, + void *entry) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_16 *bucket0, *bucket, *bucket_prev; + uint64_t signature; + uint32_t bucket_index, i; + + signature = f->f_hash(key, f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket0 = (struct rte_bucket_4_16 *) + &f->memory[bucket_index * f->bucket_size]; + signature |= RTE_BUCKET_ENTRY_VALID; + + /* Key is present in the bucket */ + for (bucket_prev = NULL, bucket = bucket0; bucket != NULL; + bucket_prev = bucket, bucket = bucket->next) + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if ((bucket_signature == signature) && + (keycmp(bucket_key, key, f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * + f->entry_size]; + + bucket->signature[i] = 0; + *key_found = 1; + if (entry) + memcpy(entry, bucket_data, f->entry_size); + + if ((bucket->signature[0] == 0) && + (bucket->signature[1] == 0) && + (bucket->signature[2] == 0) && + (bucket->signature[3] == 0) && + (bucket_prev != NULL)) { + bucket_prev->next = bucket->next; + bucket_prev->next_valid = + bucket->next_valid; + + memset(bucket, 0, + sizeof(struct rte_bucket_4_16)); + bucket_index = (((uint8_t *)bucket - + (uint8_t *)f->memory)/f->bucket_size) - f->n_buckets; + f->stack[f->stack_pos++] = bucket_index; + } + + return 0; + } + } + + /* Key is not present in the bucket */ + *key_found = 0; + return 0; +} + +#define lookup_key16_cmp(key_in, bucket, pos, f) \ +{ \ + uint64_t xor[4][2], or[4], signature[4], k[2]; \ + \ + k[0] = key_in[0] & f->key_mask[0]; \ + k[1] = key_in[1] & f->key_mask[1]; \ + signature[0] = (~bucket->signature[0]) & 1; \ + signature[1] = (~bucket->signature[1]) & 1; \ + signature[2] = (~bucket->signature[2]) & 1; \ + signature[3] = (~bucket->signature[3]) & 1; \ + \ + xor[0][0] = k[0] ^ bucket->key[0][0]; \ + xor[0][1] = k[1] ^ bucket->key[0][1]; \ + \ + xor[1][0] = k[0] ^ bucket->key[1][0]; \ + xor[1][1] = k[1] ^ bucket->key[1][1]; \ + \ + xor[2][0] = k[0] ^ bucket->key[2][0]; \ + xor[2][1] = k[1] ^ bucket->key[2][1]; \ + \ + xor[3][0] = k[0] ^ bucket->key[3][0]; \ + xor[3][1] = k[1] ^ bucket->key[3][1]; \ + \ + or[0] = xor[0][0] | xor[0][1] | signature[0]; \ + or[1] = xor[1][0] | xor[1][1] | signature[1]; \ + or[2] = xor[2][0] | xor[2][1] | signature[2]; \ + or[3] = xor[3][0] | xor[3][1] | signature[3]; \ + \ + pos = 4; \ + if (or[0] == 0) \ + pos = 0; \ + if (or[1] == 0) \ + pos = 1; \ + if (or[2] == 0) \ + pos = 2; \ + if (or[3] == 0) \ + pos = 3; \ +} + +#define lookup1_stage0(pkt0_index, mbuf0, pkts, pkts_mask, f) \ +{ \ + uint64_t pkt_mask; \ + uint32_t key_offset = f->key_offset;\ + \ + pkt0_index = __builtin_ctzll(pkts_mask); \ + pkt_mask = 1LLU << pkt0_index; \ + pkts_mask &= ~pkt_mask; \ + \ + mbuf0 = pkts[pkt0_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf0, key_offset));\ +} + +#define lookup1_stage1(mbuf1, bucket1, f) \ +{ \ + uint64_t *key; \ + uint64_t signature = 0; \ + uint32_t bucket_index; \ + \ + key = RTE_MBUF_METADATA_UINT64_PTR(mbuf1, f->key_offset);\ + signature = f->f_hash(key, f->key_mask, KEY_SIZE, f->seed); \ + \ + bucket_index = signature & (f->n_buckets - 1); \ + bucket1 = (struct rte_bucket_4_16 *) \ + &f->memory[bucket_index * f->bucket_size]; \ + rte_prefetch0(bucket1); \ + rte_prefetch0((void *)(((uintptr_t) bucket1) + RTE_CACHE_LINE_SIZE));\ +} + +#define lookup1_stage2_lru(pkt2_index, mbuf2, bucket2, \ + pkts_mask_out, entries, f) \ +{ \ + void *a; \ + uint64_t pkt_mask; \ + uint64_t *key; \ + uint32_t pos; \ + \ + key = RTE_MBUF_METADATA_UINT64_PTR(mbuf2, f->key_offset);\ + lookup_key16_cmp(key, bucket2, pos, f); \ + \ + pkt_mask = (bucket2->signature[pos] & 1LLU) << pkt2_index;\ + pkts_mask_out |= pkt_mask; \ + \ + a = (void *) &bucket2->data[pos * f->entry_size]; \ + rte_prefetch0(a); \ + entries[pkt2_index] = a; \ + lru_update(bucket2, pos); \ +} + +#define lookup1_stage2_ext(pkt2_index, mbuf2, bucket2, pkts_mask_out, entries, \ + buckets_mask, buckets, keys, f) \ +{ \ + struct rte_bucket_4_16 *bucket_next; \ + void *a; \ + uint64_t pkt_mask, bucket_mask; \ + uint64_t *key; \ + uint32_t pos; \ + \ + key = RTE_MBUF_METADATA_UINT64_PTR(mbuf2, f->key_offset);\ + lookup_key16_cmp(key, bucket2, pos, f); \ + \ + pkt_mask = (bucket2->signature[pos] & 1LLU) << pkt2_index;\ + pkts_mask_out |= pkt_mask; \ + \ + a = (void *) &bucket2->data[pos * f->entry_size]; \ + rte_prefetch0(a); \ + entries[pkt2_index] = a; \ + \ + bucket_mask = (~pkt_mask) & (bucket2->next_valid << pkt2_index);\ + buckets_mask |= bucket_mask; \ + bucket_next = bucket2->next; \ + buckets[pkt2_index] = bucket_next; \ + keys[pkt2_index] = key; \ +} + +#define lookup_grinder(pkt_index, buckets, keys, pkts_mask_out, entries,\ + buckets_mask, f) \ +{ \ + struct rte_bucket_4_16 *bucket, *bucket_next; \ + void *a; \ + uint64_t pkt_mask, bucket_mask; \ + uint64_t *key; \ + uint32_t pos; \ + \ + bucket = buckets[pkt_index]; \ + key = keys[pkt_index]; \ + lookup_key16_cmp(key, bucket, pos, f); \ + \ + pkt_mask = (bucket->signature[pos] & 1LLU) << pkt_index;\ + pkts_mask_out |= pkt_mask; \ + \ + a = (void *) &bucket->data[pos * f->entry_size]; \ + rte_prefetch0(a); \ + entries[pkt_index] = a; \ + \ + bucket_mask = (~pkt_mask) & (bucket->next_valid << pkt_index);\ + buckets_mask |= bucket_mask; \ + bucket_next = bucket->next; \ + rte_prefetch0(bucket_next); \ + rte_prefetch0((void *)(((uintptr_t) bucket_next) + RTE_CACHE_LINE_SIZE));\ + buckets[pkt_index] = bucket_next; \ + keys[pkt_index] = key; \ +} + +#define lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01,\ + pkts, pkts_mask, f) \ +{ \ + uint64_t pkt00_mask, pkt01_mask; \ + uint32_t key_offset = f->key_offset; \ + \ + pkt00_index = __builtin_ctzll(pkts_mask); \ + pkt00_mask = 1LLU << pkt00_index; \ + pkts_mask &= ~pkt00_mask; \ + \ + mbuf00 = pkts[pkt00_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf00, key_offset));\ + \ + pkt01_index = __builtin_ctzll(pkts_mask); \ + pkt01_mask = 1LLU << pkt01_index; \ + pkts_mask &= ~pkt01_mask; \ + \ + mbuf01 = pkts[pkt01_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf01, key_offset));\ +} + +#define lookup2_stage0_with_odd_support(pkt00_index, pkt01_index,\ + mbuf00, mbuf01, pkts, pkts_mask, f) \ +{ \ + uint64_t pkt00_mask, pkt01_mask; \ + uint32_t key_offset = f->key_offset; \ + \ + pkt00_index = __builtin_ctzll(pkts_mask); \ + pkt00_mask = 1LLU << pkt00_index; \ + pkts_mask &= ~pkt00_mask; \ + \ + mbuf00 = pkts[pkt00_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf00, key_offset)); \ + \ + pkt01_index = __builtin_ctzll(pkts_mask); \ + if (pkts_mask == 0) \ + pkt01_index = pkt00_index; \ + pkt01_mask = 1LLU << pkt01_index; \ + pkts_mask &= ~pkt01_mask; \ + \ + mbuf01 = pkts[pkt01_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf01, key_offset)); \ +} + +#define lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f) \ +{ \ + uint64_t *key10, *key11; \ + uint64_t signature10, signature11; \ + uint32_t bucket10_index, bucket11_index; \ + \ + key10 = RTE_MBUF_METADATA_UINT64_PTR(mbuf10, f->key_offset);\ + signature10 = f->f_hash(key10, f->key_mask, KEY_SIZE, f->seed);\ + bucket10_index = signature10 & (f->n_buckets - 1); \ + bucket10 = (struct rte_bucket_4_16 *) \ + &f->memory[bucket10_index * f->bucket_size]; \ + rte_prefetch0(bucket10); \ + rte_prefetch0((void *)(((uintptr_t) bucket10) + RTE_CACHE_LINE_SIZE));\ + \ + key11 = RTE_MBUF_METADATA_UINT64_PTR(mbuf11, f->key_offset);\ + signature11 = f->f_hash(key11, f->key_mask, KEY_SIZE, f->seed);\ + bucket11_index = signature11 & (f->n_buckets - 1); \ + bucket11 = (struct rte_bucket_4_16 *) \ + &f->memory[bucket11_index * f->bucket_size]; \ + rte_prefetch0(bucket11); \ + rte_prefetch0((void *)(((uintptr_t) bucket11) + RTE_CACHE_LINE_SIZE));\ +} + +#define lookup2_stage2_lru(pkt20_index, pkt21_index, mbuf20, mbuf21,\ + bucket20, bucket21, pkts_mask_out, entries, f) \ +{ \ + void *a20, *a21; \ + uint64_t pkt20_mask, pkt21_mask; \ + uint64_t *key20, *key21; \ + uint32_t pos20, pos21; \ + \ + key20 = RTE_MBUF_METADATA_UINT64_PTR(mbuf20, f->key_offset);\ + key21 = RTE_MBUF_METADATA_UINT64_PTR(mbuf21, f->key_offset);\ + \ + lookup_key16_cmp(key20, bucket20, pos20, f); \ + lookup_key16_cmp(key21, bucket21, pos21, f); \ + \ + pkt20_mask = (bucket20->signature[pos20] & 1LLU) << pkt20_index;\ + pkt21_mask = (bucket21->signature[pos21] & 1LLU) << pkt21_index;\ + pkts_mask_out |= pkt20_mask | pkt21_mask; \ + \ + a20 = (void *) &bucket20->data[pos20 * f->entry_size]; \ + a21 = (void *) &bucket21->data[pos21 * f->entry_size]; \ + rte_prefetch0(a20); \ + rte_prefetch0(a21); \ + entries[pkt20_index] = a20; \ + entries[pkt21_index] = a21; \ + lru_update(bucket20, pos20); \ + lru_update(bucket21, pos21); \ +} + +#define lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, bucket20, \ + bucket21, pkts_mask_out, entries, buckets_mask, buckets, keys, f) \ +{ \ + struct rte_bucket_4_16 *bucket20_next, *bucket21_next; \ + void *a20, *a21; \ + uint64_t pkt20_mask, pkt21_mask, bucket20_mask, bucket21_mask;\ + uint64_t *key20, *key21; \ + uint32_t pos20, pos21; \ + \ + key20 = RTE_MBUF_METADATA_UINT64_PTR(mbuf20, f->key_offset);\ + key21 = RTE_MBUF_METADATA_UINT64_PTR(mbuf21, f->key_offset);\ + \ + lookup_key16_cmp(key20, bucket20, pos20, f); \ + lookup_key16_cmp(key21, bucket21, pos21, f); \ + \ + pkt20_mask = (bucket20->signature[pos20] & 1LLU) << pkt20_index;\ + pkt21_mask = (bucket21->signature[pos21] & 1LLU) << pkt21_index;\ + pkts_mask_out |= pkt20_mask | pkt21_mask; \ + \ + a20 = (void *) &bucket20->data[pos20 * f->entry_size]; \ + a21 = (void *) &bucket21->data[pos21 * f->entry_size]; \ + rte_prefetch0(a20); \ + rte_prefetch0(a21); \ + entries[pkt20_index] = a20; \ + entries[pkt21_index] = a21; \ + \ + bucket20_mask = (~pkt20_mask) & (bucket20->next_valid << pkt20_index);\ + bucket21_mask = (~pkt21_mask) & (bucket21->next_valid << pkt21_index);\ + buckets_mask |= bucket20_mask | bucket21_mask; \ + bucket20_next = bucket20->next; \ + bucket21_next = bucket21->next; \ + buckets[pkt20_index] = bucket20_next; \ + buckets[pkt21_index] = bucket21_next; \ + keys[pkt20_index] = key20; \ + keys[pkt21_index] = key21; \ +} + +static int +rte_table_hash_lookup_key16_lru( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *f = (struct rte_table_hash *) table; + struct rte_bucket_4_16 *bucket10, *bucket11, *bucket20, *bucket21; + struct rte_mbuf *mbuf00, *mbuf01, *mbuf10, *mbuf11, *mbuf20, *mbuf21; + uint32_t pkt00_index, pkt01_index, pkt10_index; + uint32_t pkt11_index, pkt20_index, pkt21_index; + uint64_t pkts_mask_out = 0; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + + RTE_TABLE_HASH_KEY16_STATS_PKTS_IN_ADD(f, n_pkts_in); + + /* Cannot run the pipeline with less than 5 packets */ + if (__builtin_popcountll(pkts_mask) < 5) { + for ( ; pkts_mask; ) { + struct rte_bucket_4_16 *bucket; + struct rte_mbuf *mbuf; + uint32_t pkt_index; + + lookup1_stage0(pkt_index, mbuf, pkts, pkts_mask, f); + lookup1_stage1(mbuf, bucket, f); + lookup1_stage2_lru(pkt_index, mbuf, bucket, + pkts_mask_out, entries, f); + } + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_KEY16_STATS_PKTS_LOOKUP_MISS(f, n_pkts_in - + __builtin_popcountll(pkts_mask_out)); + return 0; + } + + /* + * Pipeline fill + * + */ + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline feed */ + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* + * Pipeline run + * + */ + for ( ; pkts_mask; ) { + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0_with_odd_support(pkt00_index, pkt01_index, + mbuf00, mbuf01, pkts, pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_lru(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, f); + } + + /* + * Pipeline flush + * + */ + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_lru(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, f); + + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + + /* Pipeline stage 2 */ + lookup2_stage2_lru(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, f); + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_KEY16_STATS_PKTS_LOOKUP_MISS(f, n_pkts_in - + __builtin_popcountll(pkts_mask_out)); + return 0; +} /* lookup LRU */ + +static int +rte_table_hash_lookup_key16_ext( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *f = (struct rte_table_hash *) table; + struct rte_bucket_4_16 *bucket10, *bucket11, *bucket20, *bucket21; + struct rte_mbuf *mbuf00, *mbuf01, *mbuf10, *mbuf11, *mbuf20, *mbuf21; + uint32_t pkt00_index, pkt01_index, pkt10_index; + uint32_t pkt11_index, pkt20_index, pkt21_index; + uint64_t pkts_mask_out = 0, buckets_mask = 0; + struct rte_bucket_4_16 *buckets[RTE_PORT_IN_BURST_SIZE_MAX]; + uint64_t *keys[RTE_PORT_IN_BURST_SIZE_MAX]; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + + RTE_TABLE_HASH_KEY16_STATS_PKTS_IN_ADD(f, n_pkts_in); + + /* Cannot run the pipeline with less than 5 packets */ + if (__builtin_popcountll(pkts_mask) < 5) { + for ( ; pkts_mask; ) { + struct rte_bucket_4_16 *bucket; + struct rte_mbuf *mbuf; + uint32_t pkt_index; + + lookup1_stage0(pkt_index, mbuf, pkts, pkts_mask, f); + lookup1_stage1(mbuf, bucket, f); + lookup1_stage2_ext(pkt_index, mbuf, bucket, + pkts_mask_out, entries, buckets_mask, + buckets, keys, f); + } + + goto grind_next_buckets; + } + + /* + * Pipeline fill + * + */ + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline feed */ + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* + * Pipeline run + * + */ + for ( ; pkts_mask; ) { + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0_with_odd_support(pkt00_index, pkt01_index, + mbuf00, mbuf01, pkts, pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, + buckets_mask, buckets, keys, f); + } + + /* + * Pipeline flush + * + */ + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, + buckets_mask, buckets, keys, f); + + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + + /* Pipeline stage 2 */ + lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, + buckets_mask, buckets, keys, f); + +grind_next_buckets: + /* Grind next buckets */ + for ( ; buckets_mask; ) { + uint64_t buckets_mask_next = 0; + + for ( ; buckets_mask; ) { + uint64_t pkt_mask; + uint32_t pkt_index; + + pkt_index = __builtin_ctzll(buckets_mask); + pkt_mask = 1LLU << pkt_index; + buckets_mask &= ~pkt_mask; + + lookup_grinder(pkt_index, buckets, keys, pkts_mask_out, + entries, buckets_mask_next, f); + } + + buckets_mask = buckets_mask_next; + } + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_KEY16_STATS_PKTS_LOOKUP_MISS(f, n_pkts_in - + __builtin_popcountll(pkts_mask_out)); + return 0; +} /* lookup EXT */ + +static int +rte_table_hash_key16_stats_read(void *table, struct rte_table_stats *stats, int clear) +{ + struct rte_table_hash *t = table; + + if (stats != NULL) + memcpy(stats, &t->stats, sizeof(t->stats)); + + if (clear) + memset(&t->stats, 0, sizeof(t->stats)); + + return 0; +} + +struct rte_table_ops rte_table_hash_key16_lru_ops = { + .f_create = rte_table_hash_create_key16_lru, + .f_free = rte_table_hash_free_key16_lru, + .f_add = rte_table_hash_entry_add_key16_lru, + .f_delete = rte_table_hash_entry_delete_key16_lru, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_hash_lookup_key16_lru, + .f_stats = rte_table_hash_key16_stats_read, +}; + +struct rte_table_ops rte_table_hash_key16_ext_ops = { + .f_create = rte_table_hash_create_key16_ext, + .f_free = rte_table_hash_free_key16_ext, + .f_add = rte_table_hash_entry_add_key16_ext, + .f_delete = rte_table_hash_entry_delete_key16_ext, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_hash_lookup_key16_ext, + .f_stats = rte_table_hash_key16_stats_read, +}; diff --git a/lib/table/rte_table_hash_key32.c b/lib/table/rte_table_hash_key32.c new file mode 100644 index 0000000000..3e0031fe1e --- /dev/null +++ b/lib/table/rte_table_hash_key32.c @@ -0,0 +1,1220 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2017 Intel Corporation + */ +#include <string.h> +#include <stdio.h> + +#include <rte_common.h> +#include <rte_mbuf.h> +#include <rte_memory.h> +#include <rte_malloc.h> +#include <rte_log.h> + +#include "rte_table_hash.h" +#include "rte_lru.h" + +#define KEY_SIZE 32 + +#define KEYS_PER_BUCKET 4 + +#define RTE_BUCKET_ENTRY_VALID 0x1LLU + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_HASH_KEY32_STATS_PKTS_IN_ADD(table, val) \ + table->stats.n_pkts_in += val +#define RTE_TABLE_HASH_KEY32_STATS_PKTS_LOOKUP_MISS(table, val) \ + table->stats.n_pkts_lookup_miss += val + +#else + +#define RTE_TABLE_HASH_KEY32_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_HASH_KEY32_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + +#ifdef RTE_ARCH_64 +struct rte_bucket_4_32 { + /* Cache line 0 */ + uint64_t signature[4 + 1]; + uint64_t lru_list; + struct rte_bucket_4_32 *next; + uint64_t next_valid; + + /* Cache lines 1 and 2 */ + uint64_t key[4][4]; + + /* Cache line 3 */ + uint8_t data[0]; +}; +#else +struct rte_bucket_4_32 { + /* Cache line 0 */ + uint64_t signature[4 + 1]; + uint64_t lru_list; + struct rte_bucket_4_32 *next; + uint32_t pad; + uint64_t next_valid; + + /* Cache lines 1 and 2 */ + uint64_t key[4][4]; + + /* Cache line 3 */ + uint8_t data[0]; +}; +#endif + +struct rte_table_hash { + struct rte_table_stats stats; + + /* Input parameters */ + uint32_t n_buckets; + uint32_t key_size; + uint32_t entry_size; + uint32_t bucket_size; + uint32_t key_offset; + uint64_t key_mask[4]; + rte_table_hash_op_hash f_hash; + uint64_t seed; + + /* Extendible buckets */ + uint32_t n_buckets_ext; + uint32_t stack_pos; + uint32_t *stack; + + /* Lookup table */ + uint8_t memory[0] __rte_cache_aligned; +}; + +static int +keycmp(void *a, void *b, void *b_mask) +{ + uint64_t *a64 = a, *b64 = b, *b_mask64 = b_mask; + + return (a64[0] != (b64[0] & b_mask64[0])) || + (a64[1] != (b64[1] & b_mask64[1])) || + (a64[2] != (b64[2] & b_mask64[2])) || + (a64[3] != (b64[3] & b_mask64[3])); +} + +static void +keycpy(void *dst, void *src, void *src_mask) +{ + uint64_t *dst64 = dst, *src64 = src, *src_mask64 = src_mask; + + dst64[0] = src64[0] & src_mask64[0]; + dst64[1] = src64[1] & src_mask64[1]; + dst64[2] = src64[2] & src_mask64[2]; + dst64[3] = src64[3] & src_mask64[3]; +} + +static int +check_params_create(struct rte_table_hash_params *params) +{ + /* name */ + if (params->name == NULL) { + RTE_LOG(ERR, TABLE, "%s: name invalid value\n", __func__); + return -EINVAL; + } + + /* key_size */ + if (params->key_size != KEY_SIZE) { + RTE_LOG(ERR, TABLE, "%s: key_size invalid value\n", __func__); + return -EINVAL; + } + + /* n_keys */ + if (params->n_keys == 0) { + RTE_LOG(ERR, TABLE, "%s: n_keys is zero\n", __func__); + return -EINVAL; + } + + /* n_buckets */ + if ((params->n_buckets == 0) || + (!rte_is_power_of_2(params->n_buckets))) { + RTE_LOG(ERR, TABLE, "%s: n_buckets invalid value\n", __func__); + return -EINVAL; + } + + /* f_hash */ + if (params->f_hash == NULL) { + RTE_LOG(ERR, TABLE, "%s: f_hash function pointer is NULL\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static void * +rte_table_hash_create_key32_lru(void *params, + int socket_id, + uint32_t entry_size) +{ + struct rte_table_hash_params *p = params; + struct rte_table_hash *f; + uint64_t bucket_size, total_size; + uint32_t n_buckets, i; + + /* Check input parameters */ + if ((check_params_create(p) != 0) || + ((sizeof(struct rte_table_hash) % RTE_CACHE_LINE_SIZE) != 0) || + ((sizeof(struct rte_bucket_4_32) % 64) != 0)) + return NULL; + + /* + * Table dimensioning + * + * Objective: Pick the number of buckets (n_buckets) so that there a chance + * to store n_keys keys in the table. + * + * Note: Since the buckets do not get extended, it is not possible to + * guarantee that n_keys keys can be stored in the table at any time. In the + * worst case scenario when all the n_keys fall into the same bucket, only + * a maximum of KEYS_PER_BUCKET keys will be stored in the table. This case + * defeats the purpose of the hash table. It indicates unsuitable f_hash or + * n_keys to n_buckets ratio. + * + * MIN(n_buckets) = (n_keys + KEYS_PER_BUCKET - 1) / KEYS_PER_BUCKET + */ + n_buckets = rte_align32pow2( + (p->n_keys + KEYS_PER_BUCKET - 1) / KEYS_PER_BUCKET); + n_buckets = RTE_MAX(n_buckets, p->n_buckets); + + /* Memory allocation */ + bucket_size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct rte_bucket_4_32) + + KEYS_PER_BUCKET * entry_size); + total_size = sizeof(struct rte_table_hash) + n_buckets * bucket_size; + if (total_size > SIZE_MAX) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes " + "for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + + f = rte_zmalloc_socket(p->name, + (size_t)total_size, + RTE_CACHE_LINE_SIZE, + socket_id); + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes " + "for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + RTE_LOG(INFO, TABLE, + "%s: Hash table %s memory footprint " + "is %" PRIu64 " bytes\n", + __func__, p->name, total_size); + + /* Memory initialization */ + f->n_buckets = n_buckets; + f->key_size = KEY_SIZE; + f->entry_size = entry_size; + f->bucket_size = bucket_size; + f->key_offset = p->key_offset; + f->f_hash = p->f_hash; + f->seed = p->seed; + + if (p->key_mask != NULL) { + f->key_mask[0] = ((uint64_t *)p->key_mask)[0]; + f->key_mask[1] = ((uint64_t *)p->key_mask)[1]; + f->key_mask[2] = ((uint64_t *)p->key_mask)[2]; + f->key_mask[3] = ((uint64_t *)p->key_mask)[3]; + } else { + f->key_mask[0] = 0xFFFFFFFFFFFFFFFFLLU; + f->key_mask[1] = 0xFFFFFFFFFFFFFFFFLLU; + f->key_mask[2] = 0xFFFFFFFFFFFFFFFFLLU; + f->key_mask[3] = 0xFFFFFFFFFFFFFFFFLLU; + } + + for (i = 0; i < n_buckets; i++) { + struct rte_bucket_4_32 *bucket; + + bucket = (struct rte_bucket_4_32 *) &f->memory[i * + f->bucket_size]; + bucket->lru_list = 0x0000000100020003LLU; + } + + return f; +} + +static int +rte_table_hash_free_key32_lru(void *table) +{ + struct rte_table_hash *f = table; + + /* Check input parameters */ + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + + rte_free(f); + return 0; +} + +static int +rte_table_hash_entry_add_key32_lru( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_32 *bucket; + uint64_t signature, pos; + uint32_t bucket_index, i; + + signature = f->f_hash(key, f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket = (struct rte_bucket_4_32 *) + &f->memory[bucket_index * f->bucket_size]; + signature |= RTE_BUCKET_ENTRY_VALID; + + /* Key is present in the bucket */ + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if ((bucket_signature == signature) && + (keycmp(bucket_key, key, f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * f->entry_size]; + + memcpy(bucket_data, entry, f->entry_size); + lru_update(bucket, i); + *key_found = 1; + *entry_ptr = (void *) bucket_data; + return 0; + } + } + + /* Key is not present in the bucket */ + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if (bucket_signature == 0) { + uint8_t *bucket_data = &bucket->data[i * f->entry_size]; + + bucket->signature[i] = signature; + keycpy(bucket_key, key, f->key_mask); + memcpy(bucket_data, entry, f->entry_size); + lru_update(bucket, i); + *key_found = 0; + *entry_ptr = (void *) bucket_data; + + return 0; + } + } + + /* Bucket full: replace LRU entry */ + pos = lru_pos(bucket); + bucket->signature[pos] = signature; + keycpy(&bucket->key[pos], key, f->key_mask); + memcpy(&bucket->data[pos * f->entry_size], entry, f->entry_size); + lru_update(bucket, pos); + *key_found = 0; + *entry_ptr = (void *) &bucket->data[pos * f->entry_size]; + + return 0; +} + +static int +rte_table_hash_entry_delete_key32_lru( + void *table, + void *key, + int *key_found, + void *entry) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_32 *bucket; + uint64_t signature; + uint32_t bucket_index, i; + + signature = f->f_hash(key, f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket = (struct rte_bucket_4_32 *) + &f->memory[bucket_index * f->bucket_size]; + signature |= RTE_BUCKET_ENTRY_VALID; + + /* Key is present in the bucket */ + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if ((bucket_signature == signature) && + (keycmp(bucket_key, key, f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * f->entry_size]; + + bucket->signature[i] = 0; + *key_found = 1; + if (entry) + memcpy(entry, bucket_data, f->entry_size); + + return 0; + } + } + + /* Key is not present in the bucket */ + *key_found = 0; + return 0; +} + +static void * +rte_table_hash_create_key32_ext(void *params, + int socket_id, + uint32_t entry_size) +{ + struct rte_table_hash_params *p = params; + struct rte_table_hash *f; + uint64_t bucket_size, stack_size, total_size; + uint32_t n_buckets_ext, i; + + /* Check input parameters */ + if ((check_params_create(p) != 0) || + ((sizeof(struct rte_table_hash) % RTE_CACHE_LINE_SIZE) != 0) || + ((sizeof(struct rte_bucket_4_32) % 64) != 0)) + return NULL; + + /* + * Table dimensioning + * + * Objective: Pick the number of bucket extensions (n_buckets_ext) so that + * it is guaranteed that n_keys keys can be stored in the table at any time. + * + * The worst case scenario takes place when all the n_keys keys fall into + * the same bucket. Actually, due to the KEYS_PER_BUCKET scheme, the worst + * case takes place when (n_keys - KEYS_PER_BUCKET + 1) keys fall into the + * same bucket, while the remaining (KEYS_PER_BUCKET - 1) keys each fall + * into a different bucket. This case defeats the purpose of the hash table. + * It indicates unsuitable f_hash or n_keys to n_buckets ratio. + * + * n_buckets_ext = n_keys / KEYS_PER_BUCKET + KEYS_PER_BUCKET - 1 + */ + n_buckets_ext = p->n_keys / KEYS_PER_BUCKET + KEYS_PER_BUCKET - 1; + + /* Memory allocation */ + bucket_size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct rte_bucket_4_32) + + KEYS_PER_BUCKET * entry_size); + stack_size = RTE_CACHE_LINE_ROUNDUP(n_buckets_ext * sizeof(uint32_t)); + total_size = sizeof(struct rte_table_hash) + + (p->n_buckets + n_buckets_ext) * bucket_size + stack_size; + if (total_size > SIZE_MAX) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes " + "for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + + f = rte_zmalloc_socket(p->name, + (size_t)total_size, + RTE_CACHE_LINE_SIZE, + socket_id); + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes " + "for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + RTE_LOG(INFO, TABLE, + "%s: Hash table %s memory footprint " + "is %" PRIu64" bytes\n", + __func__, p->name, total_size); + + /* Memory initialization */ + f->n_buckets = p->n_buckets; + f->key_size = KEY_SIZE; + f->entry_size = entry_size; + f->bucket_size = bucket_size; + f->key_offset = p->key_offset; + f->f_hash = p->f_hash; + f->seed = p->seed; + + f->n_buckets_ext = n_buckets_ext; + f->stack_pos = n_buckets_ext; + f->stack = (uint32_t *) + &f->memory[(p->n_buckets + n_buckets_ext) * f->bucket_size]; + + if (p->key_mask != NULL) { + f->key_mask[0] = (((uint64_t *)p->key_mask)[0]); + f->key_mask[1] = (((uint64_t *)p->key_mask)[1]); + f->key_mask[2] = (((uint64_t *)p->key_mask)[2]); + f->key_mask[3] = (((uint64_t *)p->key_mask)[3]); + } else { + f->key_mask[0] = 0xFFFFFFFFFFFFFFFFLLU; + f->key_mask[1] = 0xFFFFFFFFFFFFFFFFLLU; + f->key_mask[2] = 0xFFFFFFFFFFFFFFFFLLU; + f->key_mask[3] = 0xFFFFFFFFFFFFFFFFLLU; + } + + for (i = 0; i < n_buckets_ext; i++) + f->stack[i] = i; + + return f; +} + +static int +rte_table_hash_free_key32_ext(void *table) +{ + struct rte_table_hash *f = table; + + /* Check input parameters */ + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + + rte_free(f); + return 0; +} + +static int +rte_table_hash_entry_add_key32_ext( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_32 *bucket0, *bucket, *bucket_prev; + uint64_t signature; + uint32_t bucket_index, i; + + signature = f->f_hash(key, f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket0 = (struct rte_bucket_4_32 *) + &f->memory[bucket_index * f->bucket_size]; + signature |= RTE_BUCKET_ENTRY_VALID; + + /* Key is present in the bucket */ + for (bucket = bucket0; bucket != NULL; bucket = bucket->next) { + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if ((bucket_signature == signature) && + (keycmp(bucket_key, key, f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * + f->entry_size]; + + memcpy(bucket_data, entry, f->entry_size); + *key_found = 1; + *entry_ptr = (void *) bucket_data; + + return 0; + } + } + } + + /* Key is not present in the bucket */ + for (bucket_prev = NULL, bucket = bucket0; bucket != NULL; + bucket_prev = bucket, bucket = bucket->next) + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if (bucket_signature == 0) { + uint8_t *bucket_data = &bucket->data[i * + f->entry_size]; + + bucket->signature[i] = signature; + keycpy(bucket_key, key, f->key_mask); + memcpy(bucket_data, entry, f->entry_size); + *key_found = 0; + *entry_ptr = (void *) bucket_data; + + return 0; + } + } + + /* Bucket full: extend bucket */ + if (f->stack_pos > 0) { + bucket_index = f->stack[--f->stack_pos]; + + bucket = (struct rte_bucket_4_32 *) + &f->memory[(f->n_buckets + bucket_index) * + f->bucket_size]; + bucket_prev->next = bucket; + bucket_prev->next_valid = 1; + + bucket->signature[0] = signature; + keycpy(&bucket->key[0], key, f->key_mask); + memcpy(&bucket->data[0], entry, f->entry_size); + *key_found = 0; + *entry_ptr = (void *) &bucket->data[0]; + return 0; + } + + return -ENOSPC; +} + +static int +rte_table_hash_entry_delete_key32_ext( + void *table, + void *key, + int *key_found, + void *entry) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_32 *bucket0, *bucket, *bucket_prev; + uint64_t signature; + uint32_t bucket_index, i; + + signature = f->f_hash(key, f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket0 = (struct rte_bucket_4_32 *) + &f->memory[bucket_index * f->bucket_size]; + signature |= RTE_BUCKET_ENTRY_VALID; + + /* Key is present in the bucket */ + for (bucket_prev = NULL, bucket = bucket0; bucket != NULL; + bucket_prev = bucket, bucket = bucket->next) + for (i = 0; i < 4; i++) { + uint64_t bucket_signature = bucket->signature[i]; + uint8_t *bucket_key = (uint8_t *) &bucket->key[i]; + + if ((bucket_signature == signature) && + (keycmp(bucket_key, key, f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * + f->entry_size]; + + bucket->signature[i] = 0; + *key_found = 1; + if (entry) + memcpy(entry, bucket_data, f->entry_size); + + if ((bucket->signature[0] == 0) && + (bucket->signature[1] == 0) && + (bucket->signature[2] == 0) && + (bucket->signature[3] == 0) && + (bucket_prev != NULL)) { + bucket_prev->next = bucket->next; + bucket_prev->next_valid = + bucket->next_valid; + + memset(bucket, 0, + sizeof(struct rte_bucket_4_32)); + bucket_index = (((uint8_t *)bucket - + (uint8_t *)f->memory)/f->bucket_size) - f->n_buckets; + f->stack[f->stack_pos++] = bucket_index; + } + + return 0; + } + } + + /* Key is not present in the bucket */ + *key_found = 0; + return 0; +} + +#define lookup_key32_cmp(key_in, bucket, pos, f) \ +{ \ + uint64_t xor[4][4], or[4], signature[4], k[4]; \ + \ + k[0] = key_in[0] & f->key_mask[0]; \ + k[1] = key_in[1] & f->key_mask[1]; \ + k[2] = key_in[2] & f->key_mask[2]; \ + k[3] = key_in[3] & f->key_mask[3]; \ + \ + signature[0] = ((~bucket->signature[0]) & 1); \ + signature[1] = ((~bucket->signature[1]) & 1); \ + signature[2] = ((~bucket->signature[2]) & 1); \ + signature[3] = ((~bucket->signature[3]) & 1); \ + \ + xor[0][0] = k[0] ^ bucket->key[0][0]; \ + xor[0][1] = k[1] ^ bucket->key[0][1]; \ + xor[0][2] = k[2] ^ bucket->key[0][2]; \ + xor[0][3] = k[3] ^ bucket->key[0][3]; \ + \ + xor[1][0] = k[0] ^ bucket->key[1][0]; \ + xor[1][1] = k[1] ^ bucket->key[1][1]; \ + xor[1][2] = k[2] ^ bucket->key[1][2]; \ + xor[1][3] = k[3] ^ bucket->key[1][3]; \ + \ + xor[2][0] = k[0] ^ bucket->key[2][0]; \ + xor[2][1] = k[1] ^ bucket->key[2][1]; \ + xor[2][2] = k[2] ^ bucket->key[2][2]; \ + xor[2][3] = k[3] ^ bucket->key[2][3]; \ + \ + xor[3][0] = k[0] ^ bucket->key[3][0]; \ + xor[3][1] = k[1] ^ bucket->key[3][1]; \ + xor[3][2] = k[2] ^ bucket->key[3][2]; \ + xor[3][3] = k[3] ^ bucket->key[3][3]; \ + \ + or[0] = xor[0][0] | xor[0][1] | xor[0][2] | xor[0][3] | signature[0];\ + or[1] = xor[1][0] | xor[1][1] | xor[1][2] | xor[1][3] | signature[1];\ + or[2] = xor[2][0] | xor[2][1] | xor[2][2] | xor[2][3] | signature[2];\ + or[3] = xor[3][0] | xor[3][1] | xor[3][2] | xor[3][3] | signature[3];\ + \ + pos = 4; \ + if (or[0] == 0) \ + pos = 0; \ + if (or[1] == 0) \ + pos = 1; \ + if (or[2] == 0) \ + pos = 2; \ + if (or[3] == 0) \ + pos = 3; \ +} + +#define lookup1_stage0(pkt0_index, mbuf0, pkts, pkts_mask, f) \ +{ \ + uint64_t pkt_mask; \ + uint32_t key_offset = f->key_offset; \ + \ + pkt0_index = __builtin_ctzll(pkts_mask); \ + pkt_mask = 1LLU << pkt0_index; \ + pkts_mask &= ~pkt_mask; \ + \ + mbuf0 = pkts[pkt0_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf0, key_offset));\ +} + +#define lookup1_stage1(mbuf1, bucket1, f) \ +{ \ + uint64_t *key; \ + uint64_t signature; \ + uint32_t bucket_index; \ + \ + key = RTE_MBUF_METADATA_UINT64_PTR(mbuf1, f->key_offset); \ + signature = f->f_hash(key, f->key_mask, KEY_SIZE, f->seed); \ + \ + bucket_index = signature & (f->n_buckets - 1); \ + bucket1 = (struct rte_bucket_4_32 *) \ + &f->memory[bucket_index * f->bucket_size]; \ + rte_prefetch0(bucket1); \ + rte_prefetch0((void *)(((uintptr_t) bucket1) + RTE_CACHE_LINE_SIZE));\ + rte_prefetch0((void *)(((uintptr_t) bucket1) + 2 * RTE_CACHE_LINE_SIZE));\ +} + +#define lookup1_stage2_lru(pkt2_index, mbuf2, bucket2, \ + pkts_mask_out, entries, f) \ +{ \ + void *a; \ + uint64_t pkt_mask; \ + uint64_t *key; \ + uint32_t pos; \ + \ + key = RTE_MBUF_METADATA_UINT64_PTR(mbuf2, f->key_offset);\ + lookup_key32_cmp(key, bucket2, pos, f); \ + \ + pkt_mask = (bucket2->signature[pos] & 1LLU) << pkt2_index;\ + pkts_mask_out |= pkt_mask; \ + \ + a = (void *) &bucket2->data[pos * f->entry_size]; \ + rte_prefetch0(a); \ + entries[pkt2_index] = a; \ + lru_update(bucket2, pos); \ +} + +#define lookup1_stage2_ext(pkt2_index, mbuf2, bucket2, pkts_mask_out,\ + entries, buckets_mask, buckets, keys, f) \ +{ \ + struct rte_bucket_4_32 *bucket_next; \ + void *a; \ + uint64_t pkt_mask, bucket_mask; \ + uint64_t *key; \ + uint32_t pos; \ + \ + key = RTE_MBUF_METADATA_UINT64_PTR(mbuf2, f->key_offset);\ + lookup_key32_cmp(key, bucket2, pos, f); \ + \ + pkt_mask = (bucket2->signature[pos] & 1LLU) << pkt2_index;\ + pkts_mask_out |= pkt_mask; \ + \ + a = (void *) &bucket2->data[pos * f->entry_size]; \ + rte_prefetch0(a); \ + entries[pkt2_index] = a; \ + \ + bucket_mask = (~pkt_mask) & (bucket2->next_valid << pkt2_index);\ + buckets_mask |= bucket_mask; \ + bucket_next = bucket2->next; \ + buckets[pkt2_index] = bucket_next; \ + keys[pkt2_index] = key; \ +} + +#define lookup_grinder(pkt_index, buckets, keys, pkts_mask_out, \ + entries, buckets_mask, f) \ +{ \ + struct rte_bucket_4_32 *bucket, *bucket_next; \ + void *a; \ + uint64_t pkt_mask, bucket_mask; \ + uint64_t *key; \ + uint32_t pos; \ + \ + bucket = buckets[pkt_index]; \ + key = keys[pkt_index]; \ + \ + lookup_key32_cmp(key, bucket, pos, f); \ + \ + pkt_mask = (bucket->signature[pos] & 1LLU) << pkt_index;\ + pkts_mask_out |= pkt_mask; \ + \ + a = (void *) &bucket->data[pos * f->entry_size]; \ + rte_prefetch0(a); \ + entries[pkt_index] = a; \ + \ + bucket_mask = (~pkt_mask) & (bucket->next_valid << pkt_index);\ + buckets_mask |= bucket_mask; \ + bucket_next = bucket->next; \ + rte_prefetch0(bucket_next); \ + rte_prefetch0((void *)(((uintptr_t) bucket_next) + RTE_CACHE_LINE_SIZE));\ + rte_prefetch0((void *)(((uintptr_t) bucket_next) + \ + 2 * RTE_CACHE_LINE_SIZE)); \ + buckets[pkt_index] = bucket_next; \ + keys[pkt_index] = key; \ +} + +#define lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01,\ + pkts, pkts_mask, f) \ +{ \ + uint64_t pkt00_mask, pkt01_mask; \ + uint32_t key_offset = f->key_offset; \ + \ + pkt00_index = __builtin_ctzll(pkts_mask); \ + pkt00_mask = 1LLU << pkt00_index; \ + pkts_mask &= ~pkt00_mask; \ + \ + mbuf00 = pkts[pkt00_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf00, key_offset));\ + \ + pkt01_index = __builtin_ctzll(pkts_mask); \ + pkt01_mask = 1LLU << pkt01_index; \ + pkts_mask &= ~pkt01_mask; \ + \ + mbuf01 = pkts[pkt01_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf01, key_offset));\ +} + +#define lookup2_stage0_with_odd_support(pkt00_index, pkt01_index,\ + mbuf00, mbuf01, pkts, pkts_mask, f) \ +{ \ + uint64_t pkt00_mask, pkt01_mask; \ + uint32_t key_offset = f->key_offset; \ + \ + pkt00_index = __builtin_ctzll(pkts_mask); \ + pkt00_mask = 1LLU << pkt00_index; \ + pkts_mask &= ~pkt00_mask; \ + \ + mbuf00 = pkts[pkt00_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf00, key_offset)); \ + \ + pkt01_index = __builtin_ctzll(pkts_mask); \ + if (pkts_mask == 0) \ + pkt01_index = pkt00_index; \ + \ + pkt01_mask = 1LLU << pkt01_index; \ + pkts_mask &= ~pkt01_mask; \ + \ + mbuf01 = pkts[pkt01_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf01, key_offset)); \ +} + +#define lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f) \ +{ \ + uint64_t *key10, *key11; \ + uint64_t signature10, signature11; \ + uint32_t bucket10_index, bucket11_index; \ + \ + key10 = RTE_MBUF_METADATA_UINT64_PTR(mbuf10, f->key_offset); \ + signature10 = f->f_hash(key10, f->key_mask, KEY_SIZE, f->seed); \ + \ + bucket10_index = signature10 & (f->n_buckets - 1); \ + bucket10 = (struct rte_bucket_4_32 *) \ + &f->memory[bucket10_index * f->bucket_size]; \ + rte_prefetch0(bucket10); \ + rte_prefetch0((void *)(((uintptr_t) bucket10) + RTE_CACHE_LINE_SIZE));\ + rte_prefetch0((void *)(((uintptr_t) bucket10) + 2 * RTE_CACHE_LINE_SIZE));\ + \ + key11 = RTE_MBUF_METADATA_UINT64_PTR(mbuf11, f->key_offset); \ + signature11 = f->f_hash(key11, f->key_mask, KEY_SIZE, f->seed);\ + \ + bucket11_index = signature11 & (f->n_buckets - 1); \ + bucket11 = (struct rte_bucket_4_32 *) \ + &f->memory[bucket11_index * f->bucket_size]; \ + rte_prefetch0(bucket11); \ + rte_prefetch0((void *)(((uintptr_t) bucket11) + RTE_CACHE_LINE_SIZE));\ + rte_prefetch0((void *)(((uintptr_t) bucket11) + 2 * RTE_CACHE_LINE_SIZE));\ +} + +#define lookup2_stage2_lru(pkt20_index, pkt21_index, mbuf20, mbuf21,\ + bucket20, bucket21, pkts_mask_out, entries, f) \ +{ \ + void *a20, *a21; \ + uint64_t pkt20_mask, pkt21_mask; \ + uint64_t *key20, *key21; \ + uint32_t pos20, pos21; \ + \ + key20 = RTE_MBUF_METADATA_UINT64_PTR(mbuf20, f->key_offset);\ + key21 = RTE_MBUF_METADATA_UINT64_PTR(mbuf21, f->key_offset);\ + \ + lookup_key32_cmp(key20, bucket20, pos20, f); \ + lookup_key32_cmp(key21, bucket21, pos21, f); \ + \ + pkt20_mask = (bucket20->signature[pos20] & 1LLU) << pkt20_index;\ + pkt21_mask = (bucket21->signature[pos21] & 1LLU) << pkt21_index;\ + pkts_mask_out |= pkt20_mask | pkt21_mask; \ + \ + a20 = (void *) &bucket20->data[pos20 * f->entry_size]; \ + a21 = (void *) &bucket21->data[pos21 * f->entry_size]; \ + rte_prefetch0(a20); \ + rte_prefetch0(a21); \ + entries[pkt20_index] = a20; \ + entries[pkt21_index] = a21; \ + lru_update(bucket20, pos20); \ + lru_update(bucket21, pos21); \ +} + +#define lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, bucket20, \ + bucket21, pkts_mask_out, entries, buckets_mask, buckets, keys, f)\ +{ \ + struct rte_bucket_4_32 *bucket20_next, *bucket21_next; \ + void *a20, *a21; \ + uint64_t pkt20_mask, pkt21_mask, bucket20_mask, bucket21_mask;\ + uint64_t *key20, *key21; \ + uint32_t pos20, pos21; \ + \ + key20 = RTE_MBUF_METADATA_UINT64_PTR(mbuf20, f->key_offset);\ + key21 = RTE_MBUF_METADATA_UINT64_PTR(mbuf21, f->key_offset);\ + \ + lookup_key32_cmp(key20, bucket20, pos20, f); \ + lookup_key32_cmp(key21, bucket21, pos21, f); \ + \ + pkt20_mask = (bucket20->signature[pos20] & 1LLU) << pkt20_index;\ + pkt21_mask = (bucket21->signature[pos21] & 1LLU) << pkt21_index;\ + pkts_mask_out |= pkt20_mask | pkt21_mask; \ + \ + a20 = (void *) &bucket20->data[pos20 * f->entry_size]; \ + a21 = (void *) &bucket21->data[pos21 * f->entry_size]; \ + rte_prefetch0(a20); \ + rte_prefetch0(a21); \ + entries[pkt20_index] = a20; \ + entries[pkt21_index] = a21; \ + \ + bucket20_mask = (~pkt20_mask) & (bucket20->next_valid << pkt20_index);\ + bucket21_mask = (~pkt21_mask) & (bucket21->next_valid << pkt21_index);\ + buckets_mask |= bucket20_mask | bucket21_mask; \ + bucket20_next = bucket20->next; \ + bucket21_next = bucket21->next; \ + buckets[pkt20_index] = bucket20_next; \ + buckets[pkt21_index] = bucket21_next; \ + keys[pkt20_index] = key20; \ + keys[pkt21_index] = key21; \ +} + +static int +rte_table_hash_lookup_key32_lru( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *f = (struct rte_table_hash *) table; + struct rte_bucket_4_32 *bucket10, *bucket11, *bucket20, *bucket21; + struct rte_mbuf *mbuf00, *mbuf01, *mbuf10, *mbuf11, *mbuf20, *mbuf21; + uint32_t pkt00_index, pkt01_index, pkt10_index; + uint32_t pkt11_index, pkt20_index, pkt21_index; + uint64_t pkts_mask_out = 0; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_HASH_KEY32_STATS_PKTS_IN_ADD(f, n_pkts_in); + + /* Cannot run the pipeline with less than 5 packets */ + if (__builtin_popcountll(pkts_mask) < 5) { + for ( ; pkts_mask; ) { + struct rte_bucket_4_32 *bucket; + struct rte_mbuf *mbuf; + uint32_t pkt_index; + + lookup1_stage0(pkt_index, mbuf, pkts, pkts_mask, f); + lookup1_stage1(mbuf, bucket, f); + lookup1_stage2_lru(pkt_index, mbuf, bucket, + pkts_mask_out, entries, f); + } + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_KEY32_STATS_PKTS_LOOKUP_MISS(f, n_pkts_in - __builtin_popcountll(pkts_mask_out)); + return 0; + } + + /* + * Pipeline fill + * + */ + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline feed */ + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* + * Pipeline run + * + */ + for ( ; pkts_mask; ) { + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0_with_odd_support(pkt00_index, pkt01_index, + mbuf00, mbuf01, pkts, pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_lru(pkt20_index, pkt21_index, + mbuf20, mbuf21, bucket20, bucket21, pkts_mask_out, + entries, f); + } + + /* + * Pipeline flush + * + */ + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_lru(pkt20_index, pkt21_index, + mbuf20, mbuf21, bucket20, bucket21, pkts_mask_out, entries, f); + + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + + /* Pipeline stage 2 */ + lookup2_stage2_lru(pkt20_index, pkt21_index, + mbuf20, mbuf21, bucket20, bucket21, pkts_mask_out, entries, f); + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_KEY32_STATS_PKTS_LOOKUP_MISS(f, n_pkts_in - __builtin_popcountll(pkts_mask_out)); + return 0; +} /* rte_table_hash_lookup_key32_lru() */ + +static int +rte_table_hash_lookup_key32_ext( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *f = (struct rte_table_hash *) table; + struct rte_bucket_4_32 *bucket10, *bucket11, *bucket20, *bucket21; + struct rte_mbuf *mbuf00, *mbuf01, *mbuf10, *mbuf11, *mbuf20, *mbuf21; + uint32_t pkt00_index, pkt01_index, pkt10_index; + uint32_t pkt11_index, pkt20_index, pkt21_index; + uint64_t pkts_mask_out = 0, buckets_mask = 0; + struct rte_bucket_4_32 *buckets[RTE_PORT_IN_BURST_SIZE_MAX]; + uint64_t *keys[RTE_PORT_IN_BURST_SIZE_MAX]; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_HASH_KEY32_STATS_PKTS_IN_ADD(f, n_pkts_in); + + /* Cannot run the pipeline with less than 5 packets */ + if (__builtin_popcountll(pkts_mask) < 5) { + for ( ; pkts_mask; ) { + struct rte_bucket_4_32 *bucket; + struct rte_mbuf *mbuf; + uint32_t pkt_index; + + lookup1_stage0(pkt_index, mbuf, pkts, pkts_mask, f); + lookup1_stage1(mbuf, bucket, f); + lookup1_stage2_ext(pkt_index, mbuf, bucket, + pkts_mask_out, entries, buckets_mask, buckets, + keys, f); + } + + goto grind_next_buckets; + } + + /* + * Pipeline fill + * + */ + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline feed */ + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* + * Pipeline run + * + */ + for ( ; pkts_mask; ) { + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0_with_odd_support(pkt00_index, pkt01_index, + mbuf00, mbuf01, pkts, pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, + buckets_mask, buckets, keys, f); + } + + /* + * Pipeline flush + * + */ + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, + buckets_mask, buckets, keys, f); + + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + + /* Pipeline stage 2 */ + lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, + buckets_mask, buckets, keys, f); + +grind_next_buckets: + /* Grind next buckets */ + for ( ; buckets_mask; ) { + uint64_t buckets_mask_next = 0; + + for ( ; buckets_mask; ) { + uint64_t pkt_mask; + uint32_t pkt_index; + + pkt_index = __builtin_ctzll(buckets_mask); + pkt_mask = 1LLU << pkt_index; + buckets_mask &= ~pkt_mask; + + lookup_grinder(pkt_index, buckets, keys, pkts_mask_out, + entries, buckets_mask_next, f); + } + + buckets_mask = buckets_mask_next; + } + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_KEY32_STATS_PKTS_LOOKUP_MISS(f, n_pkts_in - __builtin_popcountll(pkts_mask_out)); + return 0; +} /* rte_table_hash_lookup_key32_ext() */ + +static int +rte_table_hash_key32_stats_read(void *table, struct rte_table_stats *stats, int clear) +{ + struct rte_table_hash *t = table; + + if (stats != NULL) + memcpy(stats, &t->stats, sizeof(t->stats)); + + if (clear) + memset(&t->stats, 0, sizeof(t->stats)); + + return 0; +} + +struct rte_table_ops rte_table_hash_key32_lru_ops = { + .f_create = rte_table_hash_create_key32_lru, + .f_free = rte_table_hash_free_key32_lru, + .f_add = rte_table_hash_entry_add_key32_lru, + .f_delete = rte_table_hash_entry_delete_key32_lru, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_hash_lookup_key32_lru, + .f_stats = rte_table_hash_key32_stats_read, +}; + +struct rte_table_ops rte_table_hash_key32_ext_ops = { + .f_create = rte_table_hash_create_key32_ext, + .f_free = rte_table_hash_free_key32_ext, + .f_add = rte_table_hash_entry_add_key32_ext, + .f_delete = rte_table_hash_entry_delete_key32_ext, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_hash_lookup_key32_ext, + .f_stats = rte_table_hash_key32_stats_read, +}; diff --git a/lib/table/rte_table_hash_key8.c b/lib/table/rte_table_hash_key8.c new file mode 100644 index 0000000000..34e3ed1af9 --- /dev/null +++ b/lib/table/rte_table_hash_key8.c @@ -0,0 +1,1154 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2017 Intel Corporation + */ +#include <string.h> +#include <stdio.h> + +#include <rte_common.h> +#include <rte_mbuf.h> +#include <rte_memory.h> +#include <rte_malloc.h> +#include <rte_log.h> + +#include "rte_table_hash.h" +#include "rte_lru.h" + +#define KEY_SIZE 8 + +#define KEYS_PER_BUCKET 4 + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_HASH_KEY8_STATS_PKTS_IN_ADD(table, val) \ + table->stats.n_pkts_in += val +#define RTE_TABLE_HASH_KEY8_STATS_PKTS_LOOKUP_MISS(table, val) \ + table->stats.n_pkts_lookup_miss += val + +#else + +#define RTE_TABLE_HASH_KEY8_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_HASH_KEY8_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + +#ifdef RTE_ARCH_64 +struct rte_bucket_4_8 { + /* Cache line 0 */ + uint64_t signature; + uint64_t lru_list; + struct rte_bucket_4_8 *next; + uint64_t next_valid; + + uint64_t key[4]; + + /* Cache line 1 */ + uint8_t data[0]; +}; +#else +struct rte_bucket_4_8 { + /* Cache line 0 */ + uint64_t signature; + uint64_t lru_list; + struct rte_bucket_4_8 *next; + uint32_t pad; + uint64_t next_valid; + + uint64_t key[4]; + + /* Cache line 1 */ + uint8_t data[0]; +}; +#endif + +struct rte_table_hash { + struct rte_table_stats stats; + + /* Input parameters */ + uint32_t n_buckets; + uint32_t key_size; + uint32_t entry_size; + uint32_t bucket_size; + uint32_t key_offset; + uint64_t key_mask; + rte_table_hash_op_hash f_hash; + uint64_t seed; + + /* Extendible buckets */ + uint32_t n_buckets_ext; + uint32_t stack_pos; + uint32_t *stack; + + /* Lookup table */ + uint8_t memory[0] __rte_cache_aligned; +}; + +static int +keycmp(void *a, void *b, void *b_mask) +{ + uint64_t *a64 = a, *b64 = b, *b_mask64 = b_mask; + + return a64[0] != (b64[0] & b_mask64[0]); +} + +static void +keycpy(void *dst, void *src, void *src_mask) +{ + uint64_t *dst64 = dst, *src64 = src, *src_mask64 = src_mask; + + dst64[0] = src64[0] & src_mask64[0]; +} + +static int +check_params_create(struct rte_table_hash_params *params) +{ + /* name */ + if (params->name == NULL) { + RTE_LOG(ERR, TABLE, "%s: name invalid value\n", __func__); + return -EINVAL; + } + + /* key_size */ + if (params->key_size != KEY_SIZE) { + RTE_LOG(ERR, TABLE, "%s: key_size invalid value\n", __func__); + return -EINVAL; + } + + /* n_keys */ + if (params->n_keys == 0) { + RTE_LOG(ERR, TABLE, "%s: n_keys is zero\n", __func__); + return -EINVAL; + } + + /* n_buckets */ + if ((params->n_buckets == 0) || + (!rte_is_power_of_2(params->n_buckets))) { + RTE_LOG(ERR, TABLE, "%s: n_buckets invalid value\n", __func__); + return -EINVAL; + } + + /* f_hash */ + if (params->f_hash == NULL) { + RTE_LOG(ERR, TABLE, "%s: f_hash function pointer is NULL\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static void * +rte_table_hash_create_key8_lru(void *params, int socket_id, uint32_t entry_size) +{ + struct rte_table_hash_params *p = params; + struct rte_table_hash *f; + uint64_t bucket_size, total_size; + uint32_t n_buckets, i; + + /* Check input parameters */ + if ((check_params_create(p) != 0) || + ((sizeof(struct rte_table_hash) % RTE_CACHE_LINE_SIZE) != 0) || + ((sizeof(struct rte_bucket_4_8) % 64) != 0)) + return NULL; + + /* + * Table dimensioning + * + * Objective: Pick the number of buckets (n_buckets) so that there a chance + * to store n_keys keys in the table. + * + * Note: Since the buckets do not get extended, it is not possible to + * guarantee that n_keys keys can be stored in the table at any time. In the + * worst case scenario when all the n_keys fall into the same bucket, only + * a maximum of KEYS_PER_BUCKET keys will be stored in the table. This case + * defeats the purpose of the hash table. It indicates unsuitable f_hash or + * n_keys to n_buckets ratio. + * + * MIN(n_buckets) = (n_keys + KEYS_PER_BUCKET - 1) / KEYS_PER_BUCKET + */ + n_buckets = rte_align32pow2( + (p->n_keys + KEYS_PER_BUCKET - 1) / KEYS_PER_BUCKET); + n_buckets = RTE_MAX(n_buckets, p->n_buckets); + + /* Memory allocation */ + bucket_size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct rte_bucket_4_8) + + KEYS_PER_BUCKET * entry_size); + total_size = sizeof(struct rte_table_hash) + n_buckets * bucket_size; + + if (total_size > SIZE_MAX) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes" + " for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + + f = rte_zmalloc_socket(p->name, + (size_t)total_size, + RTE_CACHE_LINE_SIZE, + socket_id); + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes" + " for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + + RTE_LOG(INFO, TABLE, "%s: Hash table %s memory footprint " + "is %" PRIu64 " bytes\n", + __func__, p->name, total_size); + + /* Memory initialization */ + f->n_buckets = n_buckets; + f->key_size = KEY_SIZE; + f->entry_size = entry_size; + f->bucket_size = bucket_size; + f->key_offset = p->key_offset; + f->f_hash = p->f_hash; + f->seed = p->seed; + + if (p->key_mask != NULL) + f->key_mask = ((uint64_t *)p->key_mask)[0]; + else + f->key_mask = 0xFFFFFFFFFFFFFFFFLLU; + + for (i = 0; i < n_buckets; i++) { + struct rte_bucket_4_8 *bucket; + + bucket = (struct rte_bucket_4_8 *) &f->memory[i * + f->bucket_size]; + bucket->lru_list = 0x0000000100020003LLU; + } + + return f; +} + +static int +rte_table_hash_free_key8_lru(void *table) +{ + struct rte_table_hash *f = table; + + /* Check input parameters */ + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + + rte_free(f); + return 0; +} + +static int +rte_table_hash_entry_add_key8_lru( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_8 *bucket; + uint64_t signature, mask, pos; + uint32_t bucket_index, i; + + signature = f->f_hash(key, &f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket = (struct rte_bucket_4_8 *) + &f->memory[bucket_index * f->bucket_size]; + + /* Key is present in the bucket */ + for (i = 0, mask = 1LLU; i < 4; i++, mask <<= 1) { + uint64_t bucket_signature = bucket->signature; + uint64_t *bucket_key = &bucket->key[i]; + + if ((bucket_signature & mask) && + (keycmp(bucket_key, key, &f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * f->entry_size]; + + memcpy(bucket_data, entry, f->entry_size); + lru_update(bucket, i); + *key_found = 1; + *entry_ptr = (void *) bucket_data; + return 0; + } + } + + /* Key is not present in the bucket */ + for (i = 0, mask = 1LLU; i < 4; i++, mask <<= 1) { + uint64_t bucket_signature = bucket->signature; + + if ((bucket_signature & mask) == 0) { + uint8_t *bucket_data = &bucket->data[i * f->entry_size]; + + bucket->signature |= mask; + keycpy(&bucket->key[i], key, &f->key_mask); + memcpy(bucket_data, entry, f->entry_size); + lru_update(bucket, i); + *key_found = 0; + *entry_ptr = (void *) bucket_data; + + return 0; + } + } + + /* Bucket full: replace LRU entry */ + pos = lru_pos(bucket); + keycpy(&bucket->key[pos], key, &f->key_mask); + memcpy(&bucket->data[pos * f->entry_size], entry, f->entry_size); + lru_update(bucket, pos); + *key_found = 0; + *entry_ptr = (void *) &bucket->data[pos * f->entry_size]; + + return 0; +} + +static int +rte_table_hash_entry_delete_key8_lru( + void *table, + void *key, + int *key_found, + void *entry) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_8 *bucket; + uint64_t signature, mask; + uint32_t bucket_index, i; + + signature = f->f_hash(key, &f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket = (struct rte_bucket_4_8 *) + &f->memory[bucket_index * f->bucket_size]; + + /* Key is present in the bucket */ + for (i = 0, mask = 1LLU; i < 4; i++, mask <<= 1) { + uint64_t bucket_signature = bucket->signature; + uint64_t *bucket_key = &bucket->key[i]; + + if ((bucket_signature & mask) && + (keycmp(bucket_key, key, &f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * f->entry_size]; + + bucket->signature &= ~mask; + *key_found = 1; + if (entry) + memcpy(entry, bucket_data, f->entry_size); + + return 0; + } + } + + /* Key is not present in the bucket */ + *key_found = 0; + return 0; +} + +static void * +rte_table_hash_create_key8_ext(void *params, int socket_id, uint32_t entry_size) +{ + struct rte_table_hash_params *p = params; + struct rte_table_hash *f; + uint64_t bucket_size, stack_size, total_size; + uint32_t n_buckets_ext, i; + + /* Check input parameters */ + if ((check_params_create(p) != 0) || + ((sizeof(struct rte_table_hash) % RTE_CACHE_LINE_SIZE) != 0) || + ((sizeof(struct rte_bucket_4_8) % 64) != 0)) + return NULL; + + /* + * Table dimensioning + * + * Objective: Pick the number of bucket extensions (n_buckets_ext) so that + * it is guaranteed that n_keys keys can be stored in the table at any time. + * + * The worst case scenario takes place when all the n_keys keys fall into + * the same bucket. Actually, due to the KEYS_PER_BUCKET scheme, the worst + * case takes place when (n_keys - KEYS_PER_BUCKET + 1) keys fall into the + * same bucket, while the remaining (KEYS_PER_BUCKET - 1) keys each fall + * into a different bucket. This case defeats the purpose of the hash table. + * It indicates unsuitable f_hash or n_keys to n_buckets ratio. + * + * n_buckets_ext = n_keys / KEYS_PER_BUCKET + KEYS_PER_BUCKET - 1 + */ + n_buckets_ext = p->n_keys / KEYS_PER_BUCKET + KEYS_PER_BUCKET - 1; + + /* Memory allocation */ + bucket_size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct rte_bucket_4_8) + + KEYS_PER_BUCKET * entry_size); + stack_size = RTE_CACHE_LINE_ROUNDUP(n_buckets_ext * sizeof(uint32_t)); + total_size = sizeof(struct rte_table_hash) + + (p->n_buckets + n_buckets_ext) * bucket_size + stack_size; + + if (total_size > SIZE_MAX) { + RTE_LOG(ERR, TABLE, "%s: Cannot allocate %" PRIu64 " bytes " + "for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + + f = rte_zmalloc_socket(p->name, + (size_t)total_size, + RTE_CACHE_LINE_SIZE, + socket_id); + if (f == NULL) { + RTE_LOG(ERR, TABLE, + "%s: Cannot allocate %" PRIu64 " bytes " + "for hash table %s\n", + __func__, total_size, p->name); + return NULL; + } + RTE_LOG(INFO, TABLE, "%s: Hash table %s memory footprint " + "is %" PRIu64 " bytes\n", + __func__, p->name, total_size); + + /* Memory initialization */ + f->n_buckets = p->n_buckets; + f->key_size = KEY_SIZE; + f->entry_size = entry_size; + f->bucket_size = bucket_size; + f->key_offset = p->key_offset; + f->f_hash = p->f_hash; + f->seed = p->seed; + + f->n_buckets_ext = n_buckets_ext; + f->stack_pos = n_buckets_ext; + f->stack = (uint32_t *) + &f->memory[(p->n_buckets + n_buckets_ext) * f->bucket_size]; + + if (p->key_mask != NULL) + f->key_mask = ((uint64_t *)p->key_mask)[0]; + else + f->key_mask = 0xFFFFFFFFFFFFFFFFLLU; + + for (i = 0; i < n_buckets_ext; i++) + f->stack[i] = i; + + return f; +} + +static int +rte_table_hash_free_key8_ext(void *table) +{ + struct rte_table_hash *f = table; + + /* Check input parameters */ + if (f == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + + rte_free(f); + return 0; +} + +static int +rte_table_hash_entry_add_key8_ext( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_8 *bucket0, *bucket, *bucket_prev; + uint64_t signature; + uint32_t bucket_index, i; + + signature = f->f_hash(key, &f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket0 = (struct rte_bucket_4_8 *) + &f->memory[bucket_index * f->bucket_size]; + + /* Key is present in the bucket */ + for (bucket = bucket0; bucket != NULL; bucket = bucket->next) { + uint64_t mask; + + for (i = 0, mask = 1LLU; i < 4; i++, mask <<= 1) { + uint64_t bucket_signature = bucket->signature; + uint64_t *bucket_key = &bucket->key[i]; + + if ((bucket_signature & mask) && + (keycmp(bucket_key, key, &f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * + f->entry_size]; + + memcpy(bucket_data, entry, f->entry_size); + *key_found = 1; + *entry_ptr = (void *) bucket_data; + return 0; + } + } + } + + /* Key is not present in the bucket */ + for (bucket_prev = NULL, bucket = bucket0; + bucket != NULL; bucket_prev = bucket, bucket = bucket->next) { + uint64_t mask; + + for (i = 0, mask = 1LLU; i < 4; i++, mask <<= 1) { + uint64_t bucket_signature = bucket->signature; + + if ((bucket_signature & mask) == 0) { + uint8_t *bucket_data = &bucket->data[i * + f->entry_size]; + + bucket->signature |= mask; + keycpy(&bucket->key[i], key, &f->key_mask); + memcpy(bucket_data, entry, f->entry_size); + *key_found = 0; + *entry_ptr = (void *) bucket_data; + + return 0; + } + } + } + + /* Bucket full: extend bucket */ + if (f->stack_pos > 0) { + bucket_index = f->stack[--f->stack_pos]; + + bucket = (struct rte_bucket_4_8 *) &f->memory[(f->n_buckets + + bucket_index) * f->bucket_size]; + bucket_prev->next = bucket; + bucket_prev->next_valid = 1; + + bucket->signature = 1; + keycpy(&bucket->key[0], key, &f->key_mask); + memcpy(&bucket->data[0], entry, f->entry_size); + *key_found = 0; + *entry_ptr = (void *) &bucket->data[0]; + return 0; + } + + return -ENOSPC; +} + +static int +rte_table_hash_entry_delete_key8_ext( + void *table, + void *key, + int *key_found, + void *entry) +{ + struct rte_table_hash *f = table; + struct rte_bucket_4_8 *bucket0, *bucket, *bucket_prev; + uint64_t signature; + uint32_t bucket_index, i; + + signature = f->f_hash(key, &f->key_mask, f->key_size, f->seed); + bucket_index = signature & (f->n_buckets - 1); + bucket0 = (struct rte_bucket_4_8 *) + &f->memory[bucket_index * f->bucket_size]; + + /* Key is present in the bucket */ + for (bucket_prev = NULL, bucket = bucket0; bucket != NULL; + bucket_prev = bucket, bucket = bucket->next) { + uint64_t mask; + + for (i = 0, mask = 1LLU; i < 4; i++, mask <<= 1) { + uint64_t bucket_signature = bucket->signature; + uint64_t *bucket_key = &bucket->key[i]; + + if ((bucket_signature & mask) && + (keycmp(bucket_key, key, &f->key_mask) == 0)) { + uint8_t *bucket_data = &bucket->data[i * + f->entry_size]; + + bucket->signature &= ~mask; + *key_found = 1; + if (entry) + memcpy(entry, bucket_data, + f->entry_size); + + if ((bucket->signature == 0) && + (bucket_prev != NULL)) { + bucket_prev->next = bucket->next; + bucket_prev->next_valid = + bucket->next_valid; + + memset(bucket, 0, + sizeof(struct rte_bucket_4_8)); + bucket_index = (((uint8_t *)bucket - + (uint8_t *)f->memory)/f->bucket_size) - f->n_buckets; + f->stack[f->stack_pos++] = bucket_index; + } + + return 0; + } + } + } + + /* Key is not present in the bucket */ + *key_found = 0; + return 0; +} + +#define lookup_key8_cmp(key_in, bucket, pos, f) \ +{ \ + uint64_t xor[4], signature, k; \ + \ + signature = ~bucket->signature; \ + \ + k = key_in[0] & f->key_mask; \ + xor[0] = (k ^ bucket->key[0]) | (signature & 1); \ + xor[1] = (k ^ bucket->key[1]) | (signature & 2); \ + xor[2] = (k ^ bucket->key[2]) | (signature & 4); \ + xor[3] = (k ^ bucket->key[3]) | (signature & 8); \ + \ + pos = 4; \ + if (xor[0] == 0) \ + pos = 0; \ + if (xor[1] == 0) \ + pos = 1; \ + if (xor[2] == 0) \ + pos = 2; \ + if (xor[3] == 0) \ + pos = 3; \ +} + +#define lookup1_stage0(pkt0_index, mbuf0, pkts, pkts_mask, f) \ +{ \ + uint64_t pkt_mask; \ + uint32_t key_offset = f->key_offset;\ + \ + pkt0_index = __builtin_ctzll(pkts_mask); \ + pkt_mask = 1LLU << pkt0_index; \ + pkts_mask &= ~pkt_mask; \ + \ + mbuf0 = pkts[pkt0_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf0, key_offset)); \ +} + +#define lookup1_stage1(mbuf1, bucket1, f) \ +{ \ + uint64_t *key; \ + uint64_t signature; \ + uint32_t bucket_index; \ + \ + key = RTE_MBUF_METADATA_UINT64_PTR(mbuf1, f->key_offset);\ + signature = f->f_hash(key, &f->key_mask, KEY_SIZE, f->seed); \ + bucket_index = signature & (f->n_buckets - 1); \ + bucket1 = (struct rte_bucket_4_8 *) \ + &f->memory[bucket_index * f->bucket_size]; \ + rte_prefetch0(bucket1); \ +} + +#define lookup1_stage2_lru(pkt2_index, mbuf2, bucket2, \ + pkts_mask_out, entries, f) \ +{ \ + void *a; \ + uint64_t pkt_mask; \ + uint64_t *key; \ + uint32_t pos; \ + \ + key = RTE_MBUF_METADATA_UINT64_PTR(mbuf2, f->key_offset);\ + lookup_key8_cmp(key, bucket2, pos, f); \ + \ + pkt_mask = ((bucket2->signature >> pos) & 1LLU) << pkt2_index;\ + pkts_mask_out |= pkt_mask; \ + \ + a = (void *) &bucket2->data[pos * f->entry_size]; \ + rte_prefetch0(a); \ + entries[pkt2_index] = a; \ + lru_update(bucket2, pos); \ +} + +#define lookup1_stage2_ext(pkt2_index, mbuf2, bucket2, pkts_mask_out,\ + entries, buckets_mask, buckets, keys, f) \ +{ \ + struct rte_bucket_4_8 *bucket_next; \ + void *a; \ + uint64_t pkt_mask, bucket_mask; \ + uint64_t *key; \ + uint32_t pos; \ + \ + key = RTE_MBUF_METADATA_UINT64_PTR(mbuf2, f->key_offset);\ + lookup_key8_cmp(key, bucket2, pos, f); \ + \ + pkt_mask = ((bucket2->signature >> pos) & 1LLU) << pkt2_index;\ + pkts_mask_out |= pkt_mask; \ + \ + a = (void *) &bucket2->data[pos * f->entry_size]; \ + rte_prefetch0(a); \ + entries[pkt2_index] = a; \ + \ + bucket_mask = (~pkt_mask) & (bucket2->next_valid << pkt2_index);\ + buckets_mask |= bucket_mask; \ + bucket_next = bucket2->next; \ + buckets[pkt2_index] = bucket_next; \ + keys[pkt2_index] = key; \ +} + +#define lookup_grinder(pkt_index, buckets, keys, pkts_mask_out, entries,\ + buckets_mask, f) \ +{ \ + struct rte_bucket_4_8 *bucket, *bucket_next; \ + void *a; \ + uint64_t pkt_mask, bucket_mask; \ + uint64_t *key; \ + uint32_t pos; \ + \ + bucket = buckets[pkt_index]; \ + key = keys[pkt_index]; \ + lookup_key8_cmp(key, bucket, pos, f); \ + \ + pkt_mask = ((bucket->signature >> pos) & 1LLU) << pkt_index;\ + pkts_mask_out |= pkt_mask; \ + \ + a = (void *) &bucket->data[pos * f->entry_size]; \ + rte_prefetch0(a); \ + entries[pkt_index] = a; \ + \ + bucket_mask = (~pkt_mask) & (bucket->next_valid << pkt_index);\ + buckets_mask |= bucket_mask; \ + bucket_next = bucket->next; \ + rte_prefetch0(bucket_next); \ + buckets[pkt_index] = bucket_next; \ + keys[pkt_index] = key; \ +} + +#define lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01,\ + pkts, pkts_mask, f) \ +{ \ + uint64_t pkt00_mask, pkt01_mask; \ + uint32_t key_offset = f->key_offset; \ + \ + pkt00_index = __builtin_ctzll(pkts_mask); \ + pkt00_mask = 1LLU << pkt00_index; \ + pkts_mask &= ~pkt00_mask; \ + \ + mbuf00 = pkts[pkt00_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf00, key_offset));\ + \ + pkt01_index = __builtin_ctzll(pkts_mask); \ + pkt01_mask = 1LLU << pkt01_index; \ + pkts_mask &= ~pkt01_mask; \ + \ + mbuf01 = pkts[pkt01_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf01, key_offset));\ +} + +#define lookup2_stage0_with_odd_support(pkt00_index, pkt01_index,\ + mbuf00, mbuf01, pkts, pkts_mask, f) \ +{ \ + uint64_t pkt00_mask, pkt01_mask; \ + uint32_t key_offset = f->key_offset; \ + \ + pkt00_index = __builtin_ctzll(pkts_mask); \ + pkt00_mask = 1LLU << pkt00_index; \ + pkts_mask &= ~pkt00_mask; \ + \ + mbuf00 = pkts[pkt00_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf00, key_offset));\ + \ + pkt01_index = __builtin_ctzll(pkts_mask); \ + if (pkts_mask == 0) \ + pkt01_index = pkt00_index; \ + \ + pkt01_mask = 1LLU << pkt01_index; \ + pkts_mask &= ~pkt01_mask; \ + \ + mbuf01 = pkts[pkt01_index]; \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf01, key_offset));\ +} + +#define lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f)\ +{ \ + uint64_t *key10, *key11; \ + uint64_t signature10, signature11; \ + uint32_t bucket10_index, bucket11_index; \ + rte_table_hash_op_hash f_hash = f->f_hash; \ + uint64_t seed = f->seed; \ + uint32_t key_offset = f->key_offset; \ + \ + key10 = RTE_MBUF_METADATA_UINT64_PTR(mbuf10, key_offset);\ + key11 = RTE_MBUF_METADATA_UINT64_PTR(mbuf11, key_offset);\ + \ + signature10 = f_hash(key10, &f->key_mask, KEY_SIZE, seed); \ + bucket10_index = signature10 & (f->n_buckets - 1); \ + bucket10 = (struct rte_bucket_4_8 *) \ + &f->memory[bucket10_index * f->bucket_size]; \ + rte_prefetch0(bucket10); \ + \ + signature11 = f_hash(key11, &f->key_mask, KEY_SIZE, seed); \ + bucket11_index = signature11 & (f->n_buckets - 1); \ + bucket11 = (struct rte_bucket_4_8 *) \ + &f->memory[bucket11_index * f->bucket_size]; \ + rte_prefetch0(bucket11); \ +} + +#define lookup2_stage2_lru(pkt20_index, pkt21_index, mbuf20, mbuf21,\ + bucket20, bucket21, pkts_mask_out, entries, f) \ +{ \ + void *a20, *a21; \ + uint64_t pkt20_mask, pkt21_mask; \ + uint64_t *key20, *key21; \ + uint32_t pos20, pos21; \ + \ + key20 = RTE_MBUF_METADATA_UINT64_PTR(mbuf20, f->key_offset);\ + key21 = RTE_MBUF_METADATA_UINT64_PTR(mbuf21, f->key_offset);\ + \ + lookup_key8_cmp(key20, bucket20, pos20, f); \ + lookup_key8_cmp(key21, bucket21, pos21, f); \ + \ + pkt20_mask = ((bucket20->signature >> pos20) & 1LLU) << pkt20_index;\ + pkt21_mask = ((bucket21->signature >> pos21) & 1LLU) << pkt21_index;\ + pkts_mask_out |= pkt20_mask | pkt21_mask; \ + \ + a20 = (void *) &bucket20->data[pos20 * f->entry_size]; \ + a21 = (void *) &bucket21->data[pos21 * f->entry_size]; \ + rte_prefetch0(a20); \ + rte_prefetch0(a21); \ + entries[pkt20_index] = a20; \ + entries[pkt21_index] = a21; \ + lru_update(bucket20, pos20); \ + lru_update(bucket21, pos21); \ +} + +#define lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, bucket20, \ + bucket21, pkts_mask_out, entries, buckets_mask, buckets, keys, f)\ +{ \ + struct rte_bucket_4_8 *bucket20_next, *bucket21_next; \ + void *a20, *a21; \ + uint64_t pkt20_mask, pkt21_mask, bucket20_mask, bucket21_mask;\ + uint64_t *key20, *key21; \ + uint32_t pos20, pos21; \ + \ + key20 = RTE_MBUF_METADATA_UINT64_PTR(mbuf20, f->key_offset);\ + key21 = RTE_MBUF_METADATA_UINT64_PTR(mbuf21, f->key_offset);\ + \ + lookup_key8_cmp(key20, bucket20, pos20, f); \ + lookup_key8_cmp(key21, bucket21, pos21, f); \ + \ + pkt20_mask = ((bucket20->signature >> pos20) & 1LLU) << pkt20_index;\ + pkt21_mask = ((bucket21->signature >> pos21) & 1LLU) << pkt21_index;\ + pkts_mask_out |= pkt20_mask | pkt21_mask; \ + \ + a20 = (void *) &bucket20->data[pos20 * f->entry_size]; \ + a21 = (void *) &bucket21->data[pos21 * f->entry_size]; \ + rte_prefetch0(a20); \ + rte_prefetch0(a21); \ + entries[pkt20_index] = a20; \ + entries[pkt21_index] = a21; \ + \ + bucket20_mask = (~pkt20_mask) & (bucket20->next_valid << pkt20_index);\ + bucket21_mask = (~pkt21_mask) & (bucket21->next_valid << pkt21_index);\ + buckets_mask |= bucket20_mask | bucket21_mask; \ + bucket20_next = bucket20->next; \ + bucket21_next = bucket21->next; \ + buckets[pkt20_index] = bucket20_next; \ + buckets[pkt21_index] = bucket21_next; \ + keys[pkt20_index] = key20; \ + keys[pkt21_index] = key21; \ +} + +static int +rte_table_hash_lookup_key8_lru( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *f = (struct rte_table_hash *) table; + struct rte_bucket_4_8 *bucket10, *bucket11, *bucket20, *bucket21; + struct rte_mbuf *mbuf00, *mbuf01, *mbuf10, *mbuf11, *mbuf20, *mbuf21; + uint32_t pkt00_index, pkt01_index, pkt10_index; + uint32_t pkt11_index, pkt20_index, pkt21_index; + uint64_t pkts_mask_out = 0; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_HASH_KEY8_STATS_PKTS_IN_ADD(f, n_pkts_in); + + /* Cannot run the pipeline with less than 5 packets */ + if (__builtin_popcountll(pkts_mask) < 5) { + for ( ; pkts_mask; ) { + struct rte_bucket_4_8 *bucket; + struct rte_mbuf *mbuf; + uint32_t pkt_index; + + lookup1_stage0(pkt_index, mbuf, pkts, pkts_mask, f); + lookup1_stage1(mbuf, bucket, f); + lookup1_stage2_lru(pkt_index, mbuf, bucket, + pkts_mask_out, entries, f); + } + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_KEY8_STATS_PKTS_LOOKUP_MISS(f, n_pkts_in - __builtin_popcountll(pkts_mask_out)); + return 0; + } + + /* + * Pipeline fill + * + */ + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline feed */ + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* + * Pipeline run + * + */ + for ( ; pkts_mask; ) { + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0_with_odd_support(pkt00_index, pkt01_index, + mbuf00, mbuf01, pkts, pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_lru(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, f); + } + + /* + * Pipeline flush + * + */ + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_lru(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, f); + + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + + /* Pipeline stage 2 */ + lookup2_stage2_lru(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, f); + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_KEY8_STATS_PKTS_LOOKUP_MISS(f, n_pkts_in - __builtin_popcountll(pkts_mask_out)); + return 0; +} /* lookup LRU */ + +static int +rte_table_hash_lookup_key8_ext( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *f = (struct rte_table_hash *) table; + struct rte_bucket_4_8 *bucket10, *bucket11, *bucket20, *bucket21; + struct rte_mbuf *mbuf00, *mbuf01, *mbuf10, *mbuf11, *mbuf20, *mbuf21; + uint32_t pkt00_index, pkt01_index, pkt10_index; + uint32_t pkt11_index, pkt20_index, pkt21_index; + uint64_t pkts_mask_out = 0, buckets_mask = 0; + struct rte_bucket_4_8 *buckets[RTE_PORT_IN_BURST_SIZE_MAX]; + uint64_t *keys[RTE_PORT_IN_BURST_SIZE_MAX]; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_HASH_KEY8_STATS_PKTS_IN_ADD(f, n_pkts_in); + + /* Cannot run the pipeline with less than 5 packets */ + if (__builtin_popcountll(pkts_mask) < 5) { + for ( ; pkts_mask; ) { + struct rte_bucket_4_8 *bucket; + struct rte_mbuf *mbuf; + uint32_t pkt_index; + + lookup1_stage0(pkt_index, mbuf, pkts, pkts_mask, f); + lookup1_stage1(mbuf, bucket, f); + lookup1_stage2_ext(pkt_index, mbuf, bucket, + pkts_mask_out, entries, buckets_mask, + buckets, keys, f); + } + + goto grind_next_buckets; + } + + /* + * Pipeline fill + * + */ + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline feed */ + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0(pkt00_index, pkt01_index, mbuf00, mbuf01, pkts, + pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* + * Pipeline run + * + */ + for ( ; pkts_mask; ) { + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0_with_odd_support(pkt00_index, pkt01_index, + mbuf00, mbuf01, pkts, pkts_mask, f); + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, + buckets_mask, buckets, keys, f); + } + + /* + * Pipeline flush + * + */ + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + mbuf10 = mbuf00; + mbuf11 = mbuf01; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 1 */ + lookup2_stage1(mbuf10, mbuf11, bucket10, bucket11, f); + + /* Pipeline stage 2 */ + lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, + buckets_mask, buckets, keys, f); + + /* Pipeline feed */ + bucket20 = bucket10; + bucket21 = bucket11; + mbuf20 = mbuf10; + mbuf21 = mbuf11; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + + /* Pipeline stage 2 */ + lookup2_stage2_ext(pkt20_index, pkt21_index, mbuf20, mbuf21, + bucket20, bucket21, pkts_mask_out, entries, + buckets_mask, buckets, keys, f); + +grind_next_buckets: + /* Grind next buckets */ + for ( ; buckets_mask; ) { + uint64_t buckets_mask_next = 0; + + for ( ; buckets_mask; ) { + uint64_t pkt_mask; + uint32_t pkt_index; + + pkt_index = __builtin_ctzll(buckets_mask); + pkt_mask = 1LLU << pkt_index; + buckets_mask &= ~pkt_mask; + + lookup_grinder(pkt_index, buckets, keys, pkts_mask_out, + entries, buckets_mask_next, f); + } + + buckets_mask = buckets_mask_next; + } + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_KEY8_STATS_PKTS_LOOKUP_MISS(f, n_pkts_in - __builtin_popcountll(pkts_mask_out)); + return 0; +} /* lookup EXT */ + +static int +rte_table_hash_key8_stats_read(void *table, struct rte_table_stats *stats, int clear) +{ + struct rte_table_hash *t = table; + + if (stats != NULL) + memcpy(stats, &t->stats, sizeof(t->stats)); + + if (clear) + memset(&t->stats, 0, sizeof(t->stats)); + + return 0; +} + +struct rte_table_ops rte_table_hash_key8_lru_ops = { + .f_create = rte_table_hash_create_key8_lru, + .f_free = rte_table_hash_free_key8_lru, + .f_add = rte_table_hash_entry_add_key8_lru, + .f_delete = rte_table_hash_entry_delete_key8_lru, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_hash_lookup_key8_lru, + .f_stats = rte_table_hash_key8_stats_read, +}; + +struct rte_table_ops rte_table_hash_key8_ext_ops = { + .f_create = rte_table_hash_create_key8_ext, + .f_free = rte_table_hash_free_key8_ext, + .f_add = rte_table_hash_entry_add_key8_ext, + .f_delete = rte_table_hash_entry_delete_key8_ext, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_hash_lookup_key8_ext, + .f_stats = rte_table_hash_key8_stats_read, +}; diff --git a/lib/table/rte_table_hash_lru.c b/lib/table/rte_table_hash_lru.c new file mode 100644 index 0000000000..5bcdb7ba02 --- /dev/null +++ b/lib/table/rte_table_hash_lru.c @@ -0,0 +1,959 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2017 Intel Corporation + */ + +#include <string.h> +#include <stdio.h> + +#include <rte_common.h> +#include <rte_mbuf.h> +#include <rte_memory.h> +#include <rte_malloc.h> +#include <rte_log.h> + +#include "rte_table_hash.h" +#include "rte_lru.h" + +#define KEYS_PER_BUCKET 4 + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_HASH_LRU_STATS_PKTS_IN_ADD(table, val) \ + table->stats.n_pkts_in += val +#define RTE_TABLE_HASH_LRU_STATS_PKTS_LOOKUP_MISS(table, val) \ + table->stats.n_pkts_lookup_miss += val + +#else + +#define RTE_TABLE_HASH_LRU_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_HASH_LRU_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + +struct bucket { + union { + struct bucket *next; + uint64_t lru_list; + }; + uint16_t sig[KEYS_PER_BUCKET]; + uint32_t key_pos[KEYS_PER_BUCKET]; +}; + +struct grinder { + struct bucket *bkt; + uint64_t sig; + uint64_t match; + uint64_t match_pos; + uint32_t key_index; +}; + +struct rte_table_hash { + struct rte_table_stats stats; + + /* Input parameters */ + uint32_t key_size; + uint32_t entry_size; + uint32_t n_keys; + uint32_t n_buckets; + rte_table_hash_op_hash f_hash; + uint64_t seed; + uint32_t key_offset; + + /* Internal */ + uint64_t bucket_mask; + uint32_t key_size_shl; + uint32_t data_size_shl; + uint32_t key_stack_tos; + + /* Grinder */ + struct grinder grinders[RTE_PORT_IN_BURST_SIZE_MAX]; + + /* Tables */ + uint64_t *key_mask; + struct bucket *buckets; + uint8_t *key_mem; + uint8_t *data_mem; + uint32_t *key_stack; + + /* Table memory */ + uint8_t memory[0] __rte_cache_aligned; +}; + +static int +keycmp(void *a, void *b, void *b_mask, uint32_t n_bytes) +{ + uint64_t *a64 = a, *b64 = b, *b_mask64 = b_mask; + uint32_t i; + + for (i = 0; i < n_bytes / sizeof(uint64_t); i++) + if (a64[i] != (b64[i] & b_mask64[i])) + return 1; + + return 0; +} + +static void +keycpy(void *dst, void *src, void *src_mask, uint32_t n_bytes) +{ + uint64_t *dst64 = dst, *src64 = src, *src_mask64 = src_mask; + uint32_t i; + + for (i = 0; i < n_bytes / sizeof(uint64_t); i++) + dst64[i] = src64[i] & src_mask64[i]; +} + +static int +check_params_create(struct rte_table_hash_params *params) +{ + /* name */ + if (params->name == NULL) { + RTE_LOG(ERR, TABLE, "%s: name invalid value\n", __func__); + return -EINVAL; + } + + /* key_size */ + if ((params->key_size < sizeof(uint64_t)) || + (!rte_is_power_of_2(params->key_size))) { + RTE_LOG(ERR, TABLE, "%s: key_size invalid value\n", __func__); + return -EINVAL; + } + + /* n_keys */ + if (params->n_keys == 0) { + RTE_LOG(ERR, TABLE, "%s: n_keys invalid value\n", __func__); + return -EINVAL; + } + + /* n_buckets */ + if ((params->n_buckets == 0) || + (!rte_is_power_of_2(params->n_buckets))) { + RTE_LOG(ERR, TABLE, "%s: n_buckets invalid value\n", __func__); + return -EINVAL; + } + + /* f_hash */ + if (params->f_hash == NULL) { + RTE_LOG(ERR, TABLE, "%s: f_hash invalid value\n", __func__); + return -EINVAL; + } + + return 0; +} + +static void * +rte_table_hash_lru_create(void *params, int socket_id, uint32_t entry_size) +{ + struct rte_table_hash_params *p = params; + struct rte_table_hash *t; + uint64_t table_meta_sz, key_mask_sz, bucket_sz, key_sz, key_stack_sz; + uint64_t data_sz, total_size; + uint64_t key_mask_offset, bucket_offset, key_offset, key_stack_offset; + uint64_t data_offset; + uint32_t n_buckets, i; + + /* Check input parameters */ + if ((check_params_create(p) != 0) || + (!rte_is_power_of_2(entry_size)) || + ((sizeof(struct rte_table_hash) % RTE_CACHE_LINE_SIZE) != 0) || + (sizeof(struct bucket) != (RTE_CACHE_LINE_SIZE / 2))) { + return NULL; + } + + /* + * Table dimensioning + * + * Objective: Pick the number of buckets (n_buckets) so that there a chance + * to store n_keys keys in the table. + * + * Note: Since the buckets do not get extended, it is not possible to + * guarantee that n_keys keys can be stored in the table at any time. In the + * worst case scenario when all the n_keys fall into the same bucket, only + * a maximum of KEYS_PER_BUCKET keys will be stored in the table. This case + * defeats the purpose of the hash table. It indicates unsuitable f_hash or + * n_keys to n_buckets ratio. + * + * MIN(n_buckets) = (n_keys + KEYS_PER_BUCKET - 1) / KEYS_PER_BUCKET + */ + n_buckets = rte_align32pow2( + (p->n_keys + KEYS_PER_BUCKET - 1) / KEYS_PER_BUCKET); + n_buckets = RTE_MAX(n_buckets, p->n_buckets); + + /* Memory allocation */ + table_meta_sz = RTE_CACHE_LINE_ROUNDUP(sizeof(struct rte_table_hash)); + key_mask_sz = RTE_CACHE_LINE_ROUNDUP(p->key_size); + bucket_sz = RTE_CACHE_LINE_ROUNDUP(n_buckets * sizeof(struct bucket)); + key_sz = RTE_CACHE_LINE_ROUNDUP(p->n_keys * p->key_size); + key_stack_sz = RTE_CACHE_LINE_ROUNDUP(p->n_keys * sizeof(uint32_t)); + data_sz = RTE_CACHE_LINE_ROUNDUP(p->n_keys * entry_size); + total_size = table_meta_sz + key_mask_sz + bucket_sz + key_sz + + key_stack_sz + data_sz; + + if (total_size > SIZE_MAX) { + RTE_LOG(ERR, TABLE, + "%s: Cannot allocate %" PRIu64 " bytes for hash " + "table %s\n", + __func__, total_size, p->name); + return NULL; + } + + t = rte_zmalloc_socket(p->name, + (size_t)total_size, + RTE_CACHE_LINE_SIZE, + socket_id); + if (t == NULL) { + RTE_LOG(ERR, TABLE, + "%s: Cannot allocate %" PRIu64 " bytes for hash " + "table %s\n", + __func__, total_size, p->name); + return NULL; + } + RTE_LOG(INFO, TABLE, "%s (%u-byte key): Hash table %s memory footprint" + " is %" PRIu64 " bytes\n", + __func__, p->key_size, p->name, total_size); + + /* Memory initialization */ + t->key_size = p->key_size; + t->entry_size = entry_size; + t->n_keys = p->n_keys; + t->n_buckets = n_buckets; + t->f_hash = p->f_hash; + t->seed = p->seed; + t->key_offset = p->key_offset; + + /* Internal */ + t->bucket_mask = t->n_buckets - 1; + t->key_size_shl = __builtin_ctzl(p->key_size); + t->data_size_shl = __builtin_ctzl(entry_size); + + /* Tables */ + key_mask_offset = 0; + bucket_offset = key_mask_offset + key_mask_sz; + key_offset = bucket_offset + bucket_sz; + key_stack_offset = key_offset + key_sz; + data_offset = key_stack_offset + key_stack_sz; + + t->key_mask = (uint64_t *) &t->memory[key_mask_offset]; + t->buckets = (struct bucket *) &t->memory[bucket_offset]; + t->key_mem = &t->memory[key_offset]; + t->key_stack = (uint32_t *) &t->memory[key_stack_offset]; + t->data_mem = &t->memory[data_offset]; + + /* Key mask */ + if (p->key_mask == NULL) + memset(t->key_mask, 0xFF, p->key_size); + else + memcpy(t->key_mask, p->key_mask, p->key_size); + + /* Key stack */ + for (i = 0; i < t->n_keys; i++) + t->key_stack[i] = t->n_keys - 1 - i; + t->key_stack_tos = t->n_keys; + + /* LRU */ + for (i = 0; i < t->n_buckets; i++) { + struct bucket *bkt = &t->buckets[i]; + + lru_init(bkt); + } + + return t; +} + +static int +rte_table_hash_lru_free(void *table) +{ + struct rte_table_hash *t = table; + + /* Check input parameters */ + if (t == NULL) + return -EINVAL; + + rte_free(t); + return 0; +} + +static int +rte_table_hash_lru_entry_add(void *table, void *key, void *entry, + int *key_found, void **entry_ptr) +{ + struct rte_table_hash *t = table; + struct bucket *bkt; + uint64_t sig; + uint32_t bkt_index, i; + + sig = t->f_hash(key, t->key_mask, t->key_size, t->seed); + bkt_index = sig & t->bucket_mask; + bkt = &t->buckets[bkt_index]; + sig = (sig >> 16) | 1LLU; + + /* Key is present in the bucket */ + for (i = 0; i < KEYS_PER_BUCKET; i++) { + uint64_t bkt_sig = (uint64_t) bkt->sig[i]; + uint32_t bkt_key_index = bkt->key_pos[i]; + uint8_t *bkt_key = &t->key_mem[bkt_key_index << + t->key_size_shl]; + + if ((sig == bkt_sig) && (keycmp(bkt_key, key, t->key_mask, + t->key_size) == 0)) { + uint8_t *data = &t->data_mem[bkt_key_index << + t->data_size_shl]; + + memcpy(data, entry, t->entry_size); + lru_update(bkt, i); + *key_found = 1; + *entry_ptr = (void *) data; + return 0; + } + } + + /* Key is not present in the bucket */ + for (i = 0; i < KEYS_PER_BUCKET; i++) { + uint64_t bkt_sig = (uint64_t) bkt->sig[i]; + + if (bkt_sig == 0) { + uint32_t bkt_key_index; + uint8_t *bkt_key, *data; + + /* Allocate new key */ + if (t->key_stack_tos == 0) { + /* No keys available */ + return -ENOSPC; + } + bkt_key_index = t->key_stack[--t->key_stack_tos]; + + /* Install new key */ + bkt_key = &t->key_mem[bkt_key_index << t->key_size_shl]; + data = &t->data_mem[bkt_key_index << t->data_size_shl]; + + bkt->sig[i] = (uint16_t) sig; + bkt->key_pos[i] = bkt_key_index; + keycpy(bkt_key, key, t->key_mask, t->key_size); + memcpy(data, entry, t->entry_size); + lru_update(bkt, i); + + *key_found = 0; + *entry_ptr = (void *) data; + return 0; + } + } + + /* Bucket full */ + { + uint64_t pos = lru_pos(bkt); + uint32_t bkt_key_index = bkt->key_pos[pos]; + uint8_t *bkt_key = &t->key_mem[bkt_key_index << + t->key_size_shl]; + uint8_t *data = &t->data_mem[bkt_key_index << t->data_size_shl]; + + bkt->sig[pos] = (uint16_t) sig; + keycpy(bkt_key, key, t->key_mask, t->key_size); + memcpy(data, entry, t->entry_size); + lru_update(bkt, pos); + + *key_found = 0; + *entry_ptr = (void *) data; + return 0; + } +} + +static int +rte_table_hash_lru_entry_delete(void *table, void *key, int *key_found, + void *entry) +{ + struct rte_table_hash *t = table; + struct bucket *bkt; + uint64_t sig; + uint32_t bkt_index, i; + + sig = t->f_hash(key, t->key_mask, t->key_size, t->seed); + bkt_index = sig & t->bucket_mask; + bkt = &t->buckets[bkt_index]; + sig = (sig >> 16) | 1LLU; + + /* Key is present in the bucket */ + for (i = 0; i < KEYS_PER_BUCKET; i++) { + uint64_t bkt_sig = (uint64_t) bkt->sig[i]; + uint32_t bkt_key_index = bkt->key_pos[i]; + uint8_t *bkt_key = &t->key_mem[bkt_key_index << + t->key_size_shl]; + + if ((sig == bkt_sig) && + (keycmp(bkt_key, key, t->key_mask, t->key_size) == 0)) { + uint8_t *data = &t->data_mem[bkt_key_index << + t->data_size_shl]; + + bkt->sig[i] = 0; + t->key_stack[t->key_stack_tos++] = bkt_key_index; + *key_found = 1; + if (entry) + memcpy(entry, data, t->entry_size); + return 0; + } + } + + /* Key is not present in the bucket */ + *key_found = 0; + return 0; +} + +static int rte_table_hash_lru_lookup_unoptimized( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *t = (struct rte_table_hash *) table; + uint64_t pkts_mask_out = 0; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_HASH_LRU_STATS_PKTS_IN_ADD(t, n_pkts_in); + + for ( ; pkts_mask; ) { + struct bucket *bkt; + struct rte_mbuf *pkt; + uint8_t *key; + uint64_t pkt_mask, sig; + uint32_t pkt_index, bkt_index, i; + + pkt_index = __builtin_ctzll(pkts_mask); + pkt_mask = 1LLU << pkt_index; + pkts_mask &= ~pkt_mask; + + pkt = pkts[pkt_index]; + key = RTE_MBUF_METADATA_UINT8_PTR(pkt, t->key_offset); + sig = (uint64_t) t->f_hash(key, t->key_mask, t->key_size, t->seed); + + bkt_index = sig & t->bucket_mask; + bkt = &t->buckets[bkt_index]; + sig = (sig >> 16) | 1LLU; + + /* Key is present in the bucket */ + for (i = 0; i < KEYS_PER_BUCKET; i++) { + uint64_t bkt_sig = (uint64_t) bkt->sig[i]; + uint32_t bkt_key_index = bkt->key_pos[i]; + uint8_t *bkt_key = &t->key_mem[bkt_key_index << + t->key_size_shl]; + + if ((sig == bkt_sig) && (keycmp(bkt_key, key, t->key_mask, + t->key_size) == 0)) { + uint8_t *data = &t->data_mem[bkt_key_index << + t->data_size_shl]; + + lru_update(bkt, i); + pkts_mask_out |= pkt_mask; + entries[pkt_index] = (void *) data; + break; + } + } + } + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_LRU_STATS_PKTS_LOOKUP_MISS(t, n_pkts_in - __builtin_popcountll(pkts_mask_out)); + return 0; +} + +/*** +* +* mask = match bitmask +* match = at least one match +* match_many = more than one match +* match_pos = position of first match +* +* ---------------------------------------- +* mask match match_many match_pos +* ---------------------------------------- +* 0000 0 0 00 +* 0001 1 0 00 +* 0010 1 0 01 +* 0011 1 1 00 +* ---------------------------------------- +* 0100 1 0 10 +* 0101 1 1 00 +* 0110 1 1 01 +* 0111 1 1 00 +* ---------------------------------------- +* 1000 1 0 11 +* 1001 1 1 00 +* 1010 1 1 01 +* 1011 1 1 00 +* ---------------------------------------- +* 1100 1 1 10 +* 1101 1 1 00 +* 1110 1 1 01 +* 1111 1 1 00 +* ---------------------------------------- +* +* match = 1111_1111_1111_1110 +* match_many = 1111_1110_1110_1000 +* match_pos = 0001_0010_0001_0011__0001_0010_0001_0000 +* +* match = 0xFFFELLU +* match_many = 0xFEE8LLU +* match_pos = 0x12131210LLU +* +***/ + +#define LUT_MATCH 0xFFFELLU +#define LUT_MATCH_MANY 0xFEE8LLU +#define LUT_MATCH_POS 0x12131210LLU + +#define lookup_cmp_sig(mbuf_sig, bucket, match, match_many, match_pos)\ +{ \ + uint64_t bucket_sig[4], mask[4], mask_all; \ + \ + bucket_sig[0] = bucket->sig[0]; \ + bucket_sig[1] = bucket->sig[1]; \ + bucket_sig[2] = bucket->sig[2]; \ + bucket_sig[3] = bucket->sig[3]; \ + \ + bucket_sig[0] ^= mbuf_sig; \ + bucket_sig[1] ^= mbuf_sig; \ + bucket_sig[2] ^= mbuf_sig; \ + bucket_sig[3] ^= mbuf_sig; \ + \ + mask[0] = 0; \ + mask[1] = 0; \ + mask[2] = 0; \ + mask[3] = 0; \ + \ + if (bucket_sig[0] == 0) \ + mask[0] = 1; \ + if (bucket_sig[1] == 0) \ + mask[1] = 2; \ + if (bucket_sig[2] == 0) \ + mask[2] = 4; \ + if (bucket_sig[3] == 0) \ + mask[3] = 8; \ + \ + mask_all = (mask[0] | mask[1]) | (mask[2] | mask[3]); \ + \ + match = (LUT_MATCH >> mask_all) & 1; \ + match_many = (LUT_MATCH_MANY >> mask_all) & 1; \ + match_pos = (LUT_MATCH_POS >> (mask_all << 1)) & 3; \ +} + +#define lookup_cmp_key(mbuf, key, match_key, f) \ +{ \ + uint64_t *pkt_key = RTE_MBUF_METADATA_UINT64_PTR(mbuf, f->key_offset);\ + uint64_t *bkt_key = (uint64_t *) key; \ + uint64_t *key_mask = f->key_mask; \ + \ + switch (f->key_size) { \ + case 8: \ + { \ + uint64_t xor = (pkt_key[0] & key_mask[0]) ^ bkt_key[0]; \ + match_key = 0; \ + if (xor == 0) \ + match_key = 1; \ + } \ + break; \ + \ + case 16: \ + { \ + uint64_t xor[2], or; \ + \ + xor[0] = (pkt_key[0] & key_mask[0]) ^ bkt_key[0]; \ + xor[1] = (pkt_key[1] & key_mask[1]) ^ bkt_key[1]; \ + or = xor[0] | xor[1]; \ + match_key = 0; \ + if (or == 0) \ + match_key = 1; \ + } \ + break; \ + \ + case 32: \ + { \ + uint64_t xor[4], or; \ + \ + xor[0] = (pkt_key[0] & key_mask[0]) ^ bkt_key[0]; \ + xor[1] = (pkt_key[1] & key_mask[1]) ^ bkt_key[1]; \ + xor[2] = (pkt_key[2] & key_mask[2]) ^ bkt_key[2]; \ + xor[3] = (pkt_key[3] & key_mask[3]) ^ bkt_key[3]; \ + or = xor[0] | xor[1] | xor[2] | xor[3]; \ + match_key = 0; \ + if (or == 0) \ + match_key = 1; \ + } \ + break; \ + \ + case 64: \ + { \ + uint64_t xor[8], or; \ + \ + xor[0] = (pkt_key[0] & key_mask[0]) ^ bkt_key[0]; \ + xor[1] = (pkt_key[1] & key_mask[1]) ^ bkt_key[1]; \ + xor[2] = (pkt_key[2] & key_mask[2]) ^ bkt_key[2]; \ + xor[3] = (pkt_key[3] & key_mask[3]) ^ bkt_key[3]; \ + xor[4] = (pkt_key[4] & key_mask[4]) ^ bkt_key[4]; \ + xor[5] = (pkt_key[5] & key_mask[5]) ^ bkt_key[5]; \ + xor[6] = (pkt_key[6] & key_mask[6]) ^ bkt_key[6]; \ + xor[7] = (pkt_key[7] & key_mask[7]) ^ bkt_key[7]; \ + or = xor[0] | xor[1] | xor[2] | xor[3] | \ + xor[4] | xor[5] | xor[6] | xor[7]; \ + match_key = 0; \ + if (or == 0) \ + match_key = 1; \ + } \ + break; \ + \ + default: \ + match_key = 0; \ + if (keycmp(bkt_key, pkt_key, key_mask, f->key_size) == 0) \ + match_key = 1; \ + } \ +} + +#define lookup2_stage0(t, g, pkts, pkts_mask, pkt00_index, pkt01_index)\ +{ \ + uint64_t pkt00_mask, pkt01_mask; \ + struct rte_mbuf *mbuf00, *mbuf01; \ + uint32_t key_offset = t->key_offset; \ + \ + pkt00_index = __builtin_ctzll(pkts_mask); \ + pkt00_mask = 1LLU << pkt00_index; \ + pkts_mask &= ~pkt00_mask; \ + mbuf00 = pkts[pkt00_index]; \ + \ + pkt01_index = __builtin_ctzll(pkts_mask); \ + pkt01_mask = 1LLU << pkt01_index; \ + pkts_mask &= ~pkt01_mask; \ + mbuf01 = pkts[pkt01_index]; \ + \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf00, key_offset));\ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf01, key_offset));\ +} + +#define lookup2_stage0_with_odd_support(t, g, pkts, pkts_mask, pkt00_index, \ + pkt01_index) \ +{ \ + uint64_t pkt00_mask, pkt01_mask; \ + struct rte_mbuf *mbuf00, *mbuf01; \ + uint32_t key_offset = t->key_offset; \ + \ + pkt00_index = __builtin_ctzll(pkts_mask); \ + pkt00_mask = 1LLU << pkt00_index; \ + pkts_mask &= ~pkt00_mask; \ + mbuf00 = pkts[pkt00_index]; \ + \ + pkt01_index = __builtin_ctzll(pkts_mask); \ + if (pkts_mask == 0) \ + pkt01_index = pkt00_index; \ + \ + pkt01_mask = 1LLU << pkt01_index; \ + pkts_mask &= ~pkt01_mask; \ + mbuf01 = pkts[pkt01_index]; \ + \ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf00, key_offset));\ + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbuf01, key_offset));\ +} + +#define lookup2_stage1(t, g, pkts, pkt10_index, pkt11_index)\ +{ \ + struct grinder *g10, *g11; \ + uint64_t sig10, sig11, bkt10_index, bkt11_index; \ + struct rte_mbuf *mbuf10, *mbuf11; \ + struct bucket *bkt10, *bkt11, *buckets = t->buckets; \ + uint8_t *key10, *key11; \ + uint64_t bucket_mask = t->bucket_mask; \ + rte_table_hash_op_hash f_hash = t->f_hash; \ + uint64_t seed = t->seed; \ + uint32_t key_size = t->key_size; \ + uint32_t key_offset = t->key_offset; \ + \ + mbuf10 = pkts[pkt10_index]; \ + key10 = RTE_MBUF_METADATA_UINT8_PTR(mbuf10, key_offset);\ + sig10 = (uint64_t) f_hash(key10, t->key_mask, key_size, seed);\ + bkt10_index = sig10 & bucket_mask; \ + bkt10 = &buckets[bkt10_index]; \ + \ + mbuf11 = pkts[pkt11_index]; \ + key11 = RTE_MBUF_METADATA_UINT8_PTR(mbuf11, key_offset);\ + sig11 = (uint64_t) f_hash(key11, t->key_mask, key_size, seed);\ + bkt11_index = sig11 & bucket_mask; \ + bkt11 = &buckets[bkt11_index]; \ + \ + rte_prefetch0(bkt10); \ + rte_prefetch0(bkt11); \ + \ + g10 = &g[pkt10_index]; \ + g10->sig = sig10; \ + g10->bkt = bkt10; \ + \ + g11 = &g[pkt11_index]; \ + g11->sig = sig11; \ + g11->bkt = bkt11; \ +} + +#define lookup2_stage2(t, g, pkt20_index, pkt21_index, pkts_mask_match_many)\ +{ \ + struct grinder *g20, *g21; \ + uint64_t sig20, sig21; \ + struct bucket *bkt20, *bkt21; \ + uint8_t *key20, *key21, *key_mem = t->key_mem; \ + uint64_t match20, match21, match_many20, match_many21; \ + uint64_t match_pos20, match_pos21; \ + uint32_t key20_index, key21_index, key_size_shl = t->key_size_shl;\ + \ + g20 = &g[pkt20_index]; \ + sig20 = g20->sig; \ + bkt20 = g20->bkt; \ + sig20 = (sig20 >> 16) | 1LLU; \ + lookup_cmp_sig(sig20, bkt20, match20, match_many20, match_pos20);\ + match20 <<= pkt20_index; \ + match_many20 <<= pkt20_index; \ + key20_index = bkt20->key_pos[match_pos20]; \ + key20 = &key_mem[key20_index << key_size_shl]; \ + \ + g21 = &g[pkt21_index]; \ + sig21 = g21->sig; \ + bkt21 = g21->bkt; \ + sig21 = (sig21 >> 16) | 1LLU; \ + lookup_cmp_sig(sig21, bkt21, match21, match_many21, match_pos21);\ + match21 <<= pkt21_index; \ + match_many21 <<= pkt21_index; \ + key21_index = bkt21->key_pos[match_pos21]; \ + key21 = &key_mem[key21_index << key_size_shl]; \ + \ + rte_prefetch0(key20); \ + rte_prefetch0(key21); \ + \ + pkts_mask_match_many |= match_many20 | match_many21; \ + \ + g20->match = match20; \ + g20->match_pos = match_pos20; \ + g20->key_index = key20_index; \ + \ + g21->match = match21; \ + g21->match_pos = match_pos21; \ + g21->key_index = key21_index; \ +} + +#define lookup2_stage3(t, g, pkts, pkt30_index, pkt31_index, pkts_mask_out, \ + entries) \ +{ \ + struct grinder *g30, *g31; \ + struct rte_mbuf *mbuf30, *mbuf31; \ + struct bucket *bkt30, *bkt31; \ + uint8_t *key30, *key31, *key_mem = t->key_mem; \ + uint8_t *data30, *data31, *data_mem = t->data_mem; \ + uint64_t match30, match31, match_pos30, match_pos31; \ + uint64_t match_key30, match_key31, match_keys; \ + uint32_t key30_index, key31_index; \ + uint32_t key_size_shl = t->key_size_shl; \ + uint32_t data_size_shl = t->data_size_shl; \ + \ + mbuf30 = pkts[pkt30_index]; \ + g30 = &g[pkt30_index]; \ + bkt30 = g30->bkt; \ + match30 = g30->match; \ + match_pos30 = g30->match_pos; \ + key30_index = g30->key_index; \ + key30 = &key_mem[key30_index << key_size_shl]; \ + lookup_cmp_key(mbuf30, key30, match_key30, t); \ + match_key30 <<= pkt30_index; \ + match_key30 &= match30; \ + data30 = &data_mem[key30_index << data_size_shl]; \ + entries[pkt30_index] = data30; \ + \ + mbuf31 = pkts[pkt31_index]; \ + g31 = &g[pkt31_index]; \ + bkt31 = g31->bkt; \ + match31 = g31->match; \ + match_pos31 = g31->match_pos; \ + key31_index = g31->key_index; \ + key31 = &key_mem[key31_index << key_size_shl]; \ + lookup_cmp_key(mbuf31, key31, match_key31, t); \ + match_key31 <<= pkt31_index; \ + match_key31 &= match31; \ + data31 = &data_mem[key31_index << data_size_shl]; \ + entries[pkt31_index] = data31; \ + \ + rte_prefetch0(data30); \ + rte_prefetch0(data31); \ + \ + match_keys = match_key30 | match_key31; \ + pkts_mask_out |= match_keys; \ + \ + if (match_key30 == 0) \ + match_pos30 = 4; \ + lru_update(bkt30, match_pos30); \ + \ + if (match_key31 == 0) \ + match_pos31 = 4; \ + lru_update(bkt31, match_pos31); \ +} + +/*** +* The lookup function implements a 4-stage pipeline, with each stage processing +* two different packets. The purpose of pipelined implementation is to hide the +* latency of prefetching the data structures and loosen the data dependency +* between instructions. +* +* p00 _______ p10 _______ p20 _______ p30 _______ +* ----->| |----->| |----->| |----->| |-----> +* | 0 | | 1 | | 2 | | 3 | +* ----->|_______|----->|_______|----->|_______|----->|_______|-----> +* p01 p11 p21 p31 +* +* The naming convention is: +* pXY = packet Y of stage X, X = 0 .. 3, Y = 0 .. 1 +* +***/ +static int rte_table_hash_lru_lookup( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_hash *t = (struct rte_table_hash *) table; + struct grinder *g = t->grinders; + uint64_t pkt00_index, pkt01_index, pkt10_index, pkt11_index; + uint64_t pkt20_index, pkt21_index, pkt30_index, pkt31_index; + uint64_t pkts_mask_out = 0, pkts_mask_match_many = 0; + int status = 0; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_HASH_LRU_STATS_PKTS_IN_ADD(t, n_pkts_in); + + /* Cannot run the pipeline with less than 7 packets */ + if (__builtin_popcountll(pkts_mask) < 7) + return rte_table_hash_lru_lookup_unoptimized(table, pkts, + pkts_mask, lookup_hit_mask, entries); + + /* Pipeline stage 0 */ + lookup2_stage0(t, g, pkts, pkts_mask, pkt00_index, pkt01_index); + + /* Pipeline feed */ + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0(t, g, pkts, pkts_mask, pkt00_index, pkt01_index); + + /* Pipeline stage 1 */ + lookup2_stage1(t, g, pkts, pkt10_index, pkt11_index); + + /* Pipeline feed */ + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0(t, g, pkts, pkts_mask, pkt00_index, pkt01_index); + + /* Pipeline stage 1 */ + lookup2_stage1(t, g, pkts, pkt10_index, pkt11_index); + + /* Pipeline stage 2 */ + lookup2_stage2(t, g, pkt20_index, pkt21_index, pkts_mask_match_many); + + /* + * Pipeline run + * + */ + for ( ; pkts_mask; ) { + /* Pipeline feed */ + pkt30_index = pkt20_index; + pkt31_index = pkt21_index; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 0 */ + lookup2_stage0_with_odd_support(t, g, pkts, pkts_mask, + pkt00_index, pkt01_index); + + /* Pipeline stage 1 */ + lookup2_stage1(t, g, pkts, pkt10_index, pkt11_index); + + /* Pipeline stage 2 */ + lookup2_stage2(t, g, pkt20_index, pkt21_index, + pkts_mask_match_many); + + /* Pipeline stage 3 */ + lookup2_stage3(t, g, pkts, pkt30_index, pkt31_index, + pkts_mask_out, entries); + } + + /* Pipeline feed */ + pkt30_index = pkt20_index; + pkt31_index = pkt21_index; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + pkt10_index = pkt00_index; + pkt11_index = pkt01_index; + + /* Pipeline stage 1 */ + lookup2_stage1(t, g, pkts, pkt10_index, pkt11_index); + + /* Pipeline stage 2 */ + lookup2_stage2(t, g, pkt20_index, pkt21_index, pkts_mask_match_many); + + /* Pipeline stage 3 */ + lookup2_stage3(t, g, pkts, pkt30_index, pkt31_index, pkts_mask_out, + entries); + + /* Pipeline feed */ + pkt30_index = pkt20_index; + pkt31_index = pkt21_index; + pkt20_index = pkt10_index; + pkt21_index = pkt11_index; + + /* Pipeline stage 2 */ + lookup2_stage2(t, g, pkt20_index, pkt21_index, pkts_mask_match_many); + + /* Pipeline stage 3 */ + lookup2_stage3(t, g, pkts, pkt30_index, pkt31_index, pkts_mask_out, + entries); + + /* Pipeline feed */ + pkt30_index = pkt20_index; + pkt31_index = pkt21_index; + + /* Pipeline stage 3 */ + lookup2_stage3(t, g, pkts, pkt30_index, pkt31_index, pkts_mask_out, + entries); + + /* Slow path */ + pkts_mask_match_many &= ~pkts_mask_out; + if (pkts_mask_match_many) { + uint64_t pkts_mask_out_slow = 0; + + status = rte_table_hash_lru_lookup_unoptimized(table, pkts, + pkts_mask_match_many, &pkts_mask_out_slow, entries); + pkts_mask_out |= pkts_mask_out_slow; + } + + *lookup_hit_mask = pkts_mask_out; + RTE_TABLE_HASH_LRU_STATS_PKTS_LOOKUP_MISS(t, n_pkts_in - __builtin_popcountll(pkts_mask_out)); + return status; +} + +static int +rte_table_hash_lru_stats_read(void *table, struct rte_table_stats *stats, int clear) +{ + struct rte_table_hash *t = table; + + if (stats != NULL) + memcpy(stats, &t->stats, sizeof(t->stats)); + + if (clear) + memset(&t->stats, 0, sizeof(t->stats)); + + return 0; +} + +struct rte_table_ops rte_table_hash_lru_ops = { + .f_create = rte_table_hash_lru_create, + .f_free = rte_table_hash_lru_free, + .f_add = rte_table_hash_lru_entry_add, + .f_delete = rte_table_hash_lru_entry_delete, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_hash_lru_lookup, + .f_stats = rte_table_hash_lru_stats_read, +}; diff --git a/lib/table/rte_table_lpm.c b/lib/table/rte_table_lpm.c new file mode 100644 index 0000000000..4dd8289ce5 --- /dev/null +++ b/lib/table/rte_table_lpm.c @@ -0,0 +1,366 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#include <string.h> +#include <stdio.h> + +#include <rte_common.h> +#include <rte_mbuf.h> +#include <rte_memory.h> +#include <rte_malloc.h> +#include <rte_byteorder.h> +#include <rte_log.h> +#include <rte_lpm.h> + +#include "rte_table_lpm.h" + +#ifndef RTE_TABLE_LPM_MAX_NEXT_HOPS +#define RTE_TABLE_LPM_MAX_NEXT_HOPS 65536 +#endif + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_LPM_STATS_PKTS_IN_ADD(table, val) \ + table->stats.n_pkts_in += val +#define RTE_TABLE_LPM_STATS_PKTS_LOOKUP_MISS(table, val) \ + table->stats.n_pkts_lookup_miss += val + +#else + +#define RTE_TABLE_LPM_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_LPM_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + +struct rte_table_lpm { + struct rte_table_stats stats; + + /* Input parameters */ + uint32_t entry_size; + uint32_t entry_unique_size; + uint32_t n_rules; + uint32_t offset; + + /* Handle to low-level LPM table */ + struct rte_lpm *lpm; + + /* Next Hop Table (NHT) */ + uint32_t nht_users[RTE_TABLE_LPM_MAX_NEXT_HOPS]; + uint8_t nht[0] __rte_cache_aligned; +}; + +static void * +rte_table_lpm_create(void *params, int socket_id, uint32_t entry_size) +{ + struct rte_table_lpm_params *p = params; + struct rte_table_lpm *lpm; + struct rte_lpm_config lpm_config; + + uint32_t total_size, nht_size; + + /* Check input parameters */ + if (p == NULL) { + RTE_LOG(ERR, TABLE, "%s: NULL input parameters\n", __func__); + return NULL; + } + if (p->n_rules == 0) { + RTE_LOG(ERR, TABLE, "%s: Invalid n_rules\n", __func__); + return NULL; + } + if (p->number_tbl8s == 0) { + RTE_LOG(ERR, TABLE, "%s: Invalid number_tbl8s\n", __func__); + return NULL; + } + if (p->entry_unique_size == 0) { + RTE_LOG(ERR, TABLE, "%s: Invalid entry_unique_size\n", + __func__); + return NULL; + } + if (p->entry_unique_size > entry_size) { + RTE_LOG(ERR, TABLE, "%s: Invalid entry_unique_size\n", + __func__); + return NULL; + } + if (p->name == NULL) { + RTE_LOG(ERR, TABLE, "%s: Table name is NULL\n", + __func__); + return NULL; + } + entry_size = RTE_ALIGN(entry_size, sizeof(uint64_t)); + + /* Memory allocation */ + nht_size = RTE_TABLE_LPM_MAX_NEXT_HOPS * entry_size; + total_size = sizeof(struct rte_table_lpm) + nht_size; + lpm = rte_zmalloc_socket("TABLE", total_size, RTE_CACHE_LINE_SIZE, + socket_id); + if (lpm == NULL) { + RTE_LOG(ERR, TABLE, + "%s: Cannot allocate %u bytes for LPM table\n", + __func__, total_size); + return NULL; + } + + /* LPM low-level table creation */ + lpm_config.max_rules = p->n_rules; + lpm_config.number_tbl8s = p->number_tbl8s; + lpm_config.flags = p->flags; + lpm->lpm = rte_lpm_create(p->name, socket_id, &lpm_config); + + if (lpm->lpm == NULL) { + rte_free(lpm); + RTE_LOG(ERR, TABLE, "Unable to create low-level LPM table\n"); + return NULL; + } + + /* Memory initialization */ + lpm->entry_size = entry_size; + lpm->entry_unique_size = p->entry_unique_size; + lpm->n_rules = p->n_rules; + lpm->offset = p->offset; + + return lpm; +} + +static int +rte_table_lpm_free(void *table) +{ + struct rte_table_lpm *lpm = table; + + /* Check input parameters */ + if (lpm == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + + /* Free previously allocated resources */ + rte_lpm_free(lpm->lpm); + rte_free(lpm); + + return 0; +} + +static int +nht_find_free(struct rte_table_lpm *lpm, uint32_t *pos) +{ + uint32_t i; + + for (i = 0; i < RTE_TABLE_LPM_MAX_NEXT_HOPS; i++) { + if (lpm->nht_users[i] == 0) { + *pos = i; + return 1; + } + } + + return 0; +} + +static int +nht_find_existing(struct rte_table_lpm *lpm, void *entry, uint32_t *pos) +{ + uint32_t i; + + for (i = 0; i < RTE_TABLE_LPM_MAX_NEXT_HOPS; i++) { + uint8_t *nht_entry = &lpm->nht[i * lpm->entry_size]; + + if ((lpm->nht_users[i] > 0) && (memcmp(nht_entry, entry, + lpm->entry_unique_size) == 0)) { + *pos = i; + return 1; + } + } + + return 0; +} + +static int +rte_table_lpm_entry_add( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr) +{ + struct rte_table_lpm *lpm = table; + struct rte_table_lpm_key *ip_prefix = key; + uint32_t nht_pos, nht_pos0_valid; + int status; + uint32_t nht_pos0 = 0; + + /* Check input parameters */ + if (lpm == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + if (ip_prefix == NULL) { + RTE_LOG(ERR, TABLE, "%s: ip_prefix parameter is NULL\n", + __func__); + return -EINVAL; + } + if (entry == NULL) { + RTE_LOG(ERR, TABLE, "%s: entry parameter is NULL\n", __func__); + return -EINVAL; + } + + if ((ip_prefix->depth == 0) || (ip_prefix->depth > 32)) { + RTE_LOG(ERR, TABLE, "%s: invalid depth (%d)\n", + __func__, ip_prefix->depth); + return -EINVAL; + } + + /* Check if rule is already present in the table */ + status = rte_lpm_is_rule_present(lpm->lpm, ip_prefix->ip, + ip_prefix->depth, &nht_pos0); + nht_pos0_valid = status > 0; + + /* Find existing or free NHT entry */ + if (nht_find_existing(lpm, entry, &nht_pos) == 0) { + uint8_t *nht_entry; + + if (nht_find_free(lpm, &nht_pos) == 0) { + RTE_LOG(ERR, TABLE, "%s: NHT full\n", __func__); + return -1; + } + + nht_entry = &lpm->nht[nht_pos * lpm->entry_size]; + memcpy(nht_entry, entry, lpm->entry_size); + } + + /* Add rule to low level LPM table */ + if (rte_lpm_add(lpm->lpm, ip_prefix->ip, ip_prefix->depth, nht_pos) < 0) { + RTE_LOG(ERR, TABLE, "%s: LPM rule add failed\n", __func__); + return -1; + } + + /* Commit NHT changes */ + lpm->nht_users[nht_pos]++; + lpm->nht_users[nht_pos0] -= nht_pos0_valid; + + *key_found = nht_pos0_valid; + *entry_ptr = (void *) &lpm->nht[nht_pos * lpm->entry_size]; + return 0; +} + +static int +rte_table_lpm_entry_delete( + void *table, + void *key, + int *key_found, + void *entry) +{ + struct rte_table_lpm *lpm = table; + struct rte_table_lpm_key *ip_prefix = key; + uint32_t nht_pos; + int status; + + /* Check input parameters */ + if (lpm == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + if (ip_prefix == NULL) { + RTE_LOG(ERR, TABLE, "%s: ip_prefix parameter is NULL\n", + __func__); + return -EINVAL; + } + if ((ip_prefix->depth == 0) || (ip_prefix->depth > 32)) { + RTE_LOG(ERR, TABLE, "%s: invalid depth (%d)\n", __func__, + ip_prefix->depth); + return -EINVAL; + } + + /* Return if rule is not present in the table */ + status = rte_lpm_is_rule_present(lpm->lpm, ip_prefix->ip, + ip_prefix->depth, &nht_pos); + if (status < 0) { + RTE_LOG(ERR, TABLE, "%s: LPM algorithmic error\n", __func__); + return -1; + } + if (status == 0) { + *key_found = 0; + return 0; + } + + /* Delete rule from the low-level LPM table */ + status = rte_lpm_delete(lpm->lpm, ip_prefix->ip, ip_prefix->depth); + if (status) { + RTE_LOG(ERR, TABLE, "%s: LPM rule delete failed\n", __func__); + return -1; + } + + /* Commit NHT changes */ + lpm->nht_users[nht_pos]--; + + *key_found = 1; + if (entry) + memcpy(entry, &lpm->nht[nht_pos * lpm->entry_size], + lpm->entry_size); + + return 0; +} + +static int +rte_table_lpm_lookup( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_lpm *lpm = (struct rte_table_lpm *) table; + uint64_t pkts_out_mask = 0; + uint32_t i; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_LPM_STATS_PKTS_IN_ADD(lpm, n_pkts_in); + + pkts_out_mask = 0; + for (i = 0; i < (uint32_t)(RTE_PORT_IN_BURST_SIZE_MAX - + __builtin_clzll(pkts_mask)); i++) { + uint64_t pkt_mask = 1LLU << i; + + if (pkt_mask & pkts_mask) { + struct rte_mbuf *pkt = pkts[i]; + uint32_t ip = rte_bswap32( + RTE_MBUF_METADATA_UINT32(pkt, lpm->offset)); + int status; + uint32_t nht_pos; + + status = rte_lpm_lookup(lpm->lpm, ip, &nht_pos); + if (status == 0) { + pkts_out_mask |= pkt_mask; + entries[i] = (void *) &lpm->nht[nht_pos * + lpm->entry_size]; + } + } + } + + *lookup_hit_mask = pkts_out_mask; + RTE_TABLE_LPM_STATS_PKTS_LOOKUP_MISS(lpm, n_pkts_in - __builtin_popcountll(pkts_out_mask)); + return 0; +} + +static int +rte_table_lpm_stats_read(void *table, struct rte_table_stats *stats, int clear) +{ + struct rte_table_lpm *t = table; + + if (stats != NULL) + memcpy(stats, &t->stats, sizeof(t->stats)); + + if (clear) + memset(&t->stats, 0, sizeof(t->stats)); + + return 0; +} + +struct rte_table_ops rte_table_lpm_ops = { + .f_create = rte_table_lpm_create, + .f_free = rte_table_lpm_free, + .f_add = rte_table_lpm_entry_add, + .f_delete = rte_table_lpm_entry_delete, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_lpm_lookup, + .f_stats = rte_table_lpm_stats_read, +}; diff --git a/lib/table/rte_table_lpm.h b/lib/table/rte_table_lpm.h new file mode 100644 index 0000000000..571ff2f009 --- /dev/null +++ b/lib/table/rte_table_lpm.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_TABLE_LPM_H__ +#define __INCLUDE_RTE_TABLE_LPM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE Table LPM for IPv4 + * + * This table uses the Longest Prefix Match (LPM) algorithm to uniquely + * associate data to lookup keys. + * + * Use-case: IP routing table. Routes that are added to the table associate a + * next hop to an IP prefix. The IP prefix is specified as IP address and depth + * and cover for a multitude of lookup keys (i.e. destination IP addresses) + * that all share the same data (i.e. next hop). The next hop information + * typically contains the output interface ID, the IP address of the next hop + * station (which is part of the same IP network the output interface is + * connected to) and other flags and counters. + * + * The LPM primitive only allows associating an 8-bit number (next hop ID) to + * an IP prefix, while a routing table can potentially contain thousands of + * routes or even more. This means that the same next hop ID (and next hop + * information) has to be shared by multiple routes, which makes sense, as + * multiple remote networks could be reached through the same next hop. + * Therefore, when a route is added or updated, the LPM table has to check + * whether the same next hop is already in use before using a new next hop ID + * for this route. + * + * The comparison between different next hops is done for the first + * “entry_unique_size” bytes of the next hop information (configurable + * parameter), which have to uniquely identify the next hop, therefore the user + * has to carefully manage the format of the LPM table entry (i.e. the next + * hop information) so that any next hop data that changes value during + * run-time (e.g. counters) is placed outside of this area. + * + ***/ + +#include <stdint.h> + +#include "rte_table.h" + +/** LPM table parameters */ +struct rte_table_lpm_params { + /** Table name */ + const char *name; + + /** Maximum number of LPM rules (i.e. IP routes) */ + uint32_t n_rules; + + /**< Number of tbl8s to allocate. */ + uint32_t number_tbl8s; + + /**< This field is currently unused. */ + int flags; + + /** Number of bytes at the start of the table entry that uniquely + identify the entry. Cannot be bigger than table entry size. */ + uint32_t entry_unique_size; + + /** Byte offset within input packet meta-data where lookup key (i.e. + the destination IP address) is located. */ + uint32_t offset; +}; + +/** LPM table rule (i.e. route), specified as IP prefix. While the key used by +the lookup operation is the destination IP address (read from the input packet +meta-data), the entry add and entry delete operations work with LPM rules, with +each rule covering for a multitude of lookup keys (destination IP addresses) +that share the same data (next hop). */ +struct rte_table_lpm_key { + /** IP address */ + uint32_t ip; + + /** IP address depth. The most significant "depth" bits of the IP + address specify the network part of the IP address, while the rest of + the bits specify the host part of the address and are ignored for the + purpose of route specification. */ + uint8_t depth; +}; + +/** LPM table operations */ +extern struct rte_table_ops rte_table_lpm_ops; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_table_lpm_ipv6.c b/lib/table/rte_table_lpm_ipv6.c new file mode 100644 index 0000000000..4e068d79bf --- /dev/null +++ b/lib/table/rte_table_lpm_ipv6.c @@ -0,0 +1,368 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#include <string.h> +#include <stdio.h> + +#include <rte_common.h> +#include <rte_mbuf.h> +#include <rte_memory.h> +#include <rte_malloc.h> +#include <rte_byteorder.h> +#include <rte_log.h> +#include <rte_lpm6.h> + +#include "rte_table_lpm_ipv6.h" + +#define RTE_TABLE_LPM_MAX_NEXT_HOPS 256 + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_LPM_IPV6_STATS_PKTS_IN_ADD(table, val) \ + table->stats.n_pkts_in += val +#define RTE_TABLE_LPM_IPV6_STATS_PKTS_LOOKUP_MISS(table, val) \ + table->stats.n_pkts_lookup_miss += val + +#else + +#define RTE_TABLE_LPM_IPV6_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_LPM_IPV6_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + +struct rte_table_lpm_ipv6 { + struct rte_table_stats stats; + + /* Input parameters */ + uint32_t entry_size; + uint32_t entry_unique_size; + uint32_t n_rules; + uint32_t offset; + + /* Handle to low-level LPM table */ + struct rte_lpm6 *lpm; + + /* Next Hop Table (NHT) */ + uint32_t nht_users[RTE_TABLE_LPM_MAX_NEXT_HOPS]; + uint8_t nht[0] __rte_cache_aligned; +}; + +static void * +rte_table_lpm_ipv6_create(void *params, int socket_id, uint32_t entry_size) +{ + struct rte_table_lpm_ipv6_params *p = + params; + struct rte_table_lpm_ipv6 *lpm; + struct rte_lpm6_config lpm6_config; + uint32_t total_size, nht_size; + + /* Check input parameters */ + if (p == NULL) { + RTE_LOG(ERR, TABLE, "%s: NULL input parameters\n", __func__); + return NULL; + } + if (p->n_rules == 0) { + RTE_LOG(ERR, TABLE, "%s: Invalid n_rules\n", __func__); + return NULL; + } + if (p->number_tbl8s == 0) { + RTE_LOG(ERR, TABLE, "%s: Invalid n_rules\n", __func__); + return NULL; + } + if (p->entry_unique_size == 0) { + RTE_LOG(ERR, TABLE, "%s: Invalid entry_unique_size\n", + __func__); + return NULL; + } + if (p->entry_unique_size > entry_size) { + RTE_LOG(ERR, TABLE, "%s: Invalid entry_unique_size\n", + __func__); + return NULL; + } + if (p->name == NULL) { + RTE_LOG(ERR, TABLE, "%s: Table name is NULL\n", + __func__); + return NULL; + } + entry_size = RTE_ALIGN(entry_size, sizeof(uint64_t)); + + /* Memory allocation */ + nht_size = RTE_TABLE_LPM_MAX_NEXT_HOPS * entry_size; + total_size = sizeof(struct rte_table_lpm_ipv6) + nht_size; + lpm = rte_zmalloc_socket("TABLE", total_size, RTE_CACHE_LINE_SIZE, + socket_id); + if (lpm == NULL) { + RTE_LOG(ERR, TABLE, + "%s: Cannot allocate %u bytes for LPM IPv6 table\n", + __func__, total_size); + return NULL; + } + + /* LPM low-level table creation */ + lpm6_config.max_rules = p->n_rules; + lpm6_config.number_tbl8s = p->number_tbl8s; + lpm6_config.flags = 0; + lpm->lpm = rte_lpm6_create(p->name, socket_id, &lpm6_config); + if (lpm->lpm == NULL) { + rte_free(lpm); + RTE_LOG(ERR, TABLE, + "Unable to create low-level LPM IPv6 table\n"); + return NULL; + } + + /* Memory initialization */ + lpm->entry_size = entry_size; + lpm->entry_unique_size = p->entry_unique_size; + lpm->n_rules = p->n_rules; + lpm->offset = p->offset; + + return lpm; +} + +static int +rte_table_lpm_ipv6_free(void *table) +{ + struct rte_table_lpm_ipv6 *lpm = table; + + /* Check input parameters */ + if (lpm == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + + /* Free previously allocated resources */ + rte_lpm6_free(lpm->lpm); + rte_free(lpm); + + return 0; +} + +static int +nht_find_free(struct rte_table_lpm_ipv6 *lpm, uint32_t *pos) +{ + uint32_t i; + + for (i = 0; i < RTE_TABLE_LPM_MAX_NEXT_HOPS; i++) { + if (lpm->nht_users[i] == 0) { + *pos = i; + return 1; + } + } + + return 0; +} + +static int +nht_find_existing(struct rte_table_lpm_ipv6 *lpm, void *entry, uint32_t *pos) +{ + uint32_t i; + + for (i = 0; i < RTE_TABLE_LPM_MAX_NEXT_HOPS; i++) { + uint8_t *nht_entry = &lpm->nht[i * lpm->entry_size]; + + if ((lpm->nht_users[i] > 0) && (memcmp(nht_entry, entry, + lpm->entry_unique_size) == 0)) { + *pos = i; + return 1; + } + } + + return 0; +} + +static int +rte_table_lpm_ipv6_entry_add( + void *table, + void *key, + void *entry, + int *key_found, + void **entry_ptr) +{ + struct rte_table_lpm_ipv6 *lpm = table; + struct rte_table_lpm_ipv6_key *ip_prefix = + key; + uint32_t nht_pos = 0, nht_pos0 = 0, nht_pos0_valid = 0; + int status; + + /* Check input parameters */ + if (lpm == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + if (ip_prefix == NULL) { + RTE_LOG(ERR, TABLE, "%s: ip_prefix parameter is NULL\n", + __func__); + return -EINVAL; + } + if (entry == NULL) { + RTE_LOG(ERR, TABLE, "%s: entry parameter is NULL\n", __func__); + return -EINVAL; + } + + if ((ip_prefix->depth == 0) || (ip_prefix->depth > 128)) { + RTE_LOG(ERR, TABLE, "%s: invalid depth (%d)\n", __func__, + ip_prefix->depth); + return -EINVAL; + } + + /* Check if rule is already present in the table */ + status = rte_lpm6_is_rule_present(lpm->lpm, ip_prefix->ip, + ip_prefix->depth, &nht_pos0); + nht_pos0_valid = status > 0; + + /* Find existing or free NHT entry */ + if (nht_find_existing(lpm, entry, &nht_pos) == 0) { + uint8_t *nht_entry; + + if (nht_find_free(lpm, &nht_pos) == 0) { + RTE_LOG(ERR, TABLE, "%s: NHT full\n", __func__); + return -1; + } + + nht_entry = &lpm->nht[nht_pos * lpm->entry_size]; + memcpy(nht_entry, entry, lpm->entry_size); + } + + /* Add rule to low level LPM table */ + if (rte_lpm6_add(lpm->lpm, ip_prefix->ip, ip_prefix->depth, + nht_pos) < 0) { + RTE_LOG(ERR, TABLE, "%s: LPM IPv6 rule add failed\n", __func__); + return -1; + } + + /* Commit NHT changes */ + lpm->nht_users[nht_pos]++; + lpm->nht_users[nht_pos0] -= nht_pos0_valid; + + *key_found = nht_pos0_valid; + *entry_ptr = (void *) &lpm->nht[nht_pos * lpm->entry_size]; + return 0; +} + +static int +rte_table_lpm_ipv6_entry_delete( + void *table, + void *key, + int *key_found, + void *entry) +{ + struct rte_table_lpm_ipv6 *lpm = table; + struct rte_table_lpm_ipv6_key *ip_prefix = + key; + uint32_t nht_pos; + int status; + + /* Check input parameters */ + if (lpm == NULL) { + RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__); + return -EINVAL; + } + if (ip_prefix == NULL) { + RTE_LOG(ERR, TABLE, "%s: ip_prefix parameter is NULL\n", + __func__); + return -EINVAL; + } + if ((ip_prefix->depth == 0) || (ip_prefix->depth > 128)) { + RTE_LOG(ERR, TABLE, "%s: invalid depth (%d)\n", __func__, + ip_prefix->depth); + return -EINVAL; + } + + /* Return if rule is not present in the table */ + status = rte_lpm6_is_rule_present(lpm->lpm, ip_prefix->ip, + ip_prefix->depth, &nht_pos); + if (status < 0) { + RTE_LOG(ERR, TABLE, "%s: LPM IPv6 algorithmic error\n", + __func__); + return -1; + } + if (status == 0) { + *key_found = 0; + return 0; + } + + /* Delete rule from the low-level LPM table */ + status = rte_lpm6_delete(lpm->lpm, ip_prefix->ip, ip_prefix->depth); + if (status) { + RTE_LOG(ERR, TABLE, "%s: LPM IPv6 rule delete failed\n", + __func__); + return -1; + } + + /* Commit NHT changes */ + lpm->nht_users[nht_pos]--; + + *key_found = 1; + if (entry) + memcpy(entry, &lpm->nht[nht_pos * lpm->entry_size], + lpm->entry_size); + + return 0; +} + +static int +rte_table_lpm_ipv6_lookup( + void *table, + struct rte_mbuf **pkts, + uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + void **entries) +{ + struct rte_table_lpm_ipv6 *lpm = (struct rte_table_lpm_ipv6 *) table; + uint64_t pkts_out_mask = 0; + uint32_t i; + + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + RTE_TABLE_LPM_IPV6_STATS_PKTS_IN_ADD(lpm, n_pkts_in); + + pkts_out_mask = 0; + for (i = 0; i < (uint32_t)(RTE_PORT_IN_BURST_SIZE_MAX - + __builtin_clzll(pkts_mask)); i++) { + uint64_t pkt_mask = 1LLU << i; + + if (pkt_mask & pkts_mask) { + struct rte_mbuf *pkt = pkts[i]; + uint8_t *ip = RTE_MBUF_METADATA_UINT8_PTR(pkt, + lpm->offset); + int status; + uint32_t nht_pos; + + status = rte_lpm6_lookup(lpm->lpm, ip, &nht_pos); + if (status == 0) { + pkts_out_mask |= pkt_mask; + entries[i] = (void *) &lpm->nht[nht_pos * + lpm->entry_size]; + } + } + } + + *lookup_hit_mask = pkts_out_mask; + RTE_TABLE_LPM_IPV6_STATS_PKTS_LOOKUP_MISS(lpm, n_pkts_in - __builtin_popcountll(pkts_out_mask)); + return 0; +} + +static int +rte_table_lpm_ipv6_stats_read(void *table, struct rte_table_stats *stats, int clear) +{ + struct rte_table_lpm_ipv6 *t = table; + + if (stats != NULL) + memcpy(stats, &t->stats, sizeof(t->stats)); + + if (clear) + memset(&t->stats, 0, sizeof(t->stats)); + + return 0; +} + +struct rte_table_ops rte_table_lpm_ipv6_ops = { + .f_create = rte_table_lpm_ipv6_create, + .f_free = rte_table_lpm_ipv6_free, + .f_add = rte_table_lpm_ipv6_entry_add, + .f_delete = rte_table_lpm_ipv6_entry_delete, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_lpm_ipv6_lookup, + .f_stats = rte_table_lpm_ipv6_stats_read, +}; diff --git a/lib/table/rte_table_lpm_ipv6.h b/lib/table/rte_table_lpm_ipv6.h new file mode 100644 index 0000000000..eadc316ee1 --- /dev/null +++ b/lib/table/rte_table_lpm_ipv6.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_TABLE_LPM_IPV6_H__ +#define __INCLUDE_RTE_TABLE_LPM_IPV6_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE Table LPM for IPv6 + * + * This table uses the Longest Prefix Match (LPM) algorithm to uniquely + * associate data to lookup keys. + * + * Use-case: IP routing table. Routes that are added to the table associate a + * next hop to an IP prefix. The IP prefix is specified as IP address and depth + * and cover for a multitude of lookup keys (i.e. destination IP addresses) + * that all share the same data (i.e. next hop). The next hop information + * typically contains the output interface ID, the IP address of the next hop + * station (which is part of the same IP network the output interface is + * connected to) and other flags and counters. + * + * The LPM primitive only allows associating an 8-bit number (next hop ID) to + * an IP prefix, while a routing table can potentially contain thousands of + * routes or even more. This means that the same next hop ID (and next hop + * information) has to be shared by multiple routes, which makes sense, as + * multiple remote networks could be reached through the same next hop. + * Therefore, when a route is added or updated, the LPM table has to check + * whether the same next hop is already in use before using a new next hop ID + * for this route. + * + * The comparison between different next hops is done for the first + * “entry_unique_size” bytes of the next hop information (configurable + * parameter), which have to uniquely identify the next hop, therefore the user + * has to carefully manage the format of the LPM table entry (i.e. the next + * hop information) so that any next hop data that changes value during + * run-time (e.g. counters) is placed outside of this area. + * + ***/ + +#include <stdint.h> + +#include "rte_table.h" + +#define RTE_LPM_IPV6_ADDR_SIZE 16 + +/** LPM table parameters */ +struct rte_table_lpm_ipv6_params { + /** Table name */ + const char *name; + + /** Maximum number of LPM rules (i.e. IP routes) */ + uint32_t n_rules; + + uint32_t number_tbl8s; + + /** Number of bytes at the start of the table entry that uniquely + identify the entry. Cannot be bigger than table entry size. */ + uint32_t entry_unique_size; + + /** Byte offset within input packet meta-data where lookup key (i.e. + the destination IP address) is located. */ + uint32_t offset; +}; + +/** LPM table rule (i.e. route), specified as IP prefix. While the key used by +the lookup operation is the destination IP address (read from the input packet +meta-data), the entry add and entry delete operations work with LPM rules, with +each rule covering for a multitude of lookup keys (destination IP addresses) +that share the same data (next hop). */ +struct rte_table_lpm_ipv6_key { + /** IP address */ + uint8_t ip[RTE_LPM_IPV6_ADDR_SIZE]; + + /** IP address depth. The most significant "depth" bits of the IP + address specify the network part of the IP address, while the rest of + the bits specify the host part of the address and are ignored for the + purpose of route specification. */ + uint8_t depth; +}; + +/** LPM table operations */ +extern struct rte_table_ops rte_table_lpm_ipv6_ops; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/rte_table_stub.c b/lib/table/rte_table_stub.c new file mode 100644 index 0000000000..9ce7be9cec --- /dev/null +++ b/lib/table/rte_table_stub.c @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#include <string.h> + +#include <rte_mbuf.h> +#include <rte_malloc.h> + +#include "rte_table_stub.h" + +#ifdef RTE_TABLE_STATS_COLLECT + +#define RTE_TABLE_LPM_STATS_PKTS_IN_ADD(table, val) \ + table->stats.n_pkts_in += val +#define RTE_TABLE_LPM_STATS_PKTS_LOOKUP_MISS(table, val) \ + table->stats.n_pkts_lookup_miss += val + +#else + +#define RTE_TABLE_LPM_STATS_PKTS_IN_ADD(table, val) +#define RTE_TABLE_LPM_STATS_PKTS_LOOKUP_MISS(table, val) + +#endif + +struct rte_table_stub { + struct rte_table_stats stats; +}; + +static void * +rte_table_stub_create(__rte_unused void *params, + __rte_unused int socket_id, + __rte_unused uint32_t entry_size) +{ + struct rte_table_stub *stub; + uint32_t size; + + size = sizeof(struct rte_table_stub); + stub = rte_zmalloc_socket("TABLE", size, RTE_CACHE_LINE_SIZE, + socket_id); + if (stub == NULL) { + RTE_LOG(ERR, TABLE, + "%s: Cannot allocate %u bytes for stub table\n", + __func__, size); + return NULL; + } + + return stub; +} + +static int +rte_table_stub_lookup( + __rte_unused void *table, + __rte_unused struct rte_mbuf **pkts, + __rte_unused uint64_t pkts_mask, + uint64_t *lookup_hit_mask, + __rte_unused void **entries) +{ + __rte_unused struct rte_table_stub *stub = (struct rte_table_stub *) table; + __rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask); + + RTE_TABLE_LPM_STATS_PKTS_IN_ADD(stub, n_pkts_in); + *lookup_hit_mask = 0; + RTE_TABLE_LPM_STATS_PKTS_LOOKUP_MISS(stub, n_pkts_in); + + return 0; +} + +static int +rte_table_stub_stats_read(void *table, struct rte_table_stats *stats, int clear) +{ + struct rte_table_stub *t = table; + + if (stats != NULL) + memcpy(stats, &t->stats, sizeof(t->stats)); + + if (clear) + memset(&t->stats, 0, sizeof(t->stats)); + + return 0; +} + +struct rte_table_ops rte_table_stub_ops = { + .f_create = rte_table_stub_create, + .f_free = NULL, + .f_add = NULL, + .f_delete = NULL, + .f_add_bulk = NULL, + .f_delete_bulk = NULL, + .f_lookup = rte_table_stub_lookup, + .f_stats = rte_table_stub_stats_read, +}; diff --git a/lib/table/rte_table_stub.h b/lib/table/rte_table_stub.h new file mode 100644 index 0000000000..2b40739790 --- /dev/null +++ b/lib/table/rte_table_stub.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#ifndef __INCLUDE_RTE_TABLE_STUB_H__ +#define __INCLUDE_RTE_TABLE_STUB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE Table Stub + * + * The stub table lookup operation produces lookup miss for all input packets. + * + ***/ + +#include <stdint.h> + +#include "rte_table.h" + +/** Stub table parameters: NONE */ + +/** Stub table operations */ +extern struct rte_table_ops rte_table_stub_ops; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/version.map b/lib/table/version.map new file mode 100644 index 0000000000..eb0291ac42 --- /dev/null +++ b/lib/table/version.map @@ -0,0 +1,31 @@ +DPDK_21 { + global: + + rte_table_acl_ops; + rte_table_array_ops; + rte_table_hash_cuckoo_ops; + rte_table_hash_ext_ops; + rte_table_hash_key16_ext_ops; + rte_table_hash_key16_lru_ops; + rte_table_hash_key32_ext_ops; + rte_table_hash_key32_lru_ops; + rte_table_hash_key8_ext_ops; + rte_table_hash_key8_lru_ops; + rte_table_hash_lru_ops; + rte_table_lpm_ipv6_ops; + rte_table_lpm_ops; + rte_table_stub_ops; + + local: *; +}; + +EXPERIMENTAL { + global: + + # added in 20.11 + rte_swx_table_exact_match_ops; + rte_swx_table_exact_match_unoptimized_ops; + + # added in 21.05 + rte_swx_table_wildcard_match_ops; +}; |
