xref: /dpdk/lib/lpm/rte_lpm6.c (revision e1a06e391ba74f9c4d46a6ecef6d8ee084f4229e)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2010-2014 Intel Corporation
399a2dd95SBruce Richardson  */
499a2dd95SBruce Richardson #include <string.h>
5e9fd1ebfSTyler Retzlaff #include <stdalign.h>
699a2dd95SBruce Richardson #include <stdint.h>
799a2dd95SBruce Richardson #include <errno.h>
899a2dd95SBruce Richardson #include <stdio.h>
999a2dd95SBruce Richardson #include <sys/queue.h>
1099a2dd95SBruce Richardson 
1199a2dd95SBruce Richardson #include <rte_log.h>
1299a2dd95SBruce Richardson #include <rte_common.h>
1399a2dd95SBruce Richardson #include <rte_malloc.h>
1499a2dd95SBruce Richardson #include <rte_memcpy.h>
1599a2dd95SBruce Richardson #include <rte_eal_memconfig.h>
1699a2dd95SBruce Richardson #include <rte_string_fns.h>
1799a2dd95SBruce Richardson #include <rte_errno.h>
1899a2dd95SBruce Richardson #include <rte_hash.h>
1999a2dd95SBruce Richardson #include <assert.h>
2099a2dd95SBruce Richardson #include <rte_jhash.h>
2199a2dd95SBruce Richardson #include <rte_tailq.h>
2299a2dd95SBruce Richardson 
2399a2dd95SBruce Richardson #include "rte_lpm6.h"
24fdb83ffbSStephen Hemminger #include "lpm_log.h"
2599a2dd95SBruce Richardson 
2699a2dd95SBruce Richardson #define RTE_LPM6_TBL24_NUM_ENTRIES        (1 << 24)
2799a2dd95SBruce Richardson #define RTE_LPM6_TBL8_GROUP_NUM_ENTRIES         256
2899a2dd95SBruce Richardson #define RTE_LPM6_TBL8_MAX_NUM_GROUPS      (1 << 21)
2999a2dd95SBruce Richardson 
3099a2dd95SBruce Richardson #define RTE_LPM6_VALID_EXT_ENTRY_BITMASK 0xA0000000
3199a2dd95SBruce Richardson #define RTE_LPM6_LOOKUP_SUCCESS          0x20000000
3299a2dd95SBruce Richardson #define RTE_LPM6_TBL8_BITMASK            0x001FFFFF
3399a2dd95SBruce Richardson 
3499a2dd95SBruce Richardson #define ADD_FIRST_BYTE                            3
3599a2dd95SBruce Richardson #define LOOKUP_FIRST_BYTE                         4
3699a2dd95SBruce Richardson #define BYTE_SIZE                                 8
3799a2dd95SBruce Richardson #define BYTES2_SIZE                              16
3899a2dd95SBruce Richardson 
3999a2dd95SBruce Richardson #define RULE_HASH_TABLE_EXTRA_SPACE              64
4099a2dd95SBruce Richardson #define TBL24_IND                        UINT32_MAX
4199a2dd95SBruce Richardson 
4299a2dd95SBruce Richardson #define lpm6_tbl8_gindex next_hop
4399a2dd95SBruce Richardson 
4499a2dd95SBruce Richardson /** Flags for setting an entry as valid/invalid. */
4599a2dd95SBruce Richardson enum valid_flag {
4699a2dd95SBruce Richardson 	INVALID = 0,
4799a2dd95SBruce Richardson 	VALID
4899a2dd95SBruce Richardson };
4999a2dd95SBruce Richardson 
5099a2dd95SBruce Richardson TAILQ_HEAD(rte_lpm6_list, rte_tailq_entry);
5199a2dd95SBruce Richardson 
5299a2dd95SBruce Richardson static struct rte_tailq_elem rte_lpm6_tailq = {
5399a2dd95SBruce Richardson 	.name = "RTE_LPM6",
5499a2dd95SBruce Richardson };
5599a2dd95SBruce Richardson EAL_REGISTER_TAILQ(rte_lpm6_tailq)
5699a2dd95SBruce Richardson 
5799a2dd95SBruce Richardson /** Tbl entry structure. It is the same for both tbl24 and tbl8 */
5899a2dd95SBruce Richardson struct rte_lpm6_tbl_entry {
5999a2dd95SBruce Richardson 	uint32_t next_hop:	21;  /**< Next hop / next table to be checked. */
6099a2dd95SBruce Richardson 	uint32_t depth	:8;      /**< Rule depth. */
6199a2dd95SBruce Richardson 
6299a2dd95SBruce Richardson 	/* Flags. */
6399a2dd95SBruce Richardson 	uint32_t valid     :1;   /**< Validation flag. */
6499a2dd95SBruce Richardson 	uint32_t valid_group :1; /**< Group validation flag. */
6599a2dd95SBruce Richardson 	uint32_t ext_entry :1;   /**< External entry. */
6699a2dd95SBruce Richardson };
6799a2dd95SBruce Richardson 
6899a2dd95SBruce Richardson /** Rules tbl entry structure. */
6999a2dd95SBruce Richardson struct rte_lpm6_rule {
70*e1a06e39SRobin Jarry 	struct rte_ipv6_addr ip; /**< Rule IP address. */
7199a2dd95SBruce Richardson 	uint32_t next_hop; /**< Rule next hop. */
7299a2dd95SBruce Richardson 	uint8_t depth; /**< Rule depth. */
7399a2dd95SBruce Richardson };
7499a2dd95SBruce Richardson 
7599a2dd95SBruce Richardson /** Rules tbl entry key. */
7699a2dd95SBruce Richardson struct rte_lpm6_rule_key {
77*e1a06e39SRobin Jarry 	struct rte_ipv6_addr ip; /**< Rule IP address. */
78b16ac536SVladimir Medvedkin 	uint32_t depth; /**< Rule depth. */
7999a2dd95SBruce Richardson };
8099a2dd95SBruce Richardson 
8199a2dd95SBruce Richardson /* Header of tbl8 */
8299a2dd95SBruce Richardson struct rte_lpm_tbl8_hdr {
8399a2dd95SBruce Richardson 	uint32_t owner_tbl_ind; /**< owner table: TBL24_IND if owner is tbl24,
8499a2dd95SBruce Richardson 				  *  otherwise index of tbl8
8599a2dd95SBruce Richardson 				  */
8699a2dd95SBruce Richardson 	uint32_t owner_entry_ind; /**< index of the owner table entry where
8799a2dd95SBruce Richardson 				    *  pointer to the tbl8 is stored
8899a2dd95SBruce Richardson 				    */
8999a2dd95SBruce Richardson 	uint32_t ref_cnt; /**< table reference counter */
9099a2dd95SBruce Richardson };
9199a2dd95SBruce Richardson 
9299a2dd95SBruce Richardson /** LPM6 structure. */
9399a2dd95SBruce Richardson struct rte_lpm6 {
9499a2dd95SBruce Richardson 	/* LPM metadata. */
9599a2dd95SBruce Richardson 	char name[RTE_LPM6_NAMESIZE];    /**< Name of the lpm. */
9699a2dd95SBruce Richardson 	uint32_t max_rules;              /**< Max number of rules. */
9799a2dd95SBruce Richardson 	uint32_t used_rules;             /**< Used rules so far. */
9899a2dd95SBruce Richardson 	uint32_t number_tbl8s;           /**< Number of tbl8s to allocate. */
9999a2dd95SBruce Richardson 
10099a2dd95SBruce Richardson 	/* LPM Tables. */
10199a2dd95SBruce Richardson 	struct rte_hash *rules_tbl; /**< LPM rules. */
102e9fd1ebfSTyler Retzlaff 	alignas(RTE_CACHE_LINE_SIZE) struct rte_lpm6_tbl_entry tbl24[RTE_LPM6_TBL24_NUM_ENTRIES];
103e9fd1ebfSTyler Retzlaff 			/**< LPM tbl24 table. */
10499a2dd95SBruce Richardson 
10599a2dd95SBruce Richardson 	uint32_t *tbl8_pool; /**< pool of indexes of free tbl8s */
10699a2dd95SBruce Richardson 	uint32_t tbl8_pool_pos; /**< current position in the tbl8 pool */
10799a2dd95SBruce Richardson 
10899a2dd95SBruce Richardson 	struct rte_lpm_tbl8_hdr *tbl8_hdrs; /* array of tbl8 headers */
10999a2dd95SBruce Richardson 
11093112c15STyler Retzlaff 	alignas(RTE_CACHE_LINE_SIZE) struct rte_lpm6_tbl_entry tbl8[];
111e9fd1ebfSTyler Retzlaff 			/**< LPM tbl8 table. */
11299a2dd95SBruce Richardson };
11399a2dd95SBruce Richardson 
11499a2dd95SBruce Richardson /*
11599a2dd95SBruce Richardson  * LPM6 rule hash function
11699a2dd95SBruce Richardson  *
11799a2dd95SBruce Richardson  * It's used as a hash function for the rte_hash
11899a2dd95SBruce Richardson  *	containing rules
11999a2dd95SBruce Richardson  */
12099a2dd95SBruce Richardson static inline uint32_t
12199a2dd95SBruce Richardson rule_hash(const void *data, __rte_unused uint32_t data_len,
12299a2dd95SBruce Richardson 		  uint32_t init_val)
12399a2dd95SBruce Richardson {
12499a2dd95SBruce Richardson 	return rte_jhash(data, sizeof(struct rte_lpm6_rule_key), init_val);
12599a2dd95SBruce Richardson }
12699a2dd95SBruce Richardson 
12799a2dd95SBruce Richardson /*
12899a2dd95SBruce Richardson  * Init pool of free tbl8 indexes
12999a2dd95SBruce Richardson  */
13099a2dd95SBruce Richardson static void
13199a2dd95SBruce Richardson tbl8_pool_init(struct rte_lpm6 *lpm)
13299a2dd95SBruce Richardson {
13399a2dd95SBruce Richardson 	uint32_t i;
13499a2dd95SBruce Richardson 
13599a2dd95SBruce Richardson 	/* put entire range of indexes to the tbl8 pool */
13699a2dd95SBruce Richardson 	for (i = 0; i < lpm->number_tbl8s; i++)
13799a2dd95SBruce Richardson 		lpm->tbl8_pool[i] = i;
13899a2dd95SBruce Richardson 
13999a2dd95SBruce Richardson 	lpm->tbl8_pool_pos = 0;
14099a2dd95SBruce Richardson }
14199a2dd95SBruce Richardson 
14299a2dd95SBruce Richardson /*
14399a2dd95SBruce Richardson  * Get an index of a free tbl8 from the pool
14499a2dd95SBruce Richardson  */
14599a2dd95SBruce Richardson static inline uint32_t
14699a2dd95SBruce Richardson tbl8_get(struct rte_lpm6 *lpm, uint32_t *tbl8_ind)
14799a2dd95SBruce Richardson {
14899a2dd95SBruce Richardson 	if (lpm->tbl8_pool_pos == lpm->number_tbl8s)
14999a2dd95SBruce Richardson 		/* no more free tbl8 */
15099a2dd95SBruce Richardson 		return -ENOSPC;
15199a2dd95SBruce Richardson 
15299a2dd95SBruce Richardson 	/* next index */
15399a2dd95SBruce Richardson 	*tbl8_ind = lpm->tbl8_pool[lpm->tbl8_pool_pos++];
15499a2dd95SBruce Richardson 	return 0;
15599a2dd95SBruce Richardson }
15699a2dd95SBruce Richardson 
15799a2dd95SBruce Richardson /*
15899a2dd95SBruce Richardson  * Put an index of a free tbl8 back to the pool
15999a2dd95SBruce Richardson  */
16099a2dd95SBruce Richardson static inline uint32_t
16199a2dd95SBruce Richardson tbl8_put(struct rte_lpm6 *lpm, uint32_t tbl8_ind)
16299a2dd95SBruce Richardson {
16399a2dd95SBruce Richardson 	if (lpm->tbl8_pool_pos == 0)
16499a2dd95SBruce Richardson 		/* pool is full */
16599a2dd95SBruce Richardson 		return -ENOSPC;
16699a2dd95SBruce Richardson 
16799a2dd95SBruce Richardson 	lpm->tbl8_pool[--lpm->tbl8_pool_pos] = tbl8_ind;
16899a2dd95SBruce Richardson 	return 0;
16999a2dd95SBruce Richardson }
17099a2dd95SBruce Richardson 
17199a2dd95SBruce Richardson /*
17299a2dd95SBruce Richardson  * Returns number of tbl8s available in the pool
17399a2dd95SBruce Richardson  */
17499a2dd95SBruce Richardson static inline uint32_t
17599a2dd95SBruce Richardson tbl8_available(struct rte_lpm6 *lpm)
17699a2dd95SBruce Richardson {
17799a2dd95SBruce Richardson 	return lpm->number_tbl8s - lpm->tbl8_pool_pos;
17899a2dd95SBruce Richardson }
17999a2dd95SBruce Richardson 
18099a2dd95SBruce Richardson /*
18199a2dd95SBruce Richardson  * Init a rule key.
18299a2dd95SBruce Richardson  *	  note that ip must be already masked
18399a2dd95SBruce Richardson  */
18499a2dd95SBruce Richardson static inline void
185*e1a06e39SRobin Jarry rule_key_init(struct rte_lpm6_rule_key *key, const struct rte_ipv6_addr *ip, uint8_t depth)
18699a2dd95SBruce Richardson {
187*e1a06e39SRobin Jarry 	key->ip = *ip;
18899a2dd95SBruce Richardson 	key->depth = depth;
18999a2dd95SBruce Richardson }
19099a2dd95SBruce Richardson 
19199a2dd95SBruce Richardson /*
19299a2dd95SBruce Richardson  * Rebuild the entire LPM tree by reinserting all rules
19399a2dd95SBruce Richardson  */
19499a2dd95SBruce Richardson static void
19599a2dd95SBruce Richardson rebuild_lpm(struct rte_lpm6 *lpm)
19699a2dd95SBruce Richardson {
19799a2dd95SBruce Richardson 	uint64_t next_hop;
19899a2dd95SBruce Richardson 	struct rte_lpm6_rule_key *rule_key;
19999a2dd95SBruce Richardson 	uint32_t iter = 0;
20099a2dd95SBruce Richardson 
20199a2dd95SBruce Richardson 	while (rte_hash_iterate(lpm->rules_tbl, (void *) &rule_key,
20299a2dd95SBruce Richardson 			(void **) &next_hop, &iter) >= 0)
203*e1a06e39SRobin Jarry 		rte_lpm6_add(lpm, &rule_key->ip, rule_key->depth,
20499a2dd95SBruce Richardson 			(uint32_t) next_hop);
20599a2dd95SBruce Richardson }
20699a2dd95SBruce Richardson 
20799a2dd95SBruce Richardson /*
20899a2dd95SBruce Richardson  * Allocates memory for LPM object
20999a2dd95SBruce Richardson  */
21099a2dd95SBruce Richardson struct rte_lpm6 *
21199a2dd95SBruce Richardson rte_lpm6_create(const char *name, int socket_id,
21299a2dd95SBruce Richardson 		const struct rte_lpm6_config *config)
21399a2dd95SBruce Richardson {
21499a2dd95SBruce Richardson 	char mem_name[RTE_LPM6_NAMESIZE];
21599a2dd95SBruce Richardson 	struct rte_lpm6 *lpm = NULL;
21699a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
21799a2dd95SBruce Richardson 	uint64_t mem_size;
21899a2dd95SBruce Richardson 	struct rte_lpm6_list *lpm_list;
21999a2dd95SBruce Richardson 	struct rte_hash *rules_tbl = NULL;
22099a2dd95SBruce Richardson 	uint32_t *tbl8_pool = NULL;
22199a2dd95SBruce Richardson 	struct rte_lpm_tbl8_hdr *tbl8_hdrs = NULL;
22299a2dd95SBruce Richardson 
22399a2dd95SBruce Richardson 	lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list);
22499a2dd95SBruce Richardson 
22599a2dd95SBruce Richardson 	RTE_BUILD_BUG_ON(sizeof(struct rte_lpm6_tbl_entry) != sizeof(uint32_t));
226b16ac536SVladimir Medvedkin 	RTE_BUILD_BUG_ON(sizeof(struct rte_lpm6_rule_key) %
227b16ac536SVladimir Medvedkin 		sizeof(uint32_t) != 0);
22899a2dd95SBruce Richardson 
22999a2dd95SBruce Richardson 	/* Check user arguments. */
23099a2dd95SBruce Richardson 	if ((name == NULL) || (socket_id < -1) || (config == NULL) ||
23199a2dd95SBruce Richardson 			(config->max_rules == 0) ||
23299a2dd95SBruce Richardson 			config->number_tbl8s > RTE_LPM6_TBL8_MAX_NUM_GROUPS) {
23399a2dd95SBruce Richardson 		rte_errno = EINVAL;
23499a2dd95SBruce Richardson 		return NULL;
23599a2dd95SBruce Richardson 	}
23699a2dd95SBruce Richardson 
23799a2dd95SBruce Richardson 	/* create rules hash table */
23899a2dd95SBruce Richardson 	snprintf(mem_name, sizeof(mem_name), "LRH_%s", name);
23999a2dd95SBruce Richardson 	struct rte_hash_parameters rule_hash_tbl_params = {
24099a2dd95SBruce Richardson 		.entries = config->max_rules * 1.2 +
24199a2dd95SBruce Richardson 			RULE_HASH_TABLE_EXTRA_SPACE,
24299a2dd95SBruce Richardson 		.key_len = sizeof(struct rte_lpm6_rule_key),
24399a2dd95SBruce Richardson 		.hash_func = rule_hash,
24499a2dd95SBruce Richardson 		.hash_func_init_val = 0,
24599a2dd95SBruce Richardson 		.name = mem_name,
24699a2dd95SBruce Richardson 		.reserved = 0,
24799a2dd95SBruce Richardson 		.socket_id = socket_id,
24899a2dd95SBruce Richardson 		.extra_flag = 0
24999a2dd95SBruce Richardson 	};
25099a2dd95SBruce Richardson 
25199a2dd95SBruce Richardson 	rules_tbl = rte_hash_create(&rule_hash_tbl_params);
25299a2dd95SBruce Richardson 	if (rules_tbl == NULL) {
253ae67895bSDavid Marchand 		LPM_LOG(ERR, "LPM rules hash table allocation failed: %s (%d)",
25499a2dd95SBruce Richardson 				  rte_strerror(rte_errno), rte_errno);
25599a2dd95SBruce Richardson 		goto fail_wo_unlock;
25699a2dd95SBruce Richardson 	}
25799a2dd95SBruce Richardson 
25899a2dd95SBruce Richardson 	/* allocate tbl8 indexes pool */
25999a2dd95SBruce Richardson 	tbl8_pool = rte_malloc(NULL,
26099a2dd95SBruce Richardson 			sizeof(uint32_t) * config->number_tbl8s,
26199a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE);
26299a2dd95SBruce Richardson 	if (tbl8_pool == NULL) {
263ae67895bSDavid Marchand 		LPM_LOG(ERR, "LPM tbl8 pool allocation failed: %s (%d)",
26499a2dd95SBruce Richardson 				  rte_strerror(rte_errno), rte_errno);
26599a2dd95SBruce Richardson 		rte_errno = ENOMEM;
26699a2dd95SBruce Richardson 		goto fail_wo_unlock;
26799a2dd95SBruce Richardson 	}
26899a2dd95SBruce Richardson 
26999a2dd95SBruce Richardson 	/* allocate tbl8 headers */
27099a2dd95SBruce Richardson 	tbl8_hdrs = rte_malloc(NULL,
27199a2dd95SBruce Richardson 			sizeof(struct rte_lpm_tbl8_hdr) * config->number_tbl8s,
27299a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE);
27399a2dd95SBruce Richardson 	if (tbl8_hdrs == NULL) {
274ae67895bSDavid Marchand 		LPM_LOG(ERR, "LPM tbl8 headers allocation failed: %s (%d)",
27599a2dd95SBruce Richardson 				  rte_strerror(rte_errno), rte_errno);
27699a2dd95SBruce Richardson 		rte_errno = ENOMEM;
27799a2dd95SBruce Richardson 		goto fail_wo_unlock;
27899a2dd95SBruce Richardson 	}
27999a2dd95SBruce Richardson 
28099a2dd95SBruce Richardson 	snprintf(mem_name, sizeof(mem_name), "LPM_%s", name);
28199a2dd95SBruce Richardson 
28299a2dd95SBruce Richardson 	/* Determine the amount of memory to allocate. */
28399a2dd95SBruce Richardson 	mem_size = sizeof(*lpm) + (sizeof(lpm->tbl8[0]) *
28499a2dd95SBruce Richardson 			RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * config->number_tbl8s);
28599a2dd95SBruce Richardson 
28699a2dd95SBruce Richardson 	rte_mcfg_tailq_write_lock();
28799a2dd95SBruce Richardson 
28899a2dd95SBruce Richardson 	/* Guarantee there's no existing */
28999a2dd95SBruce Richardson 	TAILQ_FOREACH(te, lpm_list, next) {
29099a2dd95SBruce Richardson 		lpm = (struct rte_lpm6 *) te->data;
29199a2dd95SBruce Richardson 		if (strncmp(name, lpm->name, RTE_LPM6_NAMESIZE) == 0)
29299a2dd95SBruce Richardson 			break;
29399a2dd95SBruce Richardson 	}
29499a2dd95SBruce Richardson 	lpm = NULL;
29599a2dd95SBruce Richardson 	if (te != NULL) {
29699a2dd95SBruce Richardson 		rte_errno = EEXIST;
29799a2dd95SBruce Richardson 		goto fail;
29899a2dd95SBruce Richardson 	}
29999a2dd95SBruce Richardson 
30099a2dd95SBruce Richardson 	/* allocate tailq entry */
30199a2dd95SBruce Richardson 	te = rte_zmalloc("LPM6_TAILQ_ENTRY", sizeof(*te), 0);
30299a2dd95SBruce Richardson 	if (te == NULL) {
303ae67895bSDavid Marchand 		LPM_LOG(ERR, "Failed to allocate tailq entry!");
30499a2dd95SBruce Richardson 		rte_errno = ENOMEM;
30599a2dd95SBruce Richardson 		goto fail;
30699a2dd95SBruce Richardson 	}
30799a2dd95SBruce Richardson 
30899a2dd95SBruce Richardson 	/* Allocate memory to store the LPM data structures. */
30999a2dd95SBruce Richardson 	lpm = rte_zmalloc_socket(mem_name, (size_t)mem_size,
31099a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE, socket_id);
31199a2dd95SBruce Richardson 
31299a2dd95SBruce Richardson 	if (lpm == NULL) {
313ae67895bSDavid Marchand 		LPM_LOG(ERR, "LPM memory allocation failed");
31499a2dd95SBruce Richardson 		rte_free(te);
31599a2dd95SBruce Richardson 		rte_errno = ENOMEM;
31699a2dd95SBruce Richardson 		goto fail;
31799a2dd95SBruce Richardson 	}
31899a2dd95SBruce Richardson 
31999a2dd95SBruce Richardson 	/* Save user arguments. */
32099a2dd95SBruce Richardson 	lpm->max_rules = config->max_rules;
32199a2dd95SBruce Richardson 	lpm->number_tbl8s = config->number_tbl8s;
32299a2dd95SBruce Richardson 	strlcpy(lpm->name, name, sizeof(lpm->name));
32399a2dd95SBruce Richardson 	lpm->rules_tbl = rules_tbl;
32499a2dd95SBruce Richardson 	lpm->tbl8_pool = tbl8_pool;
32599a2dd95SBruce Richardson 	lpm->tbl8_hdrs = tbl8_hdrs;
32699a2dd95SBruce Richardson 
32799a2dd95SBruce Richardson 	/* init the stack */
32899a2dd95SBruce Richardson 	tbl8_pool_init(lpm);
32999a2dd95SBruce Richardson 
33099a2dd95SBruce Richardson 	te->data = (void *) lpm;
33199a2dd95SBruce Richardson 
33299a2dd95SBruce Richardson 	TAILQ_INSERT_TAIL(lpm_list, te, next);
33399a2dd95SBruce Richardson 	rte_mcfg_tailq_write_unlock();
33499a2dd95SBruce Richardson 	return lpm;
33599a2dd95SBruce Richardson 
33699a2dd95SBruce Richardson fail:
33799a2dd95SBruce Richardson 	rte_mcfg_tailq_write_unlock();
33899a2dd95SBruce Richardson 
33999a2dd95SBruce Richardson fail_wo_unlock:
34099a2dd95SBruce Richardson 	rte_free(tbl8_hdrs);
34199a2dd95SBruce Richardson 	rte_free(tbl8_pool);
34299a2dd95SBruce Richardson 	rte_hash_free(rules_tbl);
34399a2dd95SBruce Richardson 
34499a2dd95SBruce Richardson 	return NULL;
34599a2dd95SBruce Richardson }
34699a2dd95SBruce Richardson 
34799a2dd95SBruce Richardson /*
34899a2dd95SBruce Richardson  * Find an existing lpm table and return a pointer to it.
34999a2dd95SBruce Richardson  */
35099a2dd95SBruce Richardson struct rte_lpm6 *
35199a2dd95SBruce Richardson rte_lpm6_find_existing(const char *name)
35299a2dd95SBruce Richardson {
35399a2dd95SBruce Richardson 	struct rte_lpm6 *l = NULL;
35499a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
35599a2dd95SBruce Richardson 	struct rte_lpm6_list *lpm_list;
35699a2dd95SBruce Richardson 
35799a2dd95SBruce Richardson 	lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list);
35899a2dd95SBruce Richardson 
35999a2dd95SBruce Richardson 	rte_mcfg_tailq_read_lock();
36099a2dd95SBruce Richardson 	TAILQ_FOREACH(te, lpm_list, next) {
36199a2dd95SBruce Richardson 		l = (struct rte_lpm6 *) te->data;
36299a2dd95SBruce Richardson 		if (strncmp(name, l->name, RTE_LPM6_NAMESIZE) == 0)
36399a2dd95SBruce Richardson 			break;
36499a2dd95SBruce Richardson 	}
36599a2dd95SBruce Richardson 	rte_mcfg_tailq_read_unlock();
36699a2dd95SBruce Richardson 
36799a2dd95SBruce Richardson 	if (te == NULL) {
36899a2dd95SBruce Richardson 		rte_errno = ENOENT;
36999a2dd95SBruce Richardson 		return NULL;
37099a2dd95SBruce Richardson 	}
37199a2dd95SBruce Richardson 
37299a2dd95SBruce Richardson 	return l;
37399a2dd95SBruce Richardson }
37499a2dd95SBruce Richardson 
37599a2dd95SBruce Richardson /*
37699a2dd95SBruce Richardson  * Deallocates memory for given LPM table.
37799a2dd95SBruce Richardson  */
37899a2dd95SBruce Richardson void
37999a2dd95SBruce Richardson rte_lpm6_free(struct rte_lpm6 *lpm)
38099a2dd95SBruce Richardson {
38199a2dd95SBruce Richardson 	struct rte_lpm6_list *lpm_list;
38299a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
38399a2dd95SBruce Richardson 
38499a2dd95SBruce Richardson 	/* Check user arguments. */
38599a2dd95SBruce Richardson 	if (lpm == NULL)
38699a2dd95SBruce Richardson 		return;
38799a2dd95SBruce Richardson 
38899a2dd95SBruce Richardson 	lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list);
38999a2dd95SBruce Richardson 
39099a2dd95SBruce Richardson 	rte_mcfg_tailq_write_lock();
39199a2dd95SBruce Richardson 
39299a2dd95SBruce Richardson 	/* find our tailq entry */
39399a2dd95SBruce Richardson 	TAILQ_FOREACH(te, lpm_list, next) {
39499a2dd95SBruce Richardson 		if (te->data == (void *) lpm)
39599a2dd95SBruce Richardson 			break;
39699a2dd95SBruce Richardson 	}
39799a2dd95SBruce Richardson 
39899a2dd95SBruce Richardson 	if (te != NULL)
39999a2dd95SBruce Richardson 		TAILQ_REMOVE(lpm_list, te, next);
40099a2dd95SBruce Richardson 
40199a2dd95SBruce Richardson 	rte_mcfg_tailq_write_unlock();
40299a2dd95SBruce Richardson 
40399a2dd95SBruce Richardson 	rte_free(lpm->tbl8_hdrs);
40499a2dd95SBruce Richardson 	rte_free(lpm->tbl8_pool);
40599a2dd95SBruce Richardson 	rte_hash_free(lpm->rules_tbl);
40699a2dd95SBruce Richardson 	rte_free(lpm);
40799a2dd95SBruce Richardson 	rte_free(te);
40899a2dd95SBruce Richardson }
40999a2dd95SBruce Richardson 
41099a2dd95SBruce Richardson /* Find a rule */
41199a2dd95SBruce Richardson static inline int
41299a2dd95SBruce Richardson rule_find_with_key(struct rte_lpm6 *lpm,
41399a2dd95SBruce Richardson 		  const struct rte_lpm6_rule_key *rule_key,
41499a2dd95SBruce Richardson 		  uint32_t *next_hop)
41599a2dd95SBruce Richardson {
41699a2dd95SBruce Richardson 	uint64_t hash_val;
41799a2dd95SBruce Richardson 	int ret;
41899a2dd95SBruce Richardson 
41999a2dd95SBruce Richardson 	/* lookup for a rule */
42099a2dd95SBruce Richardson 	ret = rte_hash_lookup_data(lpm->rules_tbl, (const void *) rule_key,
42199a2dd95SBruce Richardson 		(void **) &hash_val);
42299a2dd95SBruce Richardson 	if (ret >= 0) {
42399a2dd95SBruce Richardson 		*next_hop = (uint32_t) hash_val;
42499a2dd95SBruce Richardson 		return 1;
42599a2dd95SBruce Richardson 	}
42699a2dd95SBruce Richardson 
42799a2dd95SBruce Richardson 	return 0;
42899a2dd95SBruce Richardson }
42999a2dd95SBruce Richardson 
43099a2dd95SBruce Richardson /* Find a rule */
43199a2dd95SBruce Richardson static int
432*e1a06e39SRobin Jarry rule_find(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth,
43399a2dd95SBruce Richardson 		  uint32_t *next_hop)
43499a2dd95SBruce Richardson {
43599a2dd95SBruce Richardson 	struct rte_lpm6_rule_key rule_key;
43699a2dd95SBruce Richardson 
43799a2dd95SBruce Richardson 	/* init a rule key */
43899a2dd95SBruce Richardson 	rule_key_init(&rule_key, ip, depth);
43999a2dd95SBruce Richardson 
44099a2dd95SBruce Richardson 	return rule_find_with_key(lpm, &rule_key, next_hop);
44199a2dd95SBruce Richardson }
44299a2dd95SBruce Richardson 
44399a2dd95SBruce Richardson /*
44499a2dd95SBruce Richardson  * Checks if a rule already exists in the rules table and updates
44599a2dd95SBruce Richardson  * the nexthop if so. Otherwise it adds a new rule if enough space is available.
44699a2dd95SBruce Richardson  *
44799a2dd95SBruce Richardson  * Returns:
44899a2dd95SBruce Richardson  *    0 - next hop of existed rule is updated
44999a2dd95SBruce Richardson  *    1 - new rule successfully added
45099a2dd95SBruce Richardson  *   <0 - error
45199a2dd95SBruce Richardson  */
45299a2dd95SBruce Richardson static inline int
453*e1a06e39SRobin Jarry rule_add(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth, uint32_t next_hop)
45499a2dd95SBruce Richardson {
45599a2dd95SBruce Richardson 	int ret, rule_exist;
45699a2dd95SBruce Richardson 	struct rte_lpm6_rule_key rule_key;
45799a2dd95SBruce Richardson 	uint32_t unused;
45899a2dd95SBruce Richardson 
45999a2dd95SBruce Richardson 	/* init a rule key */
46099a2dd95SBruce Richardson 	rule_key_init(&rule_key, ip, depth);
46199a2dd95SBruce Richardson 
46299a2dd95SBruce Richardson 	/* Scan through rule list to see if rule already exists. */
46399a2dd95SBruce Richardson 	rule_exist = rule_find_with_key(lpm, &rule_key, &unused);
46499a2dd95SBruce Richardson 
46599a2dd95SBruce Richardson 	/*
46699a2dd95SBruce Richardson 	 * If rule does not exist check if there is space to add a new rule to
46799a2dd95SBruce Richardson 	 * this rule group. If there is no space return error.
46899a2dd95SBruce Richardson 	 */
46999a2dd95SBruce Richardson 	if (!rule_exist && lpm->used_rules == lpm->max_rules)
47099a2dd95SBruce Richardson 		return -ENOSPC;
47199a2dd95SBruce Richardson 
47299a2dd95SBruce Richardson 	/* add the rule or update rules next hop */
47399a2dd95SBruce Richardson 	ret = rte_hash_add_key_data(lpm->rules_tbl, &rule_key,
47499a2dd95SBruce Richardson 		(void *)(uintptr_t) next_hop);
47599a2dd95SBruce Richardson 	if (ret < 0)
47699a2dd95SBruce Richardson 		return ret;
47799a2dd95SBruce Richardson 
47899a2dd95SBruce Richardson 	/* Increment the used rules counter for this rule group. */
47999a2dd95SBruce Richardson 	if (!rule_exist) {
48099a2dd95SBruce Richardson 		lpm->used_rules++;
48199a2dd95SBruce Richardson 		return 1;
48299a2dd95SBruce Richardson 	}
48399a2dd95SBruce Richardson 
48499a2dd95SBruce Richardson 	return 0;
48599a2dd95SBruce Richardson }
48699a2dd95SBruce Richardson 
48799a2dd95SBruce Richardson /*
48899a2dd95SBruce Richardson  * Function that expands a rule across the data structure when a less-generic
48999a2dd95SBruce Richardson  * one has been added before. It assures that every possible combination of bits
49099a2dd95SBruce Richardson  * in the IP address returns a match.
49199a2dd95SBruce Richardson  */
49299a2dd95SBruce Richardson static void
49399a2dd95SBruce Richardson expand_rule(struct rte_lpm6 *lpm, uint32_t tbl8_gindex, uint8_t old_depth,
49499a2dd95SBruce Richardson 		uint8_t new_depth, uint32_t next_hop, uint8_t valid)
49599a2dd95SBruce Richardson {
49699a2dd95SBruce Richardson 	uint32_t tbl8_group_end, tbl8_gindex_next, j;
49799a2dd95SBruce Richardson 
49899a2dd95SBruce Richardson 	tbl8_group_end = tbl8_gindex + RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
49999a2dd95SBruce Richardson 
50099a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry new_tbl8_entry = {
50199a2dd95SBruce Richardson 		.valid = valid,
50299a2dd95SBruce Richardson 		.valid_group = valid,
50399a2dd95SBruce Richardson 		.depth = new_depth,
50499a2dd95SBruce Richardson 		.next_hop = next_hop,
50599a2dd95SBruce Richardson 		.ext_entry = 0,
50699a2dd95SBruce Richardson 	};
50799a2dd95SBruce Richardson 
50899a2dd95SBruce Richardson 	for (j = tbl8_gindex; j < tbl8_group_end; j++) {
50999a2dd95SBruce Richardson 		if (!lpm->tbl8[j].valid || (lpm->tbl8[j].ext_entry == 0
51099a2dd95SBruce Richardson 				&& lpm->tbl8[j].depth <= old_depth)) {
51199a2dd95SBruce Richardson 
51299a2dd95SBruce Richardson 			lpm->tbl8[j] = new_tbl8_entry;
51399a2dd95SBruce Richardson 
51499a2dd95SBruce Richardson 		} else if (lpm->tbl8[j].ext_entry == 1) {
51599a2dd95SBruce Richardson 
51699a2dd95SBruce Richardson 			tbl8_gindex_next = lpm->tbl8[j].lpm6_tbl8_gindex
51799a2dd95SBruce Richardson 					* RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
51899a2dd95SBruce Richardson 			expand_rule(lpm, tbl8_gindex_next, old_depth, new_depth,
51999a2dd95SBruce Richardson 					next_hop, valid);
52099a2dd95SBruce Richardson 		}
52199a2dd95SBruce Richardson 	}
52299a2dd95SBruce Richardson }
52399a2dd95SBruce Richardson 
52499a2dd95SBruce Richardson /*
52599a2dd95SBruce Richardson  * Init a tbl8 header
52699a2dd95SBruce Richardson  */
52799a2dd95SBruce Richardson static inline void
52899a2dd95SBruce Richardson init_tbl8_header(struct rte_lpm6 *lpm, uint32_t tbl_ind,
52999a2dd95SBruce Richardson 		uint32_t owner_tbl_ind, uint32_t owner_entry_ind)
53099a2dd95SBruce Richardson {
53199a2dd95SBruce Richardson 	struct rte_lpm_tbl8_hdr *tbl_hdr = &lpm->tbl8_hdrs[tbl_ind];
53299a2dd95SBruce Richardson 	tbl_hdr->owner_tbl_ind = owner_tbl_ind;
53399a2dd95SBruce Richardson 	tbl_hdr->owner_entry_ind = owner_entry_ind;
53499a2dd95SBruce Richardson 	tbl_hdr->ref_cnt = 0;
53599a2dd95SBruce Richardson }
53699a2dd95SBruce Richardson 
53799a2dd95SBruce Richardson /*
53899a2dd95SBruce Richardson  * Calculate index to the table based on the number and position
53999a2dd95SBruce Richardson  * of the bytes being inspected in this step.
54099a2dd95SBruce Richardson  */
54199a2dd95SBruce Richardson static uint32_t
542*e1a06e39SRobin Jarry get_bitshift(const struct rte_ipv6_addr *ip, uint8_t first_byte, uint8_t bytes)
54399a2dd95SBruce Richardson {
54499a2dd95SBruce Richardson 	uint32_t entry_ind, i;
54599a2dd95SBruce Richardson 	int8_t bitshift;
54699a2dd95SBruce Richardson 
54799a2dd95SBruce Richardson 	entry_ind = 0;
54899a2dd95SBruce Richardson 	for (i = first_byte; i < (uint32_t)(first_byte + bytes); i++) {
54999a2dd95SBruce Richardson 		bitshift = (int8_t)((bytes - i)*BYTE_SIZE);
55099a2dd95SBruce Richardson 
55199a2dd95SBruce Richardson 		if (bitshift < 0)
55299a2dd95SBruce Richardson 			bitshift = 0;
553*e1a06e39SRobin Jarry 		entry_ind = entry_ind | ip->a[i-1] << bitshift;
55499a2dd95SBruce Richardson 	}
55599a2dd95SBruce Richardson 
55699a2dd95SBruce Richardson 	return entry_ind;
55799a2dd95SBruce Richardson }
55899a2dd95SBruce Richardson 
55999a2dd95SBruce Richardson /*
56099a2dd95SBruce Richardson  * Simulate adding a new route to the LPM counting number
56199a2dd95SBruce Richardson  * of new tables that will be needed
56299a2dd95SBruce Richardson  *
56399a2dd95SBruce Richardson  * It returns 0 on success, or 1 if
56499a2dd95SBruce Richardson  * the process needs to be continued by calling the function again.
56599a2dd95SBruce Richardson  */
56699a2dd95SBruce Richardson static inline int
56799a2dd95SBruce Richardson simulate_add_step(struct rte_lpm6 *lpm, struct rte_lpm6_tbl_entry *tbl,
568*e1a06e39SRobin Jarry 		struct rte_lpm6_tbl_entry **next_tbl, const struct rte_ipv6_addr *ip,
56999a2dd95SBruce Richardson 		uint8_t bytes, uint8_t first_byte, uint8_t depth,
57099a2dd95SBruce Richardson 		uint32_t *need_tbl_nb)
57199a2dd95SBruce Richardson {
57299a2dd95SBruce Richardson 	uint32_t entry_ind;
57399a2dd95SBruce Richardson 	uint8_t bits_covered;
57499a2dd95SBruce Richardson 	uint32_t next_tbl_ind;
57599a2dd95SBruce Richardson 
57699a2dd95SBruce Richardson 	/*
57799a2dd95SBruce Richardson 	 * Calculate index to the table based on the number and position
57899a2dd95SBruce Richardson 	 * of the bytes being inspected in this step.
57999a2dd95SBruce Richardson 	 */
58099a2dd95SBruce Richardson 	entry_ind = get_bitshift(ip, first_byte, bytes);
58199a2dd95SBruce Richardson 
58299a2dd95SBruce Richardson 	/* Number of bits covered in this step */
58399a2dd95SBruce Richardson 	bits_covered = (uint8_t)((bytes+first_byte-1)*BYTE_SIZE);
58499a2dd95SBruce Richardson 
58599a2dd95SBruce Richardson 	if (depth <= bits_covered) {
58699a2dd95SBruce Richardson 		*need_tbl_nb = 0;
58799a2dd95SBruce Richardson 		return 0;
58899a2dd95SBruce Richardson 	}
58999a2dd95SBruce Richardson 
59099a2dd95SBruce Richardson 	if (tbl[entry_ind].valid == 0 || tbl[entry_ind].ext_entry == 0) {
59199a2dd95SBruce Richardson 		/* from this point on a new table is needed on each level
59299a2dd95SBruce Richardson 		 * that is not covered yet
59399a2dd95SBruce Richardson 		 */
59499a2dd95SBruce Richardson 		depth -= bits_covered;
59599a2dd95SBruce Richardson 		uint32_t cnt = depth >> 3; /* depth / BYTE_SIZE */
59699a2dd95SBruce Richardson 		if (depth & 7) /* 0b00000111 */
59799a2dd95SBruce Richardson 			/* if depth % 8 > 0 then one more table is needed
59899a2dd95SBruce Richardson 			 * for those last bits
59999a2dd95SBruce Richardson 			 */
60099a2dd95SBruce Richardson 			cnt++;
60199a2dd95SBruce Richardson 
60299a2dd95SBruce Richardson 		*need_tbl_nb = cnt;
60399a2dd95SBruce Richardson 		return 0;
60499a2dd95SBruce Richardson 	}
60599a2dd95SBruce Richardson 
60699a2dd95SBruce Richardson 	next_tbl_ind = tbl[entry_ind].lpm6_tbl8_gindex;
60799a2dd95SBruce Richardson 	*next_tbl = &(lpm->tbl8[next_tbl_ind *
60899a2dd95SBruce Richardson 		RTE_LPM6_TBL8_GROUP_NUM_ENTRIES]);
60999a2dd95SBruce Richardson 	*need_tbl_nb = 0;
61099a2dd95SBruce Richardson 	return 1;
61199a2dd95SBruce Richardson }
61299a2dd95SBruce Richardson 
61399a2dd95SBruce Richardson /*
61499a2dd95SBruce Richardson  * Partially adds a new route to the data structure (tbl24+tbl8s).
61599a2dd95SBruce Richardson  * It returns 0 on success, a negative number on failure, or 1 if
61699a2dd95SBruce Richardson  * the process needs to be continued by calling the function again.
61799a2dd95SBruce Richardson  */
61899a2dd95SBruce Richardson static inline int
61999a2dd95SBruce Richardson add_step(struct rte_lpm6 *lpm, struct rte_lpm6_tbl_entry *tbl,
62099a2dd95SBruce Richardson 		uint32_t tbl_ind, struct rte_lpm6_tbl_entry **next_tbl,
621*e1a06e39SRobin Jarry 		uint32_t *next_tbl_ind, struct rte_ipv6_addr *ip, uint8_t bytes,
62299a2dd95SBruce Richardson 		uint8_t first_byte, uint8_t depth, uint32_t next_hop,
62399a2dd95SBruce Richardson 		uint8_t is_new_rule)
62499a2dd95SBruce Richardson {
62599a2dd95SBruce Richardson 	uint32_t entry_ind, tbl_range, tbl8_group_start, tbl8_group_end, i;
62699a2dd95SBruce Richardson 	uint32_t tbl8_gindex;
62799a2dd95SBruce Richardson 	uint8_t bits_covered;
62899a2dd95SBruce Richardson 	int ret;
62999a2dd95SBruce Richardson 
63099a2dd95SBruce Richardson 	/*
63199a2dd95SBruce Richardson 	 * Calculate index to the table based on the number and position
63299a2dd95SBruce Richardson 	 * of the bytes being inspected in this step.
63399a2dd95SBruce Richardson 	 */
63499a2dd95SBruce Richardson 	entry_ind = get_bitshift(ip, first_byte, bytes);
63599a2dd95SBruce Richardson 
63699a2dd95SBruce Richardson 	/* Number of bits covered in this step */
63799a2dd95SBruce Richardson 	bits_covered = (uint8_t)((bytes+first_byte-1)*BYTE_SIZE);
63899a2dd95SBruce Richardson 
63999a2dd95SBruce Richardson 	/*
64099a2dd95SBruce Richardson 	 * If depth if smaller than this number (ie this is the last step)
64199a2dd95SBruce Richardson 	 * expand the rule across the relevant positions in the table.
64299a2dd95SBruce Richardson 	 */
64399a2dd95SBruce Richardson 	if (depth <= bits_covered) {
64499a2dd95SBruce Richardson 		tbl_range = 1 << (bits_covered - depth);
64599a2dd95SBruce Richardson 
64699a2dd95SBruce Richardson 		for (i = entry_ind; i < (entry_ind + tbl_range); i++) {
64799a2dd95SBruce Richardson 			if (!tbl[i].valid || (tbl[i].ext_entry == 0 &&
64899a2dd95SBruce Richardson 					tbl[i].depth <= depth)) {
64999a2dd95SBruce Richardson 
65099a2dd95SBruce Richardson 				struct rte_lpm6_tbl_entry new_tbl_entry = {
65199a2dd95SBruce Richardson 					.next_hop = next_hop,
65299a2dd95SBruce Richardson 					.depth = depth,
65399a2dd95SBruce Richardson 					.valid = VALID,
65499a2dd95SBruce Richardson 					.valid_group = VALID,
65599a2dd95SBruce Richardson 					.ext_entry = 0,
65699a2dd95SBruce Richardson 				};
65799a2dd95SBruce Richardson 
65899a2dd95SBruce Richardson 				tbl[i] = new_tbl_entry;
65999a2dd95SBruce Richardson 
66099a2dd95SBruce Richardson 			} else if (tbl[i].ext_entry == 1) {
66199a2dd95SBruce Richardson 
66299a2dd95SBruce Richardson 				/*
66399a2dd95SBruce Richardson 				 * If tbl entry is valid and extended calculate the index
66499a2dd95SBruce Richardson 				 * into next tbl8 and expand the rule across the data structure.
66599a2dd95SBruce Richardson 				 */
66699a2dd95SBruce Richardson 				tbl8_gindex = tbl[i].lpm6_tbl8_gindex *
66799a2dd95SBruce Richardson 						RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
66899a2dd95SBruce Richardson 				expand_rule(lpm, tbl8_gindex, depth, depth,
66999a2dd95SBruce Richardson 						next_hop, VALID);
67099a2dd95SBruce Richardson 			}
67199a2dd95SBruce Richardson 		}
67299a2dd95SBruce Richardson 
67399a2dd95SBruce Richardson 		/* update tbl8 rule reference counter */
67499a2dd95SBruce Richardson 		if (tbl_ind != TBL24_IND && is_new_rule)
67599a2dd95SBruce Richardson 			lpm->tbl8_hdrs[tbl_ind].ref_cnt++;
67699a2dd95SBruce Richardson 
67799a2dd95SBruce Richardson 		return 0;
67899a2dd95SBruce Richardson 	}
67999a2dd95SBruce Richardson 	/*
68099a2dd95SBruce Richardson 	 * If this is not the last step just fill one position
68199a2dd95SBruce Richardson 	 * and calculate the index to the next table.
68299a2dd95SBruce Richardson 	 */
68399a2dd95SBruce Richardson 	else {
68499a2dd95SBruce Richardson 		/* If it's invalid a new tbl8 is needed */
68599a2dd95SBruce Richardson 		if (!tbl[entry_ind].valid) {
68699a2dd95SBruce Richardson 			/* get a new table */
68799a2dd95SBruce Richardson 			ret = tbl8_get(lpm, &tbl8_gindex);
68899a2dd95SBruce Richardson 			if (ret != 0)
68999a2dd95SBruce Richardson 				return -ENOSPC;
69099a2dd95SBruce Richardson 
69199a2dd95SBruce Richardson 			/* invalidate all new tbl8 entries */
69299a2dd95SBruce Richardson 			tbl8_group_start = tbl8_gindex *
69399a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
69499a2dd95SBruce Richardson 			memset(&lpm->tbl8[tbl8_group_start], 0,
69599a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES *
69699a2dd95SBruce Richardson 					sizeof(struct rte_lpm6_tbl_entry));
69799a2dd95SBruce Richardson 
69899a2dd95SBruce Richardson 			/* init the new table's header:
69999a2dd95SBruce Richardson 			 *   save the reference to the owner table
70099a2dd95SBruce Richardson 			 */
70199a2dd95SBruce Richardson 			init_tbl8_header(lpm, tbl8_gindex, tbl_ind, entry_ind);
70299a2dd95SBruce Richardson 
70399a2dd95SBruce Richardson 			/* reference to a new tbl8 */
70499a2dd95SBruce Richardson 			struct rte_lpm6_tbl_entry new_tbl_entry = {
70599a2dd95SBruce Richardson 				.lpm6_tbl8_gindex = tbl8_gindex,
70699a2dd95SBruce Richardson 				.depth = 0,
70799a2dd95SBruce Richardson 				.valid = VALID,
70899a2dd95SBruce Richardson 				.valid_group = VALID,
70999a2dd95SBruce Richardson 				.ext_entry = 1,
71099a2dd95SBruce Richardson 			};
71199a2dd95SBruce Richardson 
71299a2dd95SBruce Richardson 			tbl[entry_ind] = new_tbl_entry;
71399a2dd95SBruce Richardson 
71499a2dd95SBruce Richardson 			/* update the current table's reference counter */
71599a2dd95SBruce Richardson 			if (tbl_ind != TBL24_IND)
71699a2dd95SBruce Richardson 				lpm->tbl8_hdrs[tbl_ind].ref_cnt++;
71799a2dd95SBruce Richardson 		}
71899a2dd95SBruce Richardson 		/*
71999a2dd95SBruce Richardson 		 * If it's valid but not extended the rule that was stored
72099a2dd95SBruce Richardson 		 * here needs to be moved to the next table.
72199a2dd95SBruce Richardson 		 */
72299a2dd95SBruce Richardson 		else if (tbl[entry_ind].ext_entry == 0) {
72399a2dd95SBruce Richardson 			/* get a new tbl8 index */
72499a2dd95SBruce Richardson 			ret = tbl8_get(lpm, &tbl8_gindex);
72599a2dd95SBruce Richardson 			if (ret != 0)
72699a2dd95SBruce Richardson 				return -ENOSPC;
72799a2dd95SBruce Richardson 
72899a2dd95SBruce Richardson 			tbl8_group_start = tbl8_gindex *
72999a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
73099a2dd95SBruce Richardson 			tbl8_group_end = tbl8_group_start +
73199a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
73299a2dd95SBruce Richardson 
73399a2dd95SBruce Richardson 			struct rte_lpm6_tbl_entry tbl_entry = {
73499a2dd95SBruce Richardson 				.next_hop = tbl[entry_ind].next_hop,
73599a2dd95SBruce Richardson 				.depth = tbl[entry_ind].depth,
73699a2dd95SBruce Richardson 				.valid = VALID,
73799a2dd95SBruce Richardson 				.valid_group = VALID,
73899a2dd95SBruce Richardson 				.ext_entry = 0
73999a2dd95SBruce Richardson 			};
74099a2dd95SBruce Richardson 
74199a2dd95SBruce Richardson 			/* Populate new tbl8 with tbl value. */
74299a2dd95SBruce Richardson 			for (i = tbl8_group_start; i < tbl8_group_end; i++)
74399a2dd95SBruce Richardson 				lpm->tbl8[i] = tbl_entry;
74499a2dd95SBruce Richardson 
74599a2dd95SBruce Richardson 			/* init the new table's header:
74699a2dd95SBruce Richardson 			 *   save the reference to the owner table
74799a2dd95SBruce Richardson 			 */
74899a2dd95SBruce Richardson 			init_tbl8_header(lpm, tbl8_gindex, tbl_ind, entry_ind);
74999a2dd95SBruce Richardson 
75099a2dd95SBruce Richardson 			/*
75199a2dd95SBruce Richardson 			 * Update tbl entry to point to new tbl8 entry. Note: The
75299a2dd95SBruce Richardson 			 * ext_flag and tbl8_index need to be updated simultaneously,
75399a2dd95SBruce Richardson 			 * so assign whole structure in one go.
75499a2dd95SBruce Richardson 			 */
75599a2dd95SBruce Richardson 			struct rte_lpm6_tbl_entry new_tbl_entry = {
75699a2dd95SBruce Richardson 				.lpm6_tbl8_gindex = tbl8_gindex,
75799a2dd95SBruce Richardson 				.depth = 0,
75899a2dd95SBruce Richardson 				.valid = VALID,
75999a2dd95SBruce Richardson 				.valid_group = VALID,
76099a2dd95SBruce Richardson 				.ext_entry = 1,
76199a2dd95SBruce Richardson 			};
76299a2dd95SBruce Richardson 
76399a2dd95SBruce Richardson 			tbl[entry_ind] = new_tbl_entry;
76499a2dd95SBruce Richardson 
76599a2dd95SBruce Richardson 			/* update the current table's reference counter */
76699a2dd95SBruce Richardson 			if (tbl_ind != TBL24_IND)
76799a2dd95SBruce Richardson 				lpm->tbl8_hdrs[tbl_ind].ref_cnt++;
76899a2dd95SBruce Richardson 		}
76999a2dd95SBruce Richardson 
77099a2dd95SBruce Richardson 		*next_tbl_ind = tbl[entry_ind].lpm6_tbl8_gindex;
77199a2dd95SBruce Richardson 		*next_tbl = &(lpm->tbl8[*next_tbl_ind *
77299a2dd95SBruce Richardson 				  RTE_LPM6_TBL8_GROUP_NUM_ENTRIES]);
77399a2dd95SBruce Richardson 	}
77499a2dd95SBruce Richardson 
77599a2dd95SBruce Richardson 	return 1;
77699a2dd95SBruce Richardson }
77799a2dd95SBruce Richardson 
77899a2dd95SBruce Richardson /*
77999a2dd95SBruce Richardson  * Simulate adding a route to LPM
78099a2dd95SBruce Richardson  *
78199a2dd95SBruce Richardson  *	Returns:
78299a2dd95SBruce Richardson  *    0 on success
78399a2dd95SBruce Richardson  *    -ENOSPC not enough tbl8 left
78499a2dd95SBruce Richardson  */
78599a2dd95SBruce Richardson static int
786*e1a06e39SRobin Jarry simulate_add(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *masked_ip, uint8_t depth)
78799a2dd95SBruce Richardson {
78899a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *tbl;
78999a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *tbl_next = NULL;
79099a2dd95SBruce Richardson 	int ret, i;
79199a2dd95SBruce Richardson 
79299a2dd95SBruce Richardson 	/* number of new tables needed for a step */
79399a2dd95SBruce Richardson 	uint32_t need_tbl_nb;
79499a2dd95SBruce Richardson 	/* total number of new tables needed */
79599a2dd95SBruce Richardson 	uint32_t total_need_tbl_nb;
79699a2dd95SBruce Richardson 
79799a2dd95SBruce Richardson 	/* Inspect the first three bytes through tbl24 on the first step. */
79899a2dd95SBruce Richardson 	ret = simulate_add_step(lpm, lpm->tbl24, &tbl_next, masked_ip,
79999a2dd95SBruce Richardson 		ADD_FIRST_BYTE, 1, depth, &need_tbl_nb);
80099a2dd95SBruce Richardson 	total_need_tbl_nb = need_tbl_nb;
80199a2dd95SBruce Richardson 	/*
80299a2dd95SBruce Richardson 	 * Inspect one by one the rest of the bytes until
80399a2dd95SBruce Richardson 	 * the process is completed.
80499a2dd95SBruce Richardson 	 */
805*e1a06e39SRobin Jarry 	for (i = ADD_FIRST_BYTE; i < RTE_IPV6_ADDR_SIZE && ret == 1; i++) {
80699a2dd95SBruce Richardson 		tbl = tbl_next;
80799a2dd95SBruce Richardson 		ret = simulate_add_step(lpm, tbl, &tbl_next, masked_ip, 1,
80899a2dd95SBruce Richardson 			(uint8_t)(i + 1), depth, &need_tbl_nb);
80999a2dd95SBruce Richardson 		total_need_tbl_nb += need_tbl_nb;
81099a2dd95SBruce Richardson 	}
81199a2dd95SBruce Richardson 
81299a2dd95SBruce Richardson 	if (tbl8_available(lpm) < total_need_tbl_nb)
81399a2dd95SBruce Richardson 		/* not enough tbl8 to add a rule */
81499a2dd95SBruce Richardson 		return -ENOSPC;
81599a2dd95SBruce Richardson 
81699a2dd95SBruce Richardson 	return 0;
81799a2dd95SBruce Richardson }
81899a2dd95SBruce Richardson 
81999a2dd95SBruce Richardson /*
82099a2dd95SBruce Richardson  * Add a route
82199a2dd95SBruce Richardson  */
82299a2dd95SBruce Richardson int
823*e1a06e39SRobin Jarry rte_lpm6_add(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth,
82499a2dd95SBruce Richardson 	     uint32_t next_hop)
82599a2dd95SBruce Richardson {
82699a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *tbl;
82799a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *tbl_next = NULL;
82899a2dd95SBruce Richardson 	/* init to avoid compiler warning */
82999a2dd95SBruce Richardson 	uint32_t tbl_next_num = 123456;
83099a2dd95SBruce Richardson 	int status;
831*e1a06e39SRobin Jarry 	struct rte_ipv6_addr masked_ip;
83299a2dd95SBruce Richardson 	int i;
83399a2dd95SBruce Richardson 
83499a2dd95SBruce Richardson 	/* Check user arguments. */
835*e1a06e39SRobin Jarry 	if ((lpm == NULL) || (depth < 1) || (depth > RTE_IPV6_MAX_DEPTH))
83699a2dd95SBruce Richardson 		return -EINVAL;
83799a2dd95SBruce Richardson 
83899a2dd95SBruce Richardson 	/* Copy the IP and mask it to avoid modifying user's input data. */
839*e1a06e39SRobin Jarry 	masked_ip = *ip;
840*e1a06e39SRobin Jarry 	rte_ipv6_addr_mask(&masked_ip, depth);
84199a2dd95SBruce Richardson 
84299a2dd95SBruce Richardson 	/* Simulate adding a new route */
843*e1a06e39SRobin Jarry 	int ret = simulate_add(lpm, &masked_ip, depth);
84499a2dd95SBruce Richardson 	if (ret < 0)
84599a2dd95SBruce Richardson 		return ret;
84699a2dd95SBruce Richardson 
84799a2dd95SBruce Richardson 	/* Add the rule to the rule table. */
848*e1a06e39SRobin Jarry 	int is_new_rule = rule_add(lpm, &masked_ip, depth, next_hop);
84999a2dd95SBruce Richardson 	/* If there is no space available for new rule return error. */
85099a2dd95SBruce Richardson 	if (is_new_rule < 0)
85199a2dd95SBruce Richardson 		return is_new_rule;
85299a2dd95SBruce Richardson 
85399a2dd95SBruce Richardson 	/* Inspect the first three bytes through tbl24 on the first step. */
85499a2dd95SBruce Richardson 	tbl = lpm->tbl24;
85599a2dd95SBruce Richardson 	status = add_step(lpm, tbl, TBL24_IND, &tbl_next, &tbl_next_num,
856*e1a06e39SRobin Jarry 		&masked_ip, ADD_FIRST_BYTE, 1, depth, next_hop,
85799a2dd95SBruce Richardson 		is_new_rule);
85899a2dd95SBruce Richardson 	assert(status >= 0);
85999a2dd95SBruce Richardson 
86099a2dd95SBruce Richardson 	/*
86199a2dd95SBruce Richardson 	 * Inspect one by one the rest of the bytes until
86299a2dd95SBruce Richardson 	 * the process is completed.
86399a2dd95SBruce Richardson 	 */
864*e1a06e39SRobin Jarry 	for (i = ADD_FIRST_BYTE; i < RTE_IPV6_ADDR_SIZE && status == 1; i++) {
86599a2dd95SBruce Richardson 		tbl = tbl_next;
86699a2dd95SBruce Richardson 		status = add_step(lpm, tbl, tbl_next_num, &tbl_next,
867*e1a06e39SRobin Jarry 			&tbl_next_num, &masked_ip, 1, (uint8_t)(i + 1),
86899a2dd95SBruce Richardson 			depth, next_hop, is_new_rule);
86999a2dd95SBruce Richardson 		assert(status >= 0);
87099a2dd95SBruce Richardson 	}
87199a2dd95SBruce Richardson 
87299a2dd95SBruce Richardson 	return status;
87399a2dd95SBruce Richardson }
87499a2dd95SBruce Richardson 
87599a2dd95SBruce Richardson /*
87699a2dd95SBruce Richardson  * Takes a pointer to a table entry and inspect one level.
87799a2dd95SBruce Richardson  * The function returns 0 on lookup success, ENOENT if no match was found
87899a2dd95SBruce Richardson  * or 1 if the process needs to be continued by calling the function again.
87999a2dd95SBruce Richardson  */
88099a2dd95SBruce Richardson static inline int
88199a2dd95SBruce Richardson lookup_step(const struct rte_lpm6 *lpm, const struct rte_lpm6_tbl_entry *tbl,
882*e1a06e39SRobin Jarry 		const struct rte_lpm6_tbl_entry **tbl_next, const struct rte_ipv6_addr *ip,
88399a2dd95SBruce Richardson 		uint8_t first_byte, uint32_t *next_hop)
88499a2dd95SBruce Richardson {
88599a2dd95SBruce Richardson 	uint32_t tbl8_index, tbl_entry;
88699a2dd95SBruce Richardson 
88799a2dd95SBruce Richardson 	/* Take the integer value from the pointer. */
88899a2dd95SBruce Richardson 	tbl_entry = *(const uint32_t *)tbl;
88999a2dd95SBruce Richardson 
89099a2dd95SBruce Richardson 	/* If it is valid and extended we calculate the new pointer to return. */
89199a2dd95SBruce Richardson 	if ((tbl_entry & RTE_LPM6_VALID_EXT_ENTRY_BITMASK) ==
89299a2dd95SBruce Richardson 			RTE_LPM6_VALID_EXT_ENTRY_BITMASK) {
89399a2dd95SBruce Richardson 
894*e1a06e39SRobin Jarry 		tbl8_index = ip->a[first_byte-1] +
89599a2dd95SBruce Richardson 				((tbl_entry & RTE_LPM6_TBL8_BITMASK) *
89699a2dd95SBruce Richardson 				RTE_LPM6_TBL8_GROUP_NUM_ENTRIES);
89799a2dd95SBruce Richardson 
89899a2dd95SBruce Richardson 		*tbl_next = &lpm->tbl8[tbl8_index];
89999a2dd95SBruce Richardson 
90099a2dd95SBruce Richardson 		return 1;
90199a2dd95SBruce Richardson 	} else {
90299a2dd95SBruce Richardson 		/* If not extended then we can have a match. */
90399a2dd95SBruce Richardson 		*next_hop = ((uint32_t)tbl_entry & RTE_LPM6_TBL8_BITMASK);
90499a2dd95SBruce Richardson 		return (tbl_entry & RTE_LPM6_LOOKUP_SUCCESS) ? 0 : -ENOENT;
90599a2dd95SBruce Richardson 	}
90699a2dd95SBruce Richardson }
90799a2dd95SBruce Richardson 
90899a2dd95SBruce Richardson /*
90999a2dd95SBruce Richardson  * Looks up an IP
91099a2dd95SBruce Richardson  */
91199a2dd95SBruce Richardson int
912*e1a06e39SRobin Jarry rte_lpm6_lookup(const struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip,
91399a2dd95SBruce Richardson 		uint32_t *next_hop)
91499a2dd95SBruce Richardson {
91599a2dd95SBruce Richardson 	const struct rte_lpm6_tbl_entry *tbl;
91699a2dd95SBruce Richardson 	const struct rte_lpm6_tbl_entry *tbl_next = NULL;
91799a2dd95SBruce Richardson 	int status;
91899a2dd95SBruce Richardson 	uint8_t first_byte;
91999a2dd95SBruce Richardson 	uint32_t tbl24_index;
92099a2dd95SBruce Richardson 
92199a2dd95SBruce Richardson 	/* DEBUG: Check user input arguments. */
92299a2dd95SBruce Richardson 	if ((lpm == NULL) || (ip == NULL) || (next_hop == NULL))
92399a2dd95SBruce Richardson 		return -EINVAL;
92499a2dd95SBruce Richardson 
92599a2dd95SBruce Richardson 	first_byte = LOOKUP_FIRST_BYTE;
926*e1a06e39SRobin Jarry 	tbl24_index = (ip->a[0] << BYTES2_SIZE) | (ip->a[1] << BYTE_SIZE) | ip->a[2];
92799a2dd95SBruce Richardson 
92899a2dd95SBruce Richardson 	/* Calculate pointer to the first entry to be inspected */
92999a2dd95SBruce Richardson 	tbl = &lpm->tbl24[tbl24_index];
93099a2dd95SBruce Richardson 
93199a2dd95SBruce Richardson 	do {
93299a2dd95SBruce Richardson 		/* Continue inspecting following levels until success or failure */
93399a2dd95SBruce Richardson 		status = lookup_step(lpm, tbl, &tbl_next, ip, first_byte++, next_hop);
93499a2dd95SBruce Richardson 		tbl = tbl_next;
93599a2dd95SBruce Richardson 	} while (status == 1);
93699a2dd95SBruce Richardson 
93799a2dd95SBruce Richardson 	return status;
93899a2dd95SBruce Richardson }
93999a2dd95SBruce Richardson 
94099a2dd95SBruce Richardson /*
94199a2dd95SBruce Richardson  * Looks up a group of IP addresses
94299a2dd95SBruce Richardson  */
94399a2dd95SBruce Richardson int
94499a2dd95SBruce Richardson rte_lpm6_lookup_bulk_func(const struct rte_lpm6 *lpm,
945*e1a06e39SRobin Jarry 		struct rte_ipv6_addr *ips,
94699a2dd95SBruce Richardson 		int32_t *next_hops, unsigned int n)
94799a2dd95SBruce Richardson {
94899a2dd95SBruce Richardson 	unsigned int i;
94999a2dd95SBruce Richardson 	const struct rte_lpm6_tbl_entry *tbl;
95099a2dd95SBruce Richardson 	const struct rte_lpm6_tbl_entry *tbl_next = NULL;
95199a2dd95SBruce Richardson 	uint32_t tbl24_index, next_hop;
95299a2dd95SBruce Richardson 	uint8_t first_byte;
95399a2dd95SBruce Richardson 	int status;
95499a2dd95SBruce Richardson 
95599a2dd95SBruce Richardson 	/* DEBUG: Check user input arguments. */
95699a2dd95SBruce Richardson 	if ((lpm == NULL) || (ips == NULL) || (next_hops == NULL))
95799a2dd95SBruce Richardson 		return -EINVAL;
95899a2dd95SBruce Richardson 
95999a2dd95SBruce Richardson 	for (i = 0; i < n; i++) {
96099a2dd95SBruce Richardson 		first_byte = LOOKUP_FIRST_BYTE;
961*e1a06e39SRobin Jarry 		tbl24_index = (ips[i].a[0] << BYTES2_SIZE) |
962*e1a06e39SRobin Jarry 				(ips[i].a[1] << BYTE_SIZE) | ips[i].a[2];
96399a2dd95SBruce Richardson 
96499a2dd95SBruce Richardson 		/* Calculate pointer to the first entry to be inspected */
96599a2dd95SBruce Richardson 		tbl = &lpm->tbl24[tbl24_index];
96699a2dd95SBruce Richardson 
96799a2dd95SBruce Richardson 		do {
96899a2dd95SBruce Richardson 			/* Continue inspecting following levels
96999a2dd95SBruce Richardson 			 * until success or failure
97099a2dd95SBruce Richardson 			 */
971*e1a06e39SRobin Jarry 			status = lookup_step(lpm, tbl, &tbl_next, &ips[i],
97299a2dd95SBruce Richardson 					first_byte++, &next_hop);
97399a2dd95SBruce Richardson 			tbl = tbl_next;
97499a2dd95SBruce Richardson 		} while (status == 1);
97599a2dd95SBruce Richardson 
97699a2dd95SBruce Richardson 		if (status < 0)
97799a2dd95SBruce Richardson 			next_hops[i] = -1;
97899a2dd95SBruce Richardson 		else
97999a2dd95SBruce Richardson 			next_hops[i] = (int32_t)next_hop;
98099a2dd95SBruce Richardson 	}
98199a2dd95SBruce Richardson 
98299a2dd95SBruce Richardson 	return 0;
98399a2dd95SBruce Richardson }
98499a2dd95SBruce Richardson 
98599a2dd95SBruce Richardson /*
98699a2dd95SBruce Richardson  * Look for a rule in the high-level rules table
98799a2dd95SBruce Richardson  */
98899a2dd95SBruce Richardson int
989*e1a06e39SRobin Jarry rte_lpm6_is_rule_present(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth,
99099a2dd95SBruce Richardson 			 uint32_t *next_hop)
99199a2dd95SBruce Richardson {
992*e1a06e39SRobin Jarry 	struct rte_ipv6_addr masked_ip;
99399a2dd95SBruce Richardson 
99499a2dd95SBruce Richardson 	/* Check user arguments. */
99599a2dd95SBruce Richardson 	if ((lpm == NULL) || next_hop == NULL || ip == NULL ||
996*e1a06e39SRobin Jarry 			(depth < 1) || (depth > RTE_IPV6_MAX_DEPTH))
99799a2dd95SBruce Richardson 		return -EINVAL;
99899a2dd95SBruce Richardson 
99999a2dd95SBruce Richardson 	/* Copy the IP and mask it to avoid modifying user's input data. */
1000*e1a06e39SRobin Jarry 	masked_ip = *ip;
1001*e1a06e39SRobin Jarry 	rte_ipv6_addr_mask(&masked_ip, depth);
100299a2dd95SBruce Richardson 
1003*e1a06e39SRobin Jarry 	return rule_find(lpm, &masked_ip, depth, next_hop);
100499a2dd95SBruce Richardson }
100599a2dd95SBruce Richardson 
100699a2dd95SBruce Richardson /*
100799a2dd95SBruce Richardson  * Delete a rule from the rule table.
100899a2dd95SBruce Richardson  * NOTE: Valid range for depth parameter is 1 .. 128 inclusive.
100999a2dd95SBruce Richardson  * return
101099a2dd95SBruce Richardson  *	  0 on success
101199a2dd95SBruce Richardson  *   <0 on failure
101299a2dd95SBruce Richardson  */
101399a2dd95SBruce Richardson static inline int
1014*e1a06e39SRobin Jarry rule_delete(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth)
101599a2dd95SBruce Richardson {
101699a2dd95SBruce Richardson 	int ret;
101799a2dd95SBruce Richardson 	struct rte_lpm6_rule_key rule_key;
101899a2dd95SBruce Richardson 
101999a2dd95SBruce Richardson 	/* init rule key */
102099a2dd95SBruce Richardson 	rule_key_init(&rule_key, ip, depth);
102199a2dd95SBruce Richardson 
102299a2dd95SBruce Richardson 	/* delete the rule */
102399a2dd95SBruce Richardson 	ret = rte_hash_del_key(lpm->rules_tbl, (void *) &rule_key);
102499a2dd95SBruce Richardson 	if (ret >= 0)
102599a2dd95SBruce Richardson 		lpm->used_rules--;
102699a2dd95SBruce Richardson 
102799a2dd95SBruce Richardson 	return ret;
102899a2dd95SBruce Richardson }
102999a2dd95SBruce Richardson 
103099a2dd95SBruce Richardson /*
103199a2dd95SBruce Richardson  * Deletes a group of rules
103299a2dd95SBruce Richardson  *
103399a2dd95SBruce Richardson  * Note that the function rebuilds the lpm table,
103499a2dd95SBruce Richardson  * rather than doing incremental updates like
103599a2dd95SBruce Richardson  * the regular delete function
103699a2dd95SBruce Richardson  */
103799a2dd95SBruce Richardson int
103899a2dd95SBruce Richardson rte_lpm6_delete_bulk_func(struct rte_lpm6 *lpm,
1039*e1a06e39SRobin Jarry 		struct rte_ipv6_addr *ips, uint8_t *depths,
104099a2dd95SBruce Richardson 		unsigned n)
104199a2dd95SBruce Richardson {
1042*e1a06e39SRobin Jarry 	struct rte_ipv6_addr masked_ip;
104399a2dd95SBruce Richardson 	unsigned i;
104499a2dd95SBruce Richardson 
104599a2dd95SBruce Richardson 	/* Check input arguments. */
104699a2dd95SBruce Richardson 	if ((lpm == NULL) || (ips == NULL) || (depths == NULL))
104799a2dd95SBruce Richardson 		return -EINVAL;
104899a2dd95SBruce Richardson 
104999a2dd95SBruce Richardson 	for (i = 0; i < n; i++) {
1050*e1a06e39SRobin Jarry 		masked_ip = ips[i];
1051*e1a06e39SRobin Jarry 		rte_ipv6_addr_mask(&masked_ip, depths[i]);
1052*e1a06e39SRobin Jarry 		rule_delete(lpm, &masked_ip, depths[i]);
105399a2dd95SBruce Richardson 	}
105499a2dd95SBruce Richardson 
105599a2dd95SBruce Richardson 	/*
105699a2dd95SBruce Richardson 	 * Set all the table entries to 0 (ie delete every rule
105799a2dd95SBruce Richardson 	 * from the data structure.
105899a2dd95SBruce Richardson 	 */
105999a2dd95SBruce Richardson 	memset(lpm->tbl24, 0, sizeof(lpm->tbl24));
106099a2dd95SBruce Richardson 	memset(lpm->tbl8, 0, sizeof(lpm->tbl8[0])
106199a2dd95SBruce Richardson 			* RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * lpm->number_tbl8s);
106299a2dd95SBruce Richardson 	tbl8_pool_init(lpm);
106399a2dd95SBruce Richardson 
106499a2dd95SBruce Richardson 	/*
106599a2dd95SBruce Richardson 	 * Add every rule again (except for the ones that were removed from
106699a2dd95SBruce Richardson 	 * the rules table).
106799a2dd95SBruce Richardson 	 */
106899a2dd95SBruce Richardson 	rebuild_lpm(lpm);
106999a2dd95SBruce Richardson 
107099a2dd95SBruce Richardson 	return 0;
107199a2dd95SBruce Richardson }
107299a2dd95SBruce Richardson 
107399a2dd95SBruce Richardson /*
107499a2dd95SBruce Richardson  * Delete all rules from the LPM table.
107599a2dd95SBruce Richardson  */
107699a2dd95SBruce Richardson void
107799a2dd95SBruce Richardson rte_lpm6_delete_all(struct rte_lpm6 *lpm)
107899a2dd95SBruce Richardson {
107999a2dd95SBruce Richardson 	/* Zero used rules counter. */
108099a2dd95SBruce Richardson 	lpm->used_rules = 0;
108199a2dd95SBruce Richardson 
108299a2dd95SBruce Richardson 	/* Zero tbl24. */
108399a2dd95SBruce Richardson 	memset(lpm->tbl24, 0, sizeof(lpm->tbl24));
108499a2dd95SBruce Richardson 
108599a2dd95SBruce Richardson 	/* Zero tbl8. */
108699a2dd95SBruce Richardson 	memset(lpm->tbl8, 0, sizeof(lpm->tbl8[0]) *
108799a2dd95SBruce Richardson 			RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * lpm->number_tbl8s);
108899a2dd95SBruce Richardson 
108999a2dd95SBruce Richardson 	/* init pool of free tbl8 indexes */
109099a2dd95SBruce Richardson 	tbl8_pool_init(lpm);
109199a2dd95SBruce Richardson 
109299a2dd95SBruce Richardson 	/* Delete all rules form the rules table. */
109399a2dd95SBruce Richardson 	rte_hash_reset(lpm->rules_tbl);
109499a2dd95SBruce Richardson }
109599a2dd95SBruce Richardson 
109699a2dd95SBruce Richardson /*
109799a2dd95SBruce Richardson  * Convert a depth to a one byte long mask
109899a2dd95SBruce Richardson  *   Example: 4 will be converted to 0xF0
109999a2dd95SBruce Richardson  */
1100ff933786STyler Retzlaff static uint8_t __rte_pure
110199a2dd95SBruce Richardson depth_to_mask_1b(uint8_t depth)
110299a2dd95SBruce Richardson {
110399a2dd95SBruce Richardson 	/* To calculate a mask start with a 1 on the left hand side and right
110499a2dd95SBruce Richardson 	 * shift while populating the left hand side with 1's
110599a2dd95SBruce Richardson 	 */
110699a2dd95SBruce Richardson 	return (signed char)0x80 >> (depth - 1);
110799a2dd95SBruce Richardson }
110899a2dd95SBruce Richardson 
110999a2dd95SBruce Richardson /*
111099a2dd95SBruce Richardson  * Find a less specific rule
111199a2dd95SBruce Richardson  */
111299a2dd95SBruce Richardson static int
1113*e1a06e39SRobin Jarry rule_find_less_specific(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth,
111499a2dd95SBruce Richardson 	struct rte_lpm6_rule *rule)
111599a2dd95SBruce Richardson {
111699a2dd95SBruce Richardson 	int ret;
111799a2dd95SBruce Richardson 	uint32_t next_hop;
111899a2dd95SBruce Richardson 	uint8_t mask;
111999a2dd95SBruce Richardson 	struct rte_lpm6_rule_key rule_key;
112099a2dd95SBruce Richardson 
112199a2dd95SBruce Richardson 	if (depth == 1)
112299a2dd95SBruce Richardson 		return 0;
112399a2dd95SBruce Richardson 
112499a2dd95SBruce Richardson 	rule_key_init(&rule_key, ip, depth);
112599a2dd95SBruce Richardson 
112699a2dd95SBruce Richardson 	while (depth > 1) {
112799a2dd95SBruce Richardson 		depth--;
112899a2dd95SBruce Richardson 
112999a2dd95SBruce Richardson 		/* each iteration zero one more bit of the key */
113099a2dd95SBruce Richardson 		mask = depth & 7; /* depth % BYTE_SIZE */
113199a2dd95SBruce Richardson 		if (mask > 0)
113299a2dd95SBruce Richardson 			mask = depth_to_mask_1b(mask);
113399a2dd95SBruce Richardson 
113499a2dd95SBruce Richardson 		rule_key.depth = depth;
1135*e1a06e39SRobin Jarry 		rule_key.ip.a[depth >> 3] &= mask;
113699a2dd95SBruce Richardson 
113799a2dd95SBruce Richardson 		ret = rule_find_with_key(lpm, &rule_key, &next_hop);
113899a2dd95SBruce Richardson 		if (ret) {
113999a2dd95SBruce Richardson 			rule->depth = depth;
1140*e1a06e39SRobin Jarry 			rule->ip = rule_key.ip;
114199a2dd95SBruce Richardson 			rule->next_hop = next_hop;
114299a2dd95SBruce Richardson 			return 1;
114399a2dd95SBruce Richardson 		}
114499a2dd95SBruce Richardson 	}
114599a2dd95SBruce Richardson 
114699a2dd95SBruce Richardson 	return 0;
114799a2dd95SBruce Richardson }
114899a2dd95SBruce Richardson 
114999a2dd95SBruce Richardson /*
115099a2dd95SBruce Richardson  * Find range of tbl8 cells occupied by a rule
115199a2dd95SBruce Richardson  */
115299a2dd95SBruce Richardson static void
1153*e1a06e39SRobin Jarry rule_find_range(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth,
115499a2dd95SBruce Richardson 		  struct rte_lpm6_tbl_entry **from,
115599a2dd95SBruce Richardson 		  struct rte_lpm6_tbl_entry **to,
115699a2dd95SBruce Richardson 		  uint32_t *out_tbl_ind)
115799a2dd95SBruce Richardson {
115899a2dd95SBruce Richardson 	uint32_t ind;
1159*e1a06e39SRobin Jarry 	uint32_t first_3bytes = (uint32_t)ip->a[0] << 16 |
1160*e1a06e39SRobin Jarry 			ip->a[1] << 8 | ip->a[2];
116199a2dd95SBruce Richardson 
116299a2dd95SBruce Richardson 	if (depth <= 24) {
116399a2dd95SBruce Richardson 		/* rule is within the top level */
116499a2dd95SBruce Richardson 		ind = first_3bytes;
116599a2dd95SBruce Richardson 		*from = &lpm->tbl24[ind];
116699a2dd95SBruce Richardson 		ind += (1 << (24 - depth)) - 1;
116799a2dd95SBruce Richardson 		*to = &lpm->tbl24[ind];
116899a2dd95SBruce Richardson 		*out_tbl_ind = TBL24_IND;
116999a2dd95SBruce Richardson 	} else {
117099a2dd95SBruce Richardson 		/* top level entry */
117199a2dd95SBruce Richardson 		struct rte_lpm6_tbl_entry *tbl = &lpm->tbl24[first_3bytes];
117299a2dd95SBruce Richardson 		assert(tbl->ext_entry == 1);
117399a2dd95SBruce Richardson 		/* first tbl8 */
117499a2dd95SBruce Richardson 		uint32_t tbl_ind = tbl->lpm6_tbl8_gindex;
117599a2dd95SBruce Richardson 		tbl = &lpm->tbl8[tbl_ind *
117699a2dd95SBruce Richardson 				RTE_LPM6_TBL8_GROUP_NUM_ENTRIES];
117799a2dd95SBruce Richardson 		/* current ip byte, the top level is already behind */
117899a2dd95SBruce Richardson 		uint8_t byte = 3;
117999a2dd95SBruce Richardson 		/* minus top level */
118099a2dd95SBruce Richardson 		depth -= 24;
118199a2dd95SBruce Richardson 
118299a2dd95SBruce Richardson 		/* iterate through levels (tbl8s)
118399a2dd95SBruce Richardson 		 * until we reach the last one
118499a2dd95SBruce Richardson 		 */
118599a2dd95SBruce Richardson 		while (depth > 8) {
1186*e1a06e39SRobin Jarry 			tbl += ip->a[byte];
118799a2dd95SBruce Richardson 			assert(tbl->ext_entry == 1);
118899a2dd95SBruce Richardson 			/* go to the next level/tbl8 */
118999a2dd95SBruce Richardson 			tbl_ind = tbl->lpm6_tbl8_gindex;
119099a2dd95SBruce Richardson 			tbl = &lpm->tbl8[tbl_ind *
119199a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES];
119299a2dd95SBruce Richardson 			byte += 1;
119399a2dd95SBruce Richardson 			depth -= 8;
119499a2dd95SBruce Richardson 		}
119599a2dd95SBruce Richardson 
119699a2dd95SBruce Richardson 		/* last level/tbl8 */
1197*e1a06e39SRobin Jarry 		ind = ip->a[byte] & depth_to_mask_1b(depth);
119899a2dd95SBruce Richardson 		*from = &tbl[ind];
119999a2dd95SBruce Richardson 		ind += (1 << (8 - depth)) - 1;
120099a2dd95SBruce Richardson 		*to = &tbl[ind];
120199a2dd95SBruce Richardson 		*out_tbl_ind = tbl_ind;
120299a2dd95SBruce Richardson 	}
120399a2dd95SBruce Richardson }
120499a2dd95SBruce Richardson 
120599a2dd95SBruce Richardson /*
120699a2dd95SBruce Richardson  * Remove a table from the LPM tree
120799a2dd95SBruce Richardson  */
120899a2dd95SBruce Richardson static void
120999a2dd95SBruce Richardson remove_tbl(struct rte_lpm6 *lpm, struct rte_lpm_tbl8_hdr *tbl_hdr,
121099a2dd95SBruce Richardson 		  uint32_t tbl_ind, struct rte_lpm6_rule *lsp_rule)
121199a2dd95SBruce Richardson {
121299a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *owner_entry;
121399a2dd95SBruce Richardson 
121499a2dd95SBruce Richardson 	if (tbl_hdr->owner_tbl_ind == TBL24_IND)
121599a2dd95SBruce Richardson 		owner_entry = &lpm->tbl24[tbl_hdr->owner_entry_ind];
121699a2dd95SBruce Richardson 	else {
121799a2dd95SBruce Richardson 		uint32_t owner_tbl_ind = tbl_hdr->owner_tbl_ind;
121899a2dd95SBruce Richardson 		owner_entry = &lpm->tbl8[
121999a2dd95SBruce Richardson 			owner_tbl_ind * RTE_LPM6_TBL8_GROUP_NUM_ENTRIES +
122099a2dd95SBruce Richardson 			tbl_hdr->owner_entry_ind];
122199a2dd95SBruce Richardson 
122299a2dd95SBruce Richardson 		struct rte_lpm_tbl8_hdr *owner_tbl_hdr =
122399a2dd95SBruce Richardson 			&lpm->tbl8_hdrs[owner_tbl_ind];
122499a2dd95SBruce Richardson 		if (--owner_tbl_hdr->ref_cnt == 0)
122599a2dd95SBruce Richardson 			remove_tbl(lpm, owner_tbl_hdr, owner_tbl_ind, lsp_rule);
122699a2dd95SBruce Richardson 	}
122799a2dd95SBruce Richardson 
122899a2dd95SBruce Richardson 	assert(owner_entry->ext_entry == 1);
122999a2dd95SBruce Richardson 
123099a2dd95SBruce Richardson 	/* unlink the table */
123199a2dd95SBruce Richardson 	if (lsp_rule != NULL) {
123299a2dd95SBruce Richardson 		struct rte_lpm6_tbl_entry new_tbl_entry = {
123399a2dd95SBruce Richardson 			.next_hop = lsp_rule->next_hop,
123499a2dd95SBruce Richardson 			.depth = lsp_rule->depth,
123599a2dd95SBruce Richardson 			.valid = VALID,
123699a2dd95SBruce Richardson 			.valid_group = VALID,
123799a2dd95SBruce Richardson 			.ext_entry = 0
123899a2dd95SBruce Richardson 		};
123999a2dd95SBruce Richardson 
124099a2dd95SBruce Richardson 		*owner_entry = new_tbl_entry;
124199a2dd95SBruce Richardson 	} else {
124299a2dd95SBruce Richardson 		struct rte_lpm6_tbl_entry new_tbl_entry = {
124399a2dd95SBruce Richardson 			.next_hop = 0,
124499a2dd95SBruce Richardson 			.depth = 0,
124599a2dd95SBruce Richardson 			.valid = INVALID,
124699a2dd95SBruce Richardson 			.valid_group = INVALID,
124799a2dd95SBruce Richardson 			.ext_entry = 0
124899a2dd95SBruce Richardson 		};
124999a2dd95SBruce Richardson 
125099a2dd95SBruce Richardson 		*owner_entry = new_tbl_entry;
125199a2dd95SBruce Richardson 	}
125299a2dd95SBruce Richardson 
125399a2dd95SBruce Richardson 	/* return the table to the pool */
125499a2dd95SBruce Richardson 	tbl8_put(lpm, tbl_ind);
125599a2dd95SBruce Richardson }
125699a2dd95SBruce Richardson 
125799a2dd95SBruce Richardson /*
125899a2dd95SBruce Richardson  * Deletes a rule
125999a2dd95SBruce Richardson  */
126099a2dd95SBruce Richardson int
1261*e1a06e39SRobin Jarry rte_lpm6_delete(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth)
126299a2dd95SBruce Richardson {
1263*e1a06e39SRobin Jarry 	struct rte_ipv6_addr masked_ip;
126499a2dd95SBruce Richardson 	struct rte_lpm6_rule lsp_rule_obj;
126599a2dd95SBruce Richardson 	struct rte_lpm6_rule *lsp_rule;
126699a2dd95SBruce Richardson 	int ret;
126799a2dd95SBruce Richardson 	uint32_t tbl_ind;
126899a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *from, *to;
126999a2dd95SBruce Richardson 
127099a2dd95SBruce Richardson 	/* Check input arguments. */
1271*e1a06e39SRobin Jarry 	if ((lpm == NULL) || (depth < 1) || (depth > RTE_IPV6_MAX_DEPTH))
127299a2dd95SBruce Richardson 		return -EINVAL;
127399a2dd95SBruce Richardson 
127499a2dd95SBruce Richardson 	/* Copy the IP and mask it to avoid modifying user's input data. */
1275*e1a06e39SRobin Jarry 	masked_ip = *ip;
1276*e1a06e39SRobin Jarry 	rte_ipv6_addr_mask(&masked_ip, depth);
127799a2dd95SBruce Richardson 
127899a2dd95SBruce Richardson 	/* Delete the rule from the rule table. */
1279*e1a06e39SRobin Jarry 	ret = rule_delete(lpm, &masked_ip, depth);
128099a2dd95SBruce Richardson 	if (ret < 0)
128199a2dd95SBruce Richardson 		return -ENOENT;
128299a2dd95SBruce Richardson 
128399a2dd95SBruce Richardson 	/* find rule cells */
1284*e1a06e39SRobin Jarry 	rule_find_range(lpm, &masked_ip, depth, &from, &to, &tbl_ind);
128599a2dd95SBruce Richardson 
128699a2dd95SBruce Richardson 	/* find a less specific rule (a rule with smaller depth)
128799a2dd95SBruce Richardson 	 * note: masked_ip will be modified, don't use it anymore
128899a2dd95SBruce Richardson 	 */
1289*e1a06e39SRobin Jarry 	ret = rule_find_less_specific(lpm, &masked_ip, depth,
129099a2dd95SBruce Richardson 			&lsp_rule_obj);
129199a2dd95SBruce Richardson 	lsp_rule = ret ? &lsp_rule_obj : NULL;
129299a2dd95SBruce Richardson 
129399a2dd95SBruce Richardson 	/* decrement the table rule counter,
129499a2dd95SBruce Richardson 	 * note that tbl24 doesn't have a header
129599a2dd95SBruce Richardson 	 */
129699a2dd95SBruce Richardson 	if (tbl_ind != TBL24_IND) {
129799a2dd95SBruce Richardson 		struct rte_lpm_tbl8_hdr *tbl_hdr = &lpm->tbl8_hdrs[tbl_ind];
129899a2dd95SBruce Richardson 		if (--tbl_hdr->ref_cnt == 0) {
129999a2dd95SBruce Richardson 			/* remove the table */
130099a2dd95SBruce Richardson 			remove_tbl(lpm, tbl_hdr, tbl_ind, lsp_rule);
130199a2dd95SBruce Richardson 			return 0;
130299a2dd95SBruce Richardson 		}
130399a2dd95SBruce Richardson 	}
130499a2dd95SBruce Richardson 
130599a2dd95SBruce Richardson 	/* iterate rule cells */
130699a2dd95SBruce Richardson 	for (; from <= to; from++)
130799a2dd95SBruce Richardson 		if (from->ext_entry == 1) {
130899a2dd95SBruce Richardson 			/* reference to a more specific space
130999a2dd95SBruce Richardson 			 * of the prefix/rule. Entries in a more
131099a2dd95SBruce Richardson 			 * specific space that are not used by
131199a2dd95SBruce Richardson 			 * a more specific prefix must be occupied
131299a2dd95SBruce Richardson 			 * by the prefix
131399a2dd95SBruce Richardson 			 */
131499a2dd95SBruce Richardson 			if (lsp_rule != NULL)
131599a2dd95SBruce Richardson 				expand_rule(lpm,
131699a2dd95SBruce Richardson 					from->lpm6_tbl8_gindex *
131799a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES,
131899a2dd95SBruce Richardson 					depth, lsp_rule->depth,
131999a2dd95SBruce Richardson 					lsp_rule->next_hop, VALID);
132099a2dd95SBruce Richardson 			else
132199a2dd95SBruce Richardson 				/* since the prefix has no less specific prefix,
132299a2dd95SBruce Richardson 				 * its more specific space must be invalidated
132399a2dd95SBruce Richardson 				 */
132499a2dd95SBruce Richardson 				expand_rule(lpm,
132599a2dd95SBruce Richardson 					from->lpm6_tbl8_gindex *
132699a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES,
132799a2dd95SBruce Richardson 					depth, 0, 0, INVALID);
132899a2dd95SBruce Richardson 		} else if (from->depth == depth) {
132999a2dd95SBruce Richardson 			/* entry is not a reference and belongs to the prefix */
133099a2dd95SBruce Richardson 			if (lsp_rule != NULL) {
133199a2dd95SBruce Richardson 				struct rte_lpm6_tbl_entry new_tbl_entry = {
133299a2dd95SBruce Richardson 					.next_hop = lsp_rule->next_hop,
133399a2dd95SBruce Richardson 					.depth = lsp_rule->depth,
133499a2dd95SBruce Richardson 					.valid = VALID,
133599a2dd95SBruce Richardson 					.valid_group = VALID,
133699a2dd95SBruce Richardson 					.ext_entry = 0
133799a2dd95SBruce Richardson 				};
133899a2dd95SBruce Richardson 
133999a2dd95SBruce Richardson 				*from = new_tbl_entry;
134099a2dd95SBruce Richardson 			} else {
134199a2dd95SBruce Richardson 				struct rte_lpm6_tbl_entry new_tbl_entry = {
134299a2dd95SBruce Richardson 					.next_hop = 0,
134399a2dd95SBruce Richardson 					.depth = 0,
134499a2dd95SBruce Richardson 					.valid = INVALID,
134599a2dd95SBruce Richardson 					.valid_group = INVALID,
134699a2dd95SBruce Richardson 					.ext_entry = 0
134799a2dd95SBruce Richardson 				};
134899a2dd95SBruce Richardson 
134999a2dd95SBruce Richardson 				*from = new_tbl_entry;
135099a2dd95SBruce Richardson 			}
135199a2dd95SBruce Richardson 		}
135299a2dd95SBruce Richardson 
135399a2dd95SBruce Richardson 	return 0;
135499a2dd95SBruce Richardson }
1355