xref: /dpdk/lib/table/rte_swx_table_learner.c (revision c6552d9a8deffa448de2d5e2e726f50508c1efd2)
10c06fa3bSCristian Dumitrescu /* SPDX-License-Identifier: BSD-3-Clause
20c06fa3bSCristian Dumitrescu  * Copyright(c) 2020 Intel Corporation
30c06fa3bSCristian Dumitrescu  */
40c06fa3bSCristian Dumitrescu #include <string.h>
50c06fa3bSCristian Dumitrescu #include <stdio.h>
60c06fa3bSCristian Dumitrescu #include <errno.h>
70c06fa3bSCristian Dumitrescu 
80c06fa3bSCristian Dumitrescu #include <rte_common.h>
90c06fa3bSCristian Dumitrescu #include <rte_cycles.h>
100c06fa3bSCristian Dumitrescu #include <rte_prefetch.h>
110d871d7eSCristian Dumitrescu #include <rte_jhash.h>
120d871d7eSCristian Dumitrescu #include <rte_hash_crc.h>
130c06fa3bSCristian Dumitrescu 
140d871d7eSCristian Dumitrescu #include "rte_swx_keycmp.h"
150c06fa3bSCristian Dumitrescu #include "rte_swx_table_learner.h"
160c06fa3bSCristian Dumitrescu 
170c06fa3bSCristian Dumitrescu #ifndef RTE_SWX_TABLE_LEARNER_USE_HUGE_PAGES
180c06fa3bSCristian Dumitrescu #define RTE_SWX_TABLE_LEARNER_USE_HUGE_PAGES 1
190c06fa3bSCristian Dumitrescu #endif
200c06fa3bSCristian Dumitrescu 
210c06fa3bSCristian Dumitrescu #ifndef RTE_SWX_TABLE_SELECTOR_HUGE_PAGES_DISABLE
220c06fa3bSCristian Dumitrescu 
230c06fa3bSCristian Dumitrescu #include <rte_malloc.h>
240c06fa3bSCristian Dumitrescu 
250c06fa3bSCristian Dumitrescu static void *
env_calloc(size_t size,size_t alignment,int numa_node)260c06fa3bSCristian Dumitrescu env_calloc(size_t size, size_t alignment, int numa_node)
270c06fa3bSCristian Dumitrescu {
280c06fa3bSCristian Dumitrescu 	return rte_zmalloc_socket(NULL, size, alignment, numa_node);
290c06fa3bSCristian Dumitrescu }
300c06fa3bSCristian Dumitrescu 
310c06fa3bSCristian Dumitrescu static void
env_free(void * start,size_t size __rte_unused)320c06fa3bSCristian Dumitrescu env_free(void *start, size_t size __rte_unused)
330c06fa3bSCristian Dumitrescu {
340c06fa3bSCristian Dumitrescu 	rte_free(start);
350c06fa3bSCristian Dumitrescu }
360c06fa3bSCristian Dumitrescu 
370c06fa3bSCristian Dumitrescu #else
380c06fa3bSCristian Dumitrescu 
390c06fa3bSCristian Dumitrescu #include <numa.h>
400c06fa3bSCristian Dumitrescu 
410c06fa3bSCristian Dumitrescu static void *
env_calloc(size_t size,size_t alignment __rte_unused,int numa_node)420c06fa3bSCristian Dumitrescu env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)
430c06fa3bSCristian Dumitrescu {
440c06fa3bSCristian Dumitrescu 	void *start;
450c06fa3bSCristian Dumitrescu 
460c06fa3bSCristian Dumitrescu 	if (numa_available() == -1)
470c06fa3bSCristian Dumitrescu 		return NULL;
480c06fa3bSCristian Dumitrescu 
490c06fa3bSCristian Dumitrescu 	start = numa_alloc_onnode(size, numa_node);
500c06fa3bSCristian Dumitrescu 	if (!start)
510c06fa3bSCristian Dumitrescu 		return NULL;
520c06fa3bSCristian Dumitrescu 
530c06fa3bSCristian Dumitrescu 	memset(start, 0, size);
540c06fa3bSCristian Dumitrescu 	return start;
550c06fa3bSCristian Dumitrescu }
560c06fa3bSCristian Dumitrescu 
570c06fa3bSCristian Dumitrescu static void
env_free(void * start,size_t size)580c06fa3bSCristian Dumitrescu env_free(void *start, size_t size)
590c06fa3bSCristian Dumitrescu {
600c06fa3bSCristian Dumitrescu 	if ((numa_available() == -1) || !start)
610c06fa3bSCristian Dumitrescu 		return;
620c06fa3bSCristian Dumitrescu 
630c06fa3bSCristian Dumitrescu 	numa_free(start, size);
640c06fa3bSCristian Dumitrescu }
650c06fa3bSCristian Dumitrescu 
660c06fa3bSCristian Dumitrescu #endif
670c06fa3bSCristian Dumitrescu 
688186c0bbSCristian Dumitrescu static void
table_keycpy(void * dst,void * src,uint32_t n_bytes)690d871d7eSCristian Dumitrescu table_keycpy(void *dst, void *src, uint32_t n_bytes)
708186c0bbSCristian Dumitrescu {
710d871d7eSCristian Dumitrescu 	memcpy(dst, src, n_bytes);
720c06fa3bSCristian Dumitrescu }
730c06fa3bSCristian Dumitrescu 
740c06fa3bSCristian Dumitrescu #define TABLE_KEYS_PER_BUCKET 4
75c1b4b26aSCristian Dumitrescu #define TABLE_KEYS_PER_BUCKET_LOG2 2
760c06fa3bSCristian Dumitrescu 
778186c0bbSCristian Dumitrescu #define TABLE_BUCKET_USEFUL_SIZE \
788186c0bbSCristian Dumitrescu 	(TABLE_KEYS_PER_BUCKET * (sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t)))
798186c0bbSCristian Dumitrescu 
800c06fa3bSCristian Dumitrescu #define TABLE_BUCKET_PAD_SIZE \
818186c0bbSCristian Dumitrescu 	(RTE_CACHE_LINE_SIZE - TABLE_BUCKET_USEFUL_SIZE)
820c06fa3bSCristian Dumitrescu 
830c06fa3bSCristian Dumitrescu struct table_bucket {
840c06fa3bSCristian Dumitrescu 	uint32_t time[TABLE_KEYS_PER_BUCKET];
850c06fa3bSCristian Dumitrescu 	uint32_t sig[TABLE_KEYS_PER_BUCKET];
868186c0bbSCristian Dumitrescu 	uint8_t key_timeout_id[TABLE_KEYS_PER_BUCKET];
870c06fa3bSCristian Dumitrescu 	uint8_t pad[TABLE_BUCKET_PAD_SIZE];
88013b4c52SBruce Richardson 	uint8_t key[];
890c06fa3bSCristian Dumitrescu };
900c06fa3bSCristian Dumitrescu 
910c06fa3bSCristian Dumitrescu struct table_params {
920c06fa3bSCristian Dumitrescu 	/* The real key size. Must be non-zero. */
930c06fa3bSCristian Dumitrescu 	size_t key_size;
940c06fa3bSCristian Dumitrescu 
950d871d7eSCristian Dumitrescu 	/* The key size upgrated to the next power of 2. */
960c06fa3bSCristian Dumitrescu 	size_t key_size_pow2;
970c06fa3bSCristian Dumitrescu 
980c06fa3bSCristian Dumitrescu 	/* log2(key_size_pow2). Purpose: avoid multiplication with non-power-of-2 numbers. */
990c06fa3bSCristian Dumitrescu 	size_t key_size_log2;
1000c06fa3bSCristian Dumitrescu 
1010c06fa3bSCristian Dumitrescu 	/* The key offset within the key buffer. */
1020c06fa3bSCristian Dumitrescu 	size_t key_offset;
1030c06fa3bSCristian Dumitrescu 
1040c06fa3bSCristian Dumitrescu 	/* The real action data size. */
1050c06fa3bSCristian Dumitrescu 	size_t action_data_size;
1060c06fa3bSCristian Dumitrescu 
1070c06fa3bSCristian Dumitrescu 	/* The data size, i.e. the 8-byte action_id field plus the action data size, upgraded to the
1080c06fa3bSCristian Dumitrescu 	 * next power of 2.
1090c06fa3bSCristian Dumitrescu 	 */
1100c06fa3bSCristian Dumitrescu 	size_t data_size_pow2;
1110c06fa3bSCristian Dumitrescu 
1120c06fa3bSCristian Dumitrescu 	/* log2(data_size_pow2). Purpose: avoid multiplication with non-power of 2 numbers. */
1130c06fa3bSCristian Dumitrescu 	size_t data_size_log2;
1140c06fa3bSCristian Dumitrescu 
1150c06fa3bSCristian Dumitrescu 	/* Number of buckets. Must be a power of 2 to avoid modulo with non-power-of-2 numbers. */
1160c06fa3bSCristian Dumitrescu 	size_t n_buckets;
1170c06fa3bSCristian Dumitrescu 
1180c06fa3bSCristian Dumitrescu 	/* Bucket mask. Purpose: replace modulo with bitmask and operation. */
1190c06fa3bSCristian Dumitrescu 	size_t bucket_mask;
1200c06fa3bSCristian Dumitrescu 
1210c06fa3bSCristian Dumitrescu 	/* Total number of key bytes in the bucket, including the key padding bytes. There are
1220c06fa3bSCristian Dumitrescu 	 * (key_size_pow2 - key_size) padding bytes for each key in the bucket.
1230c06fa3bSCristian Dumitrescu 	 */
1240c06fa3bSCristian Dumitrescu 	size_t bucket_key_all_size;
1250c06fa3bSCristian Dumitrescu 
1260c06fa3bSCristian Dumitrescu 	/* Bucket size. Must be a power of 2 to avoid multiplication with non-power-of-2 number. */
1270c06fa3bSCristian Dumitrescu 	size_t bucket_size;
1280c06fa3bSCristian Dumitrescu 
1290c06fa3bSCristian Dumitrescu 	/* log2(bucket_size). Purpose: avoid multiplication with non-power of 2 numbers. */
1300c06fa3bSCristian Dumitrescu 	size_t bucket_size_log2;
1310c06fa3bSCristian Dumitrescu 
1320d871d7eSCristian Dumitrescu 	/* Hash function. */
1330d871d7eSCristian Dumitrescu 	rte_swx_hash_func_t hash_func;
1340d871d7eSCristian Dumitrescu 
1350d871d7eSCristian Dumitrescu 	/* Key comparison function. */
1360d871d7eSCristian Dumitrescu 	rte_swx_keycmp_func_t keycmp_func;
1370d871d7eSCristian Dumitrescu 
1388186c0bbSCristian Dumitrescu 	/* Set of all possible key timeout values measured in CPU clock cycles. */
1398186c0bbSCristian Dumitrescu 	uint64_t key_timeout[RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX];
1408186c0bbSCristian Dumitrescu 
1418186c0bbSCristian Dumitrescu 	/* Number of key timeout values. */
1428186c0bbSCristian Dumitrescu 	uint32_t n_key_timeouts;
1430c06fa3bSCristian Dumitrescu 
1440c06fa3bSCristian Dumitrescu 	/* Total memory size. */
1450c06fa3bSCristian Dumitrescu 	size_t total_size;
1460c06fa3bSCristian Dumitrescu };
1470c06fa3bSCristian Dumitrescu 
148*c6552d9aSTyler Retzlaff struct __rte_cache_aligned table {
1490c06fa3bSCristian Dumitrescu 	/* Table parameters. */
1500c06fa3bSCristian Dumitrescu 	struct table_params params;
1510c06fa3bSCristian Dumitrescu 
1520c06fa3bSCristian Dumitrescu 	/* Table buckets. */
153013b4c52SBruce Richardson 	uint8_t buckets[];
154*c6552d9aSTyler Retzlaff };
1550c06fa3bSCristian Dumitrescu 
1568186c0bbSCristian Dumitrescu /* The timeout (in cycles) is stored in the table as a 32-bit value by truncating its least
1578186c0bbSCristian Dumitrescu  * significant 32 bits. Therefore, to make sure the time is always advancing when adding the timeout
1588186c0bbSCristian Dumitrescu  * value on top of the current time, the minimum timeout value is 1^32 cycles, which is 2 seconds on
1598186c0bbSCristian Dumitrescu  * a 2 GHz CPU.
1608186c0bbSCristian Dumitrescu  */
1618186c0bbSCristian Dumitrescu static uint64_t
timeout_convert(uint32_t timeout_in_seconds)1628186c0bbSCristian Dumitrescu timeout_convert(uint32_t timeout_in_seconds)
1638186c0bbSCristian Dumitrescu {
1648186c0bbSCristian Dumitrescu 	uint64_t timeout_in_cycles = timeout_in_seconds * rte_get_tsc_hz();
1658186c0bbSCristian Dumitrescu 
1668186c0bbSCristian Dumitrescu 	if (!(timeout_in_cycles >> 32))
1678186c0bbSCristian Dumitrescu 		timeout_in_cycles = 1LLU << 32;
1688186c0bbSCristian Dumitrescu 
1698186c0bbSCristian Dumitrescu 	return timeout_in_cycles;
1708186c0bbSCristian Dumitrescu }
1718186c0bbSCristian Dumitrescu 
1720c06fa3bSCristian Dumitrescu static int
table_params_get(struct table_params * p,struct rte_swx_table_learner_params * params)1730c06fa3bSCristian Dumitrescu table_params_get(struct table_params *p, struct rte_swx_table_learner_params *params)
1740c06fa3bSCristian Dumitrescu {
1758186c0bbSCristian Dumitrescu 	uint32_t i;
1768186c0bbSCristian Dumitrescu 
1770c06fa3bSCristian Dumitrescu 	/* Check input parameters. */
1780c06fa3bSCristian Dumitrescu 	if (!params ||
1790c06fa3bSCristian Dumitrescu 	    !params->key_size ||
1800c06fa3bSCristian Dumitrescu 	    !params->n_keys_max ||
1810c06fa3bSCristian Dumitrescu 	    (params->n_keys_max > 1U << 31) ||
1828186c0bbSCristian Dumitrescu 	    !params->key_timeout ||
1838186c0bbSCristian Dumitrescu 	    !params->n_key_timeouts ||
1848186c0bbSCristian Dumitrescu 	    (params->n_key_timeouts > RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX))
1858186c0bbSCristian Dumitrescu 		return -EINVAL;
1868186c0bbSCristian Dumitrescu 
1870d871d7eSCristian Dumitrescu 	if (params->key_mask0) {
1880d871d7eSCristian Dumitrescu 		for (i = 0; i < params->key_size; i++)
1890d871d7eSCristian Dumitrescu 			if (params->key_mask0[i] != 0xFF)
1900d871d7eSCristian Dumitrescu 				break;
1910d871d7eSCristian Dumitrescu 
1920d871d7eSCristian Dumitrescu 		if (i < params->key_size)
1930d871d7eSCristian Dumitrescu 			return -EINVAL;
1940d871d7eSCristian Dumitrescu 	}
1950d871d7eSCristian Dumitrescu 
1968186c0bbSCristian Dumitrescu 	for (i = 0; i < params->n_key_timeouts; i++)
1978186c0bbSCristian Dumitrescu 		if (!params->key_timeout[i])
1980c06fa3bSCristian Dumitrescu 			return -EINVAL;
1990c06fa3bSCristian Dumitrescu 
2000c06fa3bSCristian Dumitrescu 	/* Key. */
2010c06fa3bSCristian Dumitrescu 	p->key_size = params->key_size;
2020c06fa3bSCristian Dumitrescu 
2030c06fa3bSCristian Dumitrescu 	p->key_size_pow2 = rte_align64pow2(p->key_size);
2040c06fa3bSCristian Dumitrescu 
2053d4e27fdSDavid Marchand 	p->key_size_log2 = rte_ctz64(p->key_size_pow2);
2060c06fa3bSCristian Dumitrescu 
2070c06fa3bSCristian Dumitrescu 	p->key_offset = params->key_offset;
2080c06fa3bSCristian Dumitrescu 
2090c06fa3bSCristian Dumitrescu 	/* Data. */
2100c06fa3bSCristian Dumitrescu 	p->action_data_size = params->action_data_size;
2110c06fa3bSCristian Dumitrescu 
2120c06fa3bSCristian Dumitrescu 	p->data_size_pow2 = rte_align64pow2(sizeof(uint64_t) + p->action_data_size);
2130c06fa3bSCristian Dumitrescu 
2143d4e27fdSDavid Marchand 	p->data_size_log2 = rte_ctz64(p->data_size_pow2);
2150c06fa3bSCristian Dumitrescu 
2160c06fa3bSCristian Dumitrescu 	/* Buckets. */
2170c06fa3bSCristian Dumitrescu 	p->n_buckets = rte_align32pow2(params->n_keys_max);
2180c06fa3bSCristian Dumitrescu 
2190c06fa3bSCristian Dumitrescu 	p->bucket_mask = p->n_buckets - 1;
2200c06fa3bSCristian Dumitrescu 
2210c06fa3bSCristian Dumitrescu 	p->bucket_key_all_size = TABLE_KEYS_PER_BUCKET * p->key_size_pow2;
2220c06fa3bSCristian Dumitrescu 
2230c06fa3bSCristian Dumitrescu 	p->bucket_size = rte_align64pow2(sizeof(struct table_bucket) +
2240c06fa3bSCristian Dumitrescu 					 p->bucket_key_all_size +
2250c06fa3bSCristian Dumitrescu 					 TABLE_KEYS_PER_BUCKET * p->data_size_pow2);
2260c06fa3bSCristian Dumitrescu 
2273d4e27fdSDavid Marchand 	p->bucket_size_log2 = rte_ctz64(p->bucket_size);
2280c06fa3bSCristian Dumitrescu 
2290d871d7eSCristian Dumitrescu 	p->hash_func = params->hash_func ? params->hash_func : rte_hash_crc;
2300d871d7eSCristian Dumitrescu 
2310d871d7eSCristian Dumitrescu 	p->keycmp_func = rte_swx_keycmp_func_get(params->key_size);
2320d871d7eSCristian Dumitrescu 
2330c06fa3bSCristian Dumitrescu 	/* Timeout. */
2348186c0bbSCristian Dumitrescu 	for (i = 0; i < params->n_key_timeouts; i++)
2358186c0bbSCristian Dumitrescu 		p->key_timeout[i] = timeout_convert(params->key_timeout[i]);
2368186c0bbSCristian Dumitrescu 
2378186c0bbSCristian Dumitrescu 	p->n_key_timeouts = rte_align32pow2(params->n_key_timeouts);
2388186c0bbSCristian Dumitrescu 
2398186c0bbSCristian Dumitrescu 	for ( ; i < p->n_key_timeouts; i++)
2408186c0bbSCristian Dumitrescu 		p->key_timeout[i] = p->key_timeout[0];
2410c06fa3bSCristian Dumitrescu 
2420c06fa3bSCristian Dumitrescu 	/* Total size. */
2430c06fa3bSCristian Dumitrescu 	p->total_size = sizeof(struct table) + p->n_buckets * p->bucket_size;
2440c06fa3bSCristian Dumitrescu 
2450c06fa3bSCristian Dumitrescu 	return 0;
2460c06fa3bSCristian Dumitrescu }
2470c06fa3bSCristian Dumitrescu 
2480c06fa3bSCristian Dumitrescu static inline struct table_bucket *
table_bucket_get(struct table * t,size_t bucket_id)2490c06fa3bSCristian Dumitrescu table_bucket_get(struct table *t, size_t bucket_id)
2500c06fa3bSCristian Dumitrescu {
2510c06fa3bSCristian Dumitrescu 	return (struct table_bucket *)&t->buckets[bucket_id << t->params.bucket_size_log2];
2520c06fa3bSCristian Dumitrescu }
2530c06fa3bSCristian Dumitrescu 
2540c06fa3bSCristian Dumitrescu static inline uint8_t *
table_bucket_key_get(struct table * t,struct table_bucket * b,size_t bucket_key_pos)2550c06fa3bSCristian Dumitrescu table_bucket_key_get(struct table *t, struct table_bucket *b, size_t bucket_key_pos)
2560c06fa3bSCristian Dumitrescu {
2570c06fa3bSCristian Dumitrescu 	return &b->key[bucket_key_pos << t->params.key_size_log2];
2580c06fa3bSCristian Dumitrescu }
2590c06fa3bSCristian Dumitrescu 
2600c06fa3bSCristian Dumitrescu static inline uint64_t *
table_bucket_data_get(struct table * t,struct table_bucket * b,size_t bucket_key_pos)2610c06fa3bSCristian Dumitrescu table_bucket_data_get(struct table *t, struct table_bucket *b, size_t bucket_key_pos)
2620c06fa3bSCristian Dumitrescu {
2630c06fa3bSCristian Dumitrescu 	return (uint64_t *)&b->key[t->params.bucket_key_all_size +
2640c06fa3bSCristian Dumitrescu 				   (bucket_key_pos << t->params.data_size_log2)];
2650c06fa3bSCristian Dumitrescu }
2660c06fa3bSCristian Dumitrescu 
267c1b4b26aSCristian Dumitrescu static inline size_t
table_entry_id_get(struct table * t,struct table_bucket * b,size_t bucket_key_pos)268c1b4b26aSCristian Dumitrescu table_entry_id_get(struct table *t, struct table_bucket *b, size_t bucket_key_pos)
269c1b4b26aSCristian Dumitrescu {
270c1b4b26aSCristian Dumitrescu 	size_t bucket_id = ((uint8_t *)b - t->buckets) >> t->params.bucket_size_log2;
271c1b4b26aSCristian Dumitrescu 
272c1b4b26aSCristian Dumitrescu 	return (bucket_id << TABLE_KEYS_PER_BUCKET_LOG2) + bucket_key_pos;
273c1b4b26aSCristian Dumitrescu }
274c1b4b26aSCristian Dumitrescu 
2750c06fa3bSCristian Dumitrescu uint64_t
rte_swx_table_learner_footprint_get(struct rte_swx_table_learner_params * params)2760c06fa3bSCristian Dumitrescu rte_swx_table_learner_footprint_get(struct rte_swx_table_learner_params *params)
2770c06fa3bSCristian Dumitrescu {
2780c06fa3bSCristian Dumitrescu 	struct table_params p;
2790c06fa3bSCristian Dumitrescu 	int status;
2800c06fa3bSCristian Dumitrescu 
2810c06fa3bSCristian Dumitrescu 	status = table_params_get(&p, params);
2820c06fa3bSCristian Dumitrescu 
2830c06fa3bSCristian Dumitrescu 	return status ? 0 : p.total_size;
2840c06fa3bSCristian Dumitrescu }
2850c06fa3bSCristian Dumitrescu 
2860c06fa3bSCristian Dumitrescu void *
rte_swx_table_learner_create(struct rte_swx_table_learner_params * params,int numa_node)2870c06fa3bSCristian Dumitrescu rte_swx_table_learner_create(struct rte_swx_table_learner_params *params, int numa_node)
2880c06fa3bSCristian Dumitrescu {
2890c06fa3bSCristian Dumitrescu 	struct table_params p;
2900c06fa3bSCristian Dumitrescu 	struct table *t;
2910c06fa3bSCristian Dumitrescu 	int status;
2920c06fa3bSCristian Dumitrescu 
2930c06fa3bSCristian Dumitrescu 	/* Check and process the input parameters. */
2940c06fa3bSCristian Dumitrescu 	status = table_params_get(&p, params);
2950c06fa3bSCristian Dumitrescu 	if (status)
2960c06fa3bSCristian Dumitrescu 		return NULL;
2970c06fa3bSCristian Dumitrescu 
2980c06fa3bSCristian Dumitrescu 	/* Memory allocation. */
2990c06fa3bSCristian Dumitrescu 	t = env_calloc(p.total_size, RTE_CACHE_LINE_SIZE, numa_node);
3000c06fa3bSCristian Dumitrescu 	if (!t)
3010c06fa3bSCristian Dumitrescu 		return NULL;
3020c06fa3bSCristian Dumitrescu 
3030c06fa3bSCristian Dumitrescu 	/* Memory initialization. */
3040c06fa3bSCristian Dumitrescu 	memcpy(&t->params, &p, sizeof(struct table_params));
3050c06fa3bSCristian Dumitrescu 
3060c06fa3bSCristian Dumitrescu 	return t;
3070c06fa3bSCristian Dumitrescu }
3080c06fa3bSCristian Dumitrescu 
3090c06fa3bSCristian Dumitrescu void
rte_swx_table_learner_free(void * table)3100c06fa3bSCristian Dumitrescu rte_swx_table_learner_free(void *table)
3110c06fa3bSCristian Dumitrescu {
3120c06fa3bSCristian Dumitrescu 	struct table *t = table;
3130c06fa3bSCristian Dumitrescu 
3140c06fa3bSCristian Dumitrescu 	if (!t)
3150c06fa3bSCristian Dumitrescu 		return;
3160c06fa3bSCristian Dumitrescu 
3170c06fa3bSCristian Dumitrescu 	env_free(t, t->params.total_size);
3180c06fa3bSCristian Dumitrescu }
3190c06fa3bSCristian Dumitrescu 
3208186c0bbSCristian Dumitrescu int
rte_swx_table_learner_timeout_update(void * table,uint32_t key_timeout_id,uint32_t key_timeout)3218186c0bbSCristian Dumitrescu rte_swx_table_learner_timeout_update(void *table,
3228186c0bbSCristian Dumitrescu 				     uint32_t key_timeout_id,
3238186c0bbSCristian Dumitrescu 				     uint32_t key_timeout)
3248186c0bbSCristian Dumitrescu {
3258186c0bbSCristian Dumitrescu 	struct table *t = table;
3268186c0bbSCristian Dumitrescu 
3278186c0bbSCristian Dumitrescu 	if (!t ||
3288186c0bbSCristian Dumitrescu 	    (key_timeout_id >= t->params.n_key_timeouts) ||
3298186c0bbSCristian Dumitrescu 	    !key_timeout)
3308186c0bbSCristian Dumitrescu 		return -EINVAL;
3318186c0bbSCristian Dumitrescu 
3328186c0bbSCristian Dumitrescu 	t->params.key_timeout[key_timeout_id] = timeout_convert(key_timeout);
3338186c0bbSCristian Dumitrescu 
3348186c0bbSCristian Dumitrescu 	return 0;
3358186c0bbSCristian Dumitrescu }
3368186c0bbSCristian Dumitrescu 
3370c06fa3bSCristian Dumitrescu struct mailbox {
3380c06fa3bSCristian Dumitrescu 	/* Writer: lookup state 0. Reader(s): lookup state 1, add(). */
3390c06fa3bSCristian Dumitrescu 	struct table_bucket *bucket;
3400c06fa3bSCristian Dumitrescu 
3410c06fa3bSCristian Dumitrescu 	/* Writer: lookup state 0. Reader(s): lookup state 1, add(). */
3420c06fa3bSCristian Dumitrescu 	uint32_t input_sig;
3430c06fa3bSCristian Dumitrescu 
344c1b4b26aSCristian Dumitrescu 	/* Writer: lookup state 0. Reader(s): lookup state 1, add(). */
3450c06fa3bSCristian Dumitrescu 	uint8_t *input_key;
3460c06fa3bSCristian Dumitrescu 
3470c06fa3bSCristian Dumitrescu 	/* Writer: lookup state 1. Reader(s): add(). Values: 0 = miss; 1 = hit. */
3480c06fa3bSCristian Dumitrescu 	uint32_t hit;
3490c06fa3bSCristian Dumitrescu 
3500c06fa3bSCristian Dumitrescu 	/* Writer: lookup state 1. Reader(s): add(). Valid only when hit is non-zero. */
3510c06fa3bSCristian Dumitrescu 	size_t bucket_key_pos;
3520c06fa3bSCristian Dumitrescu 
3530c06fa3bSCristian Dumitrescu 	/* State. */
3540c06fa3bSCristian Dumitrescu 	int state;
3550c06fa3bSCristian Dumitrescu };
3560c06fa3bSCristian Dumitrescu 
3570c06fa3bSCristian Dumitrescu uint64_t
rte_swx_table_learner_mailbox_size_get(void)3580c06fa3bSCristian Dumitrescu rte_swx_table_learner_mailbox_size_get(void)
3590c06fa3bSCristian Dumitrescu {
3600c06fa3bSCristian Dumitrescu 	return sizeof(struct mailbox);
3610c06fa3bSCristian Dumitrescu }
3620c06fa3bSCristian Dumitrescu 
3630c06fa3bSCristian Dumitrescu int
rte_swx_table_learner_lookup(void * table,void * mailbox,uint64_t input_time,uint8_t ** key,uint64_t * action_id,uint8_t ** action_data,size_t * entry_id,int * hit)3640c06fa3bSCristian Dumitrescu rte_swx_table_learner_lookup(void *table,
3650c06fa3bSCristian Dumitrescu 			     void *mailbox,
3660c06fa3bSCristian Dumitrescu 			     uint64_t input_time,
3670c06fa3bSCristian Dumitrescu 			     uint8_t **key,
3680c06fa3bSCristian Dumitrescu 			     uint64_t *action_id,
3690c06fa3bSCristian Dumitrescu 			     uint8_t **action_data,
370c1b4b26aSCristian Dumitrescu 			     size_t *entry_id,
3710c06fa3bSCristian Dumitrescu 			     int *hit)
3720c06fa3bSCristian Dumitrescu {
3730c06fa3bSCristian Dumitrescu 	struct table *t = table;
3740c06fa3bSCristian Dumitrescu 	struct mailbox *m = mailbox;
3750c06fa3bSCristian Dumitrescu 
3760c06fa3bSCristian Dumitrescu 	switch (m->state) {
3770c06fa3bSCristian Dumitrescu 	case 0: {
3780c06fa3bSCristian Dumitrescu 		uint8_t *input_key;
3790c06fa3bSCristian Dumitrescu 		struct table_bucket *b;
3800c06fa3bSCristian Dumitrescu 		size_t bucket_id;
3810c06fa3bSCristian Dumitrescu 		uint32_t input_sig;
3820c06fa3bSCristian Dumitrescu 
3830c06fa3bSCristian Dumitrescu 		input_key = &(*key)[t->params.key_offset];
3840d871d7eSCristian Dumitrescu 		input_sig = t->params.hash_func(input_key, t->params.key_size, 0);
3850c06fa3bSCristian Dumitrescu 		bucket_id = input_sig & t->params.bucket_mask;
3860c06fa3bSCristian Dumitrescu 		b = table_bucket_get(t, bucket_id);
3870c06fa3bSCristian Dumitrescu 
3880c06fa3bSCristian Dumitrescu 		rte_prefetch0(b);
3890c06fa3bSCristian Dumitrescu 		rte_prefetch0(&b->key[0]);
3900c06fa3bSCristian Dumitrescu 		rte_prefetch0(&b->key[RTE_CACHE_LINE_SIZE]);
3910c06fa3bSCristian Dumitrescu 
3920c06fa3bSCristian Dumitrescu 		m->bucket = b;
3930c06fa3bSCristian Dumitrescu 		m->input_key = input_key;
3940c06fa3bSCristian Dumitrescu 		m->input_sig = input_sig | 1;
3950c06fa3bSCristian Dumitrescu 		m->state = 1;
3960c06fa3bSCristian Dumitrescu 		return 0;
3970c06fa3bSCristian Dumitrescu 	}
3980c06fa3bSCristian Dumitrescu 
3990c06fa3bSCristian Dumitrescu 	case 1: {
4000c06fa3bSCristian Dumitrescu 		struct table_bucket *b = m->bucket;
4010c06fa3bSCristian Dumitrescu 		uint32_t i;
4020c06fa3bSCristian Dumitrescu 
4030c06fa3bSCristian Dumitrescu 		/* Search the input key through the bucket keys. */
4040c06fa3bSCristian Dumitrescu 		for (i = 0; i < TABLE_KEYS_PER_BUCKET; i++) {
4050c06fa3bSCristian Dumitrescu 			uint64_t time = b->time[i];
4060c06fa3bSCristian Dumitrescu 			uint32_t sig = b->sig[i];
4070c06fa3bSCristian Dumitrescu 			uint8_t *key = table_bucket_key_get(t, b, i);
4080c06fa3bSCristian Dumitrescu 
4090c06fa3bSCristian Dumitrescu 			time <<= 32;
4100c06fa3bSCristian Dumitrescu 
4110c06fa3bSCristian Dumitrescu 			if ((time > input_time) &&
4120c06fa3bSCristian Dumitrescu 			    (sig == m->input_sig) &&
4130d871d7eSCristian Dumitrescu 			    t->params.keycmp_func(key, m->input_key, t->params.key_size)) {
4140c06fa3bSCristian Dumitrescu 				uint64_t *data = table_bucket_data_get(t, b, i);
4150c06fa3bSCristian Dumitrescu 
4160c06fa3bSCristian Dumitrescu 				/* Hit. */
4170c06fa3bSCristian Dumitrescu 				rte_prefetch0(data);
4180c06fa3bSCristian Dumitrescu 
4190c06fa3bSCristian Dumitrescu 				m->hit = 1;
4200c06fa3bSCristian Dumitrescu 				m->bucket_key_pos = i;
4210c06fa3bSCristian Dumitrescu 				m->state = 0;
4220c06fa3bSCristian Dumitrescu 
4230c06fa3bSCristian Dumitrescu 				*action_id = data[0];
4240c06fa3bSCristian Dumitrescu 				*action_data = (uint8_t *)&data[1];
425c1b4b26aSCristian Dumitrescu 				*entry_id = table_entry_id_get(t, b, i);
4260c06fa3bSCristian Dumitrescu 				*hit = 1;
4270c06fa3bSCristian Dumitrescu 				return 1;
4280c06fa3bSCristian Dumitrescu 			}
4290c06fa3bSCristian Dumitrescu 		}
4300c06fa3bSCristian Dumitrescu 
4310c06fa3bSCristian Dumitrescu 		/* Miss. */
4320c06fa3bSCristian Dumitrescu 		m->hit = 0;
4330c06fa3bSCristian Dumitrescu 		m->state = 0;
4340c06fa3bSCristian Dumitrescu 
4350c06fa3bSCristian Dumitrescu 		*hit = 0;
4360c06fa3bSCristian Dumitrescu 		return 1;
4370c06fa3bSCristian Dumitrescu 	}
4380c06fa3bSCristian Dumitrescu 
4390c06fa3bSCristian Dumitrescu 	default:
4400c06fa3bSCristian Dumitrescu 		/* This state should never be reached. Miss. */
4410c06fa3bSCristian Dumitrescu 		m->hit = 0;
4420c06fa3bSCristian Dumitrescu 		m->state = 0;
4430c06fa3bSCristian Dumitrescu 
4440c06fa3bSCristian Dumitrescu 		*hit = 0;
4450c06fa3bSCristian Dumitrescu 		return 1;
4460c06fa3bSCristian Dumitrescu 	}
4470c06fa3bSCristian Dumitrescu }
4480c06fa3bSCristian Dumitrescu 
4498186c0bbSCristian Dumitrescu void
rte_swx_table_learner_rearm(void * table,void * mailbox,uint64_t input_time)4508186c0bbSCristian Dumitrescu rte_swx_table_learner_rearm(void *table,
4518186c0bbSCristian Dumitrescu 			    void *mailbox,
4528186c0bbSCristian Dumitrescu 			    uint64_t input_time)
4538186c0bbSCristian Dumitrescu {
4548186c0bbSCristian Dumitrescu 	struct table *t = table;
4558186c0bbSCristian Dumitrescu 	struct mailbox *m = mailbox;
4568186c0bbSCristian Dumitrescu 	struct table_bucket *b;
4578186c0bbSCristian Dumitrescu 	size_t bucket_key_pos;
4588186c0bbSCristian Dumitrescu 	uint64_t key_timeout;
4598186c0bbSCristian Dumitrescu 	uint32_t key_timeout_id;
4608186c0bbSCristian Dumitrescu 
4618186c0bbSCristian Dumitrescu 	if (!m->hit)
4628186c0bbSCristian Dumitrescu 		return;
4638186c0bbSCristian Dumitrescu 
4648186c0bbSCristian Dumitrescu 	b = m->bucket;
4658186c0bbSCristian Dumitrescu 	bucket_key_pos = m->bucket_key_pos;
4668186c0bbSCristian Dumitrescu 
4678186c0bbSCristian Dumitrescu 	key_timeout_id = b->key_timeout_id[bucket_key_pos];
4688186c0bbSCristian Dumitrescu 	key_timeout = t->params.key_timeout[key_timeout_id];
4698186c0bbSCristian Dumitrescu 	b->time[bucket_key_pos] = (input_time + key_timeout) >> 32;
4708186c0bbSCristian Dumitrescu }
4718186c0bbSCristian Dumitrescu 
4728186c0bbSCristian Dumitrescu void
rte_swx_table_learner_rearm_new(void * table,void * mailbox,uint64_t input_time,uint32_t key_timeout_id)4738186c0bbSCristian Dumitrescu rte_swx_table_learner_rearm_new(void *table,
4748186c0bbSCristian Dumitrescu 				void *mailbox,
4758186c0bbSCristian Dumitrescu 				uint64_t input_time,
4768186c0bbSCristian Dumitrescu 				uint32_t key_timeout_id)
4778186c0bbSCristian Dumitrescu {
4788186c0bbSCristian Dumitrescu 	struct table *t = table;
4798186c0bbSCristian Dumitrescu 	struct mailbox *m = mailbox;
4808186c0bbSCristian Dumitrescu 	struct table_bucket *b;
4818186c0bbSCristian Dumitrescu 	size_t bucket_key_pos;
4828186c0bbSCristian Dumitrescu 	uint64_t key_timeout;
4838186c0bbSCristian Dumitrescu 
4848186c0bbSCristian Dumitrescu 	if (!m->hit)
4858186c0bbSCristian Dumitrescu 		return;
4868186c0bbSCristian Dumitrescu 
4878186c0bbSCristian Dumitrescu 	b = m->bucket;
4888186c0bbSCristian Dumitrescu 	bucket_key_pos = m->bucket_key_pos;
4898186c0bbSCristian Dumitrescu 
4908186c0bbSCristian Dumitrescu 	key_timeout_id &= t->params.n_key_timeouts - 1;
4918186c0bbSCristian Dumitrescu 	key_timeout = t->params.key_timeout[key_timeout_id];
4928186c0bbSCristian Dumitrescu 	b->time[bucket_key_pos] = (input_time + key_timeout) >> 32;
4938186c0bbSCristian Dumitrescu 	b->key_timeout_id[bucket_key_pos] = (uint8_t)key_timeout_id;
4948186c0bbSCristian Dumitrescu }
4958186c0bbSCristian Dumitrescu 
4960c06fa3bSCristian Dumitrescu uint32_t
rte_swx_table_learner_add(void * table,void * mailbox,uint64_t input_time,uint64_t action_id,uint8_t * action_data,uint32_t key_timeout_id)4970c06fa3bSCristian Dumitrescu rte_swx_table_learner_add(void *table,
4980c06fa3bSCristian Dumitrescu 			  void *mailbox,
4990c06fa3bSCristian Dumitrescu 			  uint64_t input_time,
5000c06fa3bSCristian Dumitrescu 			  uint64_t action_id,
5018186c0bbSCristian Dumitrescu 			  uint8_t *action_data,
5028186c0bbSCristian Dumitrescu 			  uint32_t key_timeout_id)
5030c06fa3bSCristian Dumitrescu {
5040c06fa3bSCristian Dumitrescu 	struct table *t = table;
5050c06fa3bSCristian Dumitrescu 	struct mailbox *m = mailbox;
5060c06fa3bSCristian Dumitrescu 	struct table_bucket *b = m->bucket;
5078186c0bbSCristian Dumitrescu 	uint64_t key_timeout;
5080c06fa3bSCristian Dumitrescu 	uint32_t i;
5090c06fa3bSCristian Dumitrescu 
5108186c0bbSCristian Dumitrescu 	/* Adjust the key timeout ID to fit the valid range. */
5118186c0bbSCristian Dumitrescu 	key_timeout_id &= t->params.n_key_timeouts - 1;
5128186c0bbSCristian Dumitrescu 	key_timeout = t->params.key_timeout[key_timeout_id];
5138186c0bbSCristian Dumitrescu 
5148186c0bbSCristian Dumitrescu 	/* Lookup hit: The following bucket fields need to be updated:
5158186c0bbSCristian Dumitrescu 	 * - key (key, sig): NO (already correctly set).
5168186c0bbSCristian Dumitrescu 	 * - key timeout (key_timeout_id, time): YES.
5178186c0bbSCristian Dumitrescu 	 * - key data (data): YES.
5180c06fa3bSCristian Dumitrescu 	 */
5190c06fa3bSCristian Dumitrescu 	if (m->hit) {
5208186c0bbSCristian Dumitrescu 		size_t bucket_key_pos = m->bucket_key_pos;
5218186c0bbSCristian Dumitrescu 		uint64_t *data = table_bucket_data_get(t, b, bucket_key_pos);
5228186c0bbSCristian Dumitrescu 
5238186c0bbSCristian Dumitrescu 		/* Install the key timeout. */
5248186c0bbSCristian Dumitrescu 		b->time[bucket_key_pos] = (input_time + key_timeout) >> 32;
5258186c0bbSCristian Dumitrescu 		b->key_timeout_id[bucket_key_pos] = (uint8_t)key_timeout_id;
5260c06fa3bSCristian Dumitrescu 
5270c06fa3bSCristian Dumitrescu 		/* Install the key data. */
5280c06fa3bSCristian Dumitrescu 		data[0] = action_id;
5290c06fa3bSCristian Dumitrescu 		if (t->params.action_data_size && action_data)
5300c06fa3bSCristian Dumitrescu 			memcpy(&data[1], action_data, t->params.action_data_size);
5310c06fa3bSCristian Dumitrescu 
5320c06fa3bSCristian Dumitrescu 		return 0;
5330c06fa3bSCristian Dumitrescu 	}
5340c06fa3bSCristian Dumitrescu 
5350c06fa3bSCristian Dumitrescu 	/* Lookup miss: Search for a free position in the current bucket and install the key. */
5360c06fa3bSCristian Dumitrescu 	for (i = 0; i < TABLE_KEYS_PER_BUCKET; i++) {
5370c06fa3bSCristian Dumitrescu 		uint64_t time = b->time[i];
5380c06fa3bSCristian Dumitrescu 
5390c06fa3bSCristian Dumitrescu 		time <<= 32;
5400c06fa3bSCristian Dumitrescu 
5410c06fa3bSCristian Dumitrescu 		/* Free position: Either there was never a key installed here, so the key time is
5420c06fa3bSCristian Dumitrescu 		 * set to zero (the init value), which is always less than the current time, or this
5430c06fa3bSCristian Dumitrescu 		 * position was used before, but the key expired (the key time is in the past).
5440c06fa3bSCristian Dumitrescu 		 */
5450c06fa3bSCristian Dumitrescu 		if (time < input_time) {
5460c06fa3bSCristian Dumitrescu 			uint8_t *key = table_bucket_key_get(t, b, i);
5470c06fa3bSCristian Dumitrescu 			uint64_t *data = table_bucket_data_get(t, b, i);
5480c06fa3bSCristian Dumitrescu 
5498186c0bbSCristian Dumitrescu 			/* Install the key and the key timeout. */
5508186c0bbSCristian Dumitrescu 			b->time[i] = (input_time + key_timeout) >> 32;
5510c06fa3bSCristian Dumitrescu 			b->sig[i] = m->input_sig;
5528186c0bbSCristian Dumitrescu 			b->key_timeout_id[i] = (uint8_t)key_timeout_id;
5530d871d7eSCristian Dumitrescu 			table_keycpy(key, m->input_key, t->params.key_size);
5540c06fa3bSCristian Dumitrescu 
5550c06fa3bSCristian Dumitrescu 			/* Install the key data. */
5560c06fa3bSCristian Dumitrescu 			data[0] = action_id;
5570c06fa3bSCristian Dumitrescu 			if (t->params.action_data_size && action_data)
5580c06fa3bSCristian Dumitrescu 				memcpy(&data[1], action_data, t->params.action_data_size);
5590c06fa3bSCristian Dumitrescu 
5600c06fa3bSCristian Dumitrescu 			/* Mailbox. */
5610c06fa3bSCristian Dumitrescu 			m->hit = 1;
5620c06fa3bSCristian Dumitrescu 			m->bucket_key_pos = i;
5630c06fa3bSCristian Dumitrescu 
5640c06fa3bSCristian Dumitrescu 			return 0;
5650c06fa3bSCristian Dumitrescu 		}
5660c06fa3bSCristian Dumitrescu 	}
5670c06fa3bSCristian Dumitrescu 
5680c06fa3bSCristian Dumitrescu 	/* Bucket full. */
5690c06fa3bSCristian Dumitrescu 	return 1;
5700c06fa3bSCristian Dumitrescu }
5710c06fa3bSCristian Dumitrescu 
5720c06fa3bSCristian Dumitrescu void
rte_swx_table_learner_delete(void * table __rte_unused,void * mailbox)5730c06fa3bSCristian Dumitrescu rte_swx_table_learner_delete(void *table __rte_unused,
5740c06fa3bSCristian Dumitrescu 			     void *mailbox)
5750c06fa3bSCristian Dumitrescu {
5760c06fa3bSCristian Dumitrescu 	struct mailbox *m = mailbox;
5770c06fa3bSCristian Dumitrescu 
5780c06fa3bSCristian Dumitrescu 	if (m->hit) {
5790c06fa3bSCristian Dumitrescu 		struct table_bucket *b = m->bucket;
5800c06fa3bSCristian Dumitrescu 
5810c06fa3bSCristian Dumitrescu 		/* Expire the key. */
5820c06fa3bSCristian Dumitrescu 		b->time[m->bucket_key_pos] = 0;
5830c06fa3bSCristian Dumitrescu 
5840c06fa3bSCristian Dumitrescu 		/* Mailbox. */
5850c06fa3bSCristian Dumitrescu 		m->hit = 0;
5860c06fa3bSCristian Dumitrescu 	}
5870c06fa3bSCristian Dumitrescu }
588