xref: /dpdk/lib/lpm/rte_lpm.c (revision 99a2dd955fba6e4cc23b77d590a033650ced9c45)
1*99a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
2*99a2dd95SBruce Richardson  * Copyright(c) 2010-2014 Intel Corporation
3*99a2dd95SBruce Richardson  * Copyright(c) 2020 Arm Limited
4*99a2dd95SBruce Richardson  */
5*99a2dd95SBruce Richardson 
6*99a2dd95SBruce Richardson #include <string.h>
7*99a2dd95SBruce Richardson #include <stdint.h>
8*99a2dd95SBruce Richardson #include <errno.h>
9*99a2dd95SBruce Richardson #include <stdarg.h>
10*99a2dd95SBruce Richardson #include <stdio.h>
11*99a2dd95SBruce Richardson #include <sys/queue.h>
12*99a2dd95SBruce Richardson 
13*99a2dd95SBruce Richardson #include <rte_log.h>
14*99a2dd95SBruce Richardson #include <rte_branch_prediction.h>
15*99a2dd95SBruce Richardson #include <rte_common.h>
16*99a2dd95SBruce Richardson #include <rte_memory.h>        /* for definition of RTE_CACHE_LINE_SIZE */
17*99a2dd95SBruce Richardson #include <rte_malloc.h>
18*99a2dd95SBruce Richardson #include <rte_eal.h>
19*99a2dd95SBruce Richardson #include <rte_eal_memconfig.h>
20*99a2dd95SBruce Richardson #include <rte_per_lcore.h>
21*99a2dd95SBruce Richardson #include <rte_string_fns.h>
22*99a2dd95SBruce Richardson #include <rte_errno.h>
23*99a2dd95SBruce Richardson #include <rte_rwlock.h>
24*99a2dd95SBruce Richardson #include <rte_spinlock.h>
25*99a2dd95SBruce Richardson #include <rte_tailq.h>
26*99a2dd95SBruce Richardson 
27*99a2dd95SBruce Richardson #include "rte_lpm.h"
28*99a2dd95SBruce Richardson 
29*99a2dd95SBruce Richardson TAILQ_HEAD(rte_lpm_list, rte_tailq_entry);
30*99a2dd95SBruce Richardson 
31*99a2dd95SBruce Richardson static struct rte_tailq_elem rte_lpm_tailq = {
32*99a2dd95SBruce Richardson 	.name = "RTE_LPM",
33*99a2dd95SBruce Richardson };
34*99a2dd95SBruce Richardson EAL_REGISTER_TAILQ(rte_lpm_tailq)
35*99a2dd95SBruce Richardson 
36*99a2dd95SBruce Richardson #define MAX_DEPTH_TBL24 24
37*99a2dd95SBruce Richardson 
38*99a2dd95SBruce Richardson enum valid_flag {
39*99a2dd95SBruce Richardson 	INVALID = 0,
40*99a2dd95SBruce Richardson 	VALID
41*99a2dd95SBruce Richardson };
42*99a2dd95SBruce Richardson 
43*99a2dd95SBruce Richardson /** @internal Rule structure. */
44*99a2dd95SBruce Richardson struct rte_lpm_rule {
45*99a2dd95SBruce Richardson 	uint32_t ip; /**< Rule IP address. */
46*99a2dd95SBruce Richardson 	uint32_t next_hop; /**< Rule next hop. */
47*99a2dd95SBruce Richardson };
48*99a2dd95SBruce Richardson 
49*99a2dd95SBruce Richardson /** @internal Contains metadata about the rules table. */
50*99a2dd95SBruce Richardson struct rte_lpm_rule_info {
51*99a2dd95SBruce Richardson 	uint32_t used_rules; /**< Used rules so far. */
52*99a2dd95SBruce Richardson 	uint32_t first_rule; /**< Indexes the first rule of a given depth. */
53*99a2dd95SBruce Richardson };
54*99a2dd95SBruce Richardson 
55*99a2dd95SBruce Richardson /** @internal LPM structure. */
56*99a2dd95SBruce Richardson struct __rte_lpm {
57*99a2dd95SBruce Richardson 	/* Exposed LPM data. */
58*99a2dd95SBruce Richardson 	struct rte_lpm lpm;
59*99a2dd95SBruce Richardson 
60*99a2dd95SBruce Richardson 	/* LPM metadata. */
61*99a2dd95SBruce Richardson 	char name[RTE_LPM_NAMESIZE];        /**< Name of the lpm. */
62*99a2dd95SBruce Richardson 	uint32_t max_rules; /**< Max. balanced rules per lpm. */
63*99a2dd95SBruce Richardson 	uint32_t number_tbl8s; /**< Number of tbl8s. */
64*99a2dd95SBruce Richardson 	/**< Rule info table. */
65*99a2dd95SBruce Richardson 	struct rte_lpm_rule_info rule_info[RTE_LPM_MAX_DEPTH];
66*99a2dd95SBruce Richardson 	struct rte_lpm_rule *rules_tbl; /**< LPM rules. */
67*99a2dd95SBruce Richardson 
68*99a2dd95SBruce Richardson 	/* RCU config. */
69*99a2dd95SBruce Richardson 	struct rte_rcu_qsbr *v;		/* RCU QSBR variable. */
70*99a2dd95SBruce Richardson 	enum rte_lpm_qsbr_mode rcu_mode;/* Blocking, defer queue. */
71*99a2dd95SBruce Richardson 	struct rte_rcu_qsbr_dq *dq;	/* RCU QSBR defer queue. */
72*99a2dd95SBruce Richardson };
73*99a2dd95SBruce Richardson 
74*99a2dd95SBruce Richardson /* Macro to enable/disable run-time checks. */
75*99a2dd95SBruce Richardson #if defined(RTE_LIBRTE_LPM_DEBUG)
76*99a2dd95SBruce Richardson #include <rte_debug.h>
77*99a2dd95SBruce Richardson #define VERIFY_DEPTH(depth) do {                                \
78*99a2dd95SBruce Richardson 	if ((depth == 0) || (depth > RTE_LPM_MAX_DEPTH))        \
79*99a2dd95SBruce Richardson 		rte_panic("LPM: Invalid depth (%u) at line %d", \
80*99a2dd95SBruce Richardson 				(unsigned)(depth), __LINE__);   \
81*99a2dd95SBruce Richardson } while (0)
82*99a2dd95SBruce Richardson #else
83*99a2dd95SBruce Richardson #define VERIFY_DEPTH(depth)
84*99a2dd95SBruce Richardson #endif
85*99a2dd95SBruce Richardson 
86*99a2dd95SBruce Richardson /*
87*99a2dd95SBruce Richardson  * Converts a given depth value to its corresponding mask value.
88*99a2dd95SBruce Richardson  *
89*99a2dd95SBruce Richardson  * depth  (IN)		: range = 1 - 32
90*99a2dd95SBruce Richardson  * mask   (OUT)		: 32bit mask
91*99a2dd95SBruce Richardson  */
92*99a2dd95SBruce Richardson static uint32_t __attribute__((pure))
93*99a2dd95SBruce Richardson depth_to_mask(uint8_t depth)
94*99a2dd95SBruce Richardson {
95*99a2dd95SBruce Richardson 	VERIFY_DEPTH(depth);
96*99a2dd95SBruce Richardson 
97*99a2dd95SBruce Richardson 	/* To calculate a mask start with a 1 on the left hand side and right
98*99a2dd95SBruce Richardson 	 * shift while populating the left hand side with 1's
99*99a2dd95SBruce Richardson 	 */
100*99a2dd95SBruce Richardson 	return (int)0x80000000 >> (depth - 1);
101*99a2dd95SBruce Richardson }
102*99a2dd95SBruce Richardson 
103*99a2dd95SBruce Richardson /*
104*99a2dd95SBruce Richardson  * Converts given depth value to its corresponding range value.
105*99a2dd95SBruce Richardson  */
106*99a2dd95SBruce Richardson static uint32_t __attribute__((pure))
107*99a2dd95SBruce Richardson depth_to_range(uint8_t depth)
108*99a2dd95SBruce Richardson {
109*99a2dd95SBruce Richardson 	VERIFY_DEPTH(depth);
110*99a2dd95SBruce Richardson 
111*99a2dd95SBruce Richardson 	/*
112*99a2dd95SBruce Richardson 	 * Calculate tbl24 range. (Note: 2^depth = 1 << depth)
113*99a2dd95SBruce Richardson 	 */
114*99a2dd95SBruce Richardson 	if (depth <= MAX_DEPTH_TBL24)
115*99a2dd95SBruce Richardson 		return 1 << (MAX_DEPTH_TBL24 - depth);
116*99a2dd95SBruce Richardson 
117*99a2dd95SBruce Richardson 	/* Else if depth is greater than 24 */
118*99a2dd95SBruce Richardson 	return 1 << (RTE_LPM_MAX_DEPTH - depth);
119*99a2dd95SBruce Richardson }
120*99a2dd95SBruce Richardson 
121*99a2dd95SBruce Richardson /*
122*99a2dd95SBruce Richardson  * Find an existing lpm table and return a pointer to it.
123*99a2dd95SBruce Richardson  */
124*99a2dd95SBruce Richardson struct rte_lpm *
125*99a2dd95SBruce Richardson rte_lpm_find_existing(const char *name)
126*99a2dd95SBruce Richardson {
127*99a2dd95SBruce Richardson 	struct __rte_lpm *i_lpm = NULL;
128*99a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
129*99a2dd95SBruce Richardson 	struct rte_lpm_list *lpm_list;
130*99a2dd95SBruce Richardson 
131*99a2dd95SBruce Richardson 	lpm_list = RTE_TAILQ_CAST(rte_lpm_tailq.head, rte_lpm_list);
132*99a2dd95SBruce Richardson 
133*99a2dd95SBruce Richardson 	rte_mcfg_tailq_read_lock();
134*99a2dd95SBruce Richardson 	TAILQ_FOREACH(te, lpm_list, next) {
135*99a2dd95SBruce Richardson 		i_lpm = te->data;
136*99a2dd95SBruce Richardson 		if (strncmp(name, i_lpm->name, RTE_LPM_NAMESIZE) == 0)
137*99a2dd95SBruce Richardson 			break;
138*99a2dd95SBruce Richardson 	}
139*99a2dd95SBruce Richardson 	rte_mcfg_tailq_read_unlock();
140*99a2dd95SBruce Richardson 
141*99a2dd95SBruce Richardson 	if (te == NULL) {
142*99a2dd95SBruce Richardson 		rte_errno = ENOENT;
143*99a2dd95SBruce Richardson 		return NULL;
144*99a2dd95SBruce Richardson 	}
145*99a2dd95SBruce Richardson 
146*99a2dd95SBruce Richardson 	return &i_lpm->lpm;
147*99a2dd95SBruce Richardson }
148*99a2dd95SBruce Richardson 
149*99a2dd95SBruce Richardson /*
150*99a2dd95SBruce Richardson  * Allocates memory for LPM object
151*99a2dd95SBruce Richardson  */
152*99a2dd95SBruce Richardson struct rte_lpm *
153*99a2dd95SBruce Richardson rte_lpm_create(const char *name, int socket_id,
154*99a2dd95SBruce Richardson 		const struct rte_lpm_config *config)
155*99a2dd95SBruce Richardson {
156*99a2dd95SBruce Richardson 	char mem_name[RTE_LPM_NAMESIZE];
157*99a2dd95SBruce Richardson 	struct __rte_lpm *i_lpm;
158*99a2dd95SBruce Richardson 	struct rte_lpm *lpm = NULL;
159*99a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
160*99a2dd95SBruce Richardson 	uint32_t mem_size, rules_size, tbl8s_size;
161*99a2dd95SBruce Richardson 	struct rte_lpm_list *lpm_list;
162*99a2dd95SBruce Richardson 
163*99a2dd95SBruce Richardson 	lpm_list = RTE_TAILQ_CAST(rte_lpm_tailq.head, rte_lpm_list);
164*99a2dd95SBruce Richardson 
165*99a2dd95SBruce Richardson 	RTE_BUILD_BUG_ON(sizeof(struct rte_lpm_tbl_entry) != 4);
166*99a2dd95SBruce Richardson 
167*99a2dd95SBruce Richardson 	/* Check user arguments. */
168*99a2dd95SBruce Richardson 	if ((name == NULL) || (socket_id < -1) || (config->max_rules == 0)
169*99a2dd95SBruce Richardson 			|| config->number_tbl8s > RTE_LPM_MAX_TBL8_NUM_GROUPS) {
170*99a2dd95SBruce Richardson 		rte_errno = EINVAL;
171*99a2dd95SBruce Richardson 		return NULL;
172*99a2dd95SBruce Richardson 	}
173*99a2dd95SBruce Richardson 
174*99a2dd95SBruce Richardson 	snprintf(mem_name, sizeof(mem_name), "LPM_%s", name);
175*99a2dd95SBruce Richardson 
176*99a2dd95SBruce Richardson 	rte_mcfg_tailq_write_lock();
177*99a2dd95SBruce Richardson 
178*99a2dd95SBruce Richardson 	/* guarantee there's no existing */
179*99a2dd95SBruce Richardson 	TAILQ_FOREACH(te, lpm_list, next) {
180*99a2dd95SBruce Richardson 		i_lpm = te->data;
181*99a2dd95SBruce Richardson 		if (strncmp(name, i_lpm->name, RTE_LPM_NAMESIZE) == 0)
182*99a2dd95SBruce Richardson 			break;
183*99a2dd95SBruce Richardson 	}
184*99a2dd95SBruce Richardson 
185*99a2dd95SBruce Richardson 	if (te != NULL) {
186*99a2dd95SBruce Richardson 		rte_errno = EEXIST;
187*99a2dd95SBruce Richardson 		goto exit;
188*99a2dd95SBruce Richardson 	}
189*99a2dd95SBruce Richardson 
190*99a2dd95SBruce Richardson 	/* Determine the amount of memory to allocate. */
191*99a2dd95SBruce Richardson 	mem_size = sizeof(*i_lpm);
192*99a2dd95SBruce Richardson 	rules_size = sizeof(struct rte_lpm_rule) * config->max_rules;
193*99a2dd95SBruce Richardson 	tbl8s_size = sizeof(struct rte_lpm_tbl_entry) *
194*99a2dd95SBruce Richardson 			RTE_LPM_TBL8_GROUP_NUM_ENTRIES * config->number_tbl8s;
195*99a2dd95SBruce Richardson 
196*99a2dd95SBruce Richardson 	/* allocate tailq entry */
197*99a2dd95SBruce Richardson 	te = rte_zmalloc("LPM_TAILQ_ENTRY", sizeof(*te), 0);
198*99a2dd95SBruce Richardson 	if (te == NULL) {
199*99a2dd95SBruce Richardson 		RTE_LOG(ERR, LPM, "Failed to allocate tailq entry\n");
200*99a2dd95SBruce Richardson 		rte_errno = ENOMEM;
201*99a2dd95SBruce Richardson 		goto exit;
202*99a2dd95SBruce Richardson 	}
203*99a2dd95SBruce Richardson 
204*99a2dd95SBruce Richardson 	/* Allocate memory to store the LPM data structures. */
205*99a2dd95SBruce Richardson 	i_lpm = rte_zmalloc_socket(mem_name, mem_size,
206*99a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE, socket_id);
207*99a2dd95SBruce Richardson 	if (i_lpm == NULL) {
208*99a2dd95SBruce Richardson 		RTE_LOG(ERR, LPM, "LPM memory allocation failed\n");
209*99a2dd95SBruce Richardson 		rte_free(te);
210*99a2dd95SBruce Richardson 		rte_errno = ENOMEM;
211*99a2dd95SBruce Richardson 		goto exit;
212*99a2dd95SBruce Richardson 	}
213*99a2dd95SBruce Richardson 
214*99a2dd95SBruce Richardson 	i_lpm->rules_tbl = rte_zmalloc_socket(NULL,
215*99a2dd95SBruce Richardson 			(size_t)rules_size, RTE_CACHE_LINE_SIZE, socket_id);
216*99a2dd95SBruce Richardson 
217*99a2dd95SBruce Richardson 	if (i_lpm->rules_tbl == NULL) {
218*99a2dd95SBruce Richardson 		RTE_LOG(ERR, LPM, "LPM rules_tbl memory allocation failed\n");
219*99a2dd95SBruce Richardson 		rte_free(i_lpm);
220*99a2dd95SBruce Richardson 		i_lpm = NULL;
221*99a2dd95SBruce Richardson 		rte_free(te);
222*99a2dd95SBruce Richardson 		rte_errno = ENOMEM;
223*99a2dd95SBruce Richardson 		goto exit;
224*99a2dd95SBruce Richardson 	}
225*99a2dd95SBruce Richardson 
226*99a2dd95SBruce Richardson 	i_lpm->lpm.tbl8 = rte_zmalloc_socket(NULL,
227*99a2dd95SBruce Richardson 			(size_t)tbl8s_size, RTE_CACHE_LINE_SIZE, socket_id);
228*99a2dd95SBruce Richardson 
229*99a2dd95SBruce Richardson 	if (i_lpm->lpm.tbl8 == NULL) {
230*99a2dd95SBruce Richardson 		RTE_LOG(ERR, LPM, "LPM tbl8 memory allocation failed\n");
231*99a2dd95SBruce Richardson 		rte_free(i_lpm->rules_tbl);
232*99a2dd95SBruce Richardson 		rte_free(i_lpm);
233*99a2dd95SBruce Richardson 		i_lpm = NULL;
234*99a2dd95SBruce Richardson 		rte_free(te);
235*99a2dd95SBruce Richardson 		rte_errno = ENOMEM;
236*99a2dd95SBruce Richardson 		goto exit;
237*99a2dd95SBruce Richardson 	}
238*99a2dd95SBruce Richardson 
239*99a2dd95SBruce Richardson 	/* Save user arguments. */
240*99a2dd95SBruce Richardson 	i_lpm->max_rules = config->max_rules;
241*99a2dd95SBruce Richardson 	i_lpm->number_tbl8s = config->number_tbl8s;
242*99a2dd95SBruce Richardson 	strlcpy(i_lpm->name, name, sizeof(i_lpm->name));
243*99a2dd95SBruce Richardson 
244*99a2dd95SBruce Richardson 	te->data = i_lpm;
245*99a2dd95SBruce Richardson 	lpm = &i_lpm->lpm;
246*99a2dd95SBruce Richardson 
247*99a2dd95SBruce Richardson 	TAILQ_INSERT_TAIL(lpm_list, te, next);
248*99a2dd95SBruce Richardson 
249*99a2dd95SBruce Richardson exit:
250*99a2dd95SBruce Richardson 	rte_mcfg_tailq_write_unlock();
251*99a2dd95SBruce Richardson 
252*99a2dd95SBruce Richardson 	return lpm;
253*99a2dd95SBruce Richardson }
254*99a2dd95SBruce Richardson 
255*99a2dd95SBruce Richardson /*
256*99a2dd95SBruce Richardson  * Deallocates memory for given LPM table.
257*99a2dd95SBruce Richardson  */
258*99a2dd95SBruce Richardson void
259*99a2dd95SBruce Richardson rte_lpm_free(struct rte_lpm *lpm)
260*99a2dd95SBruce Richardson {
261*99a2dd95SBruce Richardson 	struct rte_lpm_list *lpm_list;
262*99a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
263*99a2dd95SBruce Richardson 	struct __rte_lpm *i_lpm;
264*99a2dd95SBruce Richardson 
265*99a2dd95SBruce Richardson 	/* Check user arguments. */
266*99a2dd95SBruce Richardson 	if (lpm == NULL)
267*99a2dd95SBruce Richardson 		return;
268*99a2dd95SBruce Richardson 	i_lpm = container_of(lpm, struct __rte_lpm, lpm);
269*99a2dd95SBruce Richardson 
270*99a2dd95SBruce Richardson 	lpm_list = RTE_TAILQ_CAST(rte_lpm_tailq.head, rte_lpm_list);
271*99a2dd95SBruce Richardson 
272*99a2dd95SBruce Richardson 	rte_mcfg_tailq_write_lock();
273*99a2dd95SBruce Richardson 
274*99a2dd95SBruce Richardson 	/* find our tailq entry */
275*99a2dd95SBruce Richardson 	TAILQ_FOREACH(te, lpm_list, next) {
276*99a2dd95SBruce Richardson 		if (te->data == (void *)i_lpm)
277*99a2dd95SBruce Richardson 			break;
278*99a2dd95SBruce Richardson 	}
279*99a2dd95SBruce Richardson 	if (te != NULL)
280*99a2dd95SBruce Richardson 		TAILQ_REMOVE(lpm_list, te, next);
281*99a2dd95SBruce Richardson 
282*99a2dd95SBruce Richardson 	rte_mcfg_tailq_write_unlock();
283*99a2dd95SBruce Richardson 
284*99a2dd95SBruce Richardson 	if (i_lpm->dq != NULL)
285*99a2dd95SBruce Richardson 		rte_rcu_qsbr_dq_delete(i_lpm->dq);
286*99a2dd95SBruce Richardson 	rte_free(i_lpm->lpm.tbl8);
287*99a2dd95SBruce Richardson 	rte_free(i_lpm->rules_tbl);
288*99a2dd95SBruce Richardson 	rte_free(i_lpm);
289*99a2dd95SBruce Richardson 	rte_free(te);
290*99a2dd95SBruce Richardson }
291*99a2dd95SBruce Richardson 
292*99a2dd95SBruce Richardson static void
293*99a2dd95SBruce Richardson __lpm_rcu_qsbr_free_resource(void *p, void *data, unsigned int n)
294*99a2dd95SBruce Richardson {
295*99a2dd95SBruce Richardson 	struct rte_lpm_tbl_entry *tbl8 = ((struct __rte_lpm *)p)->lpm.tbl8;
296*99a2dd95SBruce Richardson 	struct rte_lpm_tbl_entry zero_tbl8_entry = {0};
297*99a2dd95SBruce Richardson 	uint32_t tbl8_group_index = *(uint32_t *)data;
298*99a2dd95SBruce Richardson 
299*99a2dd95SBruce Richardson 	RTE_SET_USED(n);
300*99a2dd95SBruce Richardson 	/* Set tbl8 group invalid */
301*99a2dd95SBruce Richardson 	__atomic_store(&tbl8[tbl8_group_index], &zero_tbl8_entry,
302*99a2dd95SBruce Richardson 		__ATOMIC_RELAXED);
303*99a2dd95SBruce Richardson }
304*99a2dd95SBruce Richardson 
305*99a2dd95SBruce Richardson /* Associate QSBR variable with an LPM object.
306*99a2dd95SBruce Richardson  */
307*99a2dd95SBruce Richardson int
308*99a2dd95SBruce Richardson rte_lpm_rcu_qsbr_add(struct rte_lpm *lpm, struct rte_lpm_rcu_config *cfg)
309*99a2dd95SBruce Richardson {
310*99a2dd95SBruce Richardson 	struct rte_rcu_qsbr_dq_parameters params = {0};
311*99a2dd95SBruce Richardson 	char rcu_dq_name[RTE_RCU_QSBR_DQ_NAMESIZE];
312*99a2dd95SBruce Richardson 	struct __rte_lpm *i_lpm;
313*99a2dd95SBruce Richardson 
314*99a2dd95SBruce Richardson 	if (lpm == NULL || cfg == NULL) {
315*99a2dd95SBruce Richardson 		rte_errno = EINVAL;
316*99a2dd95SBruce Richardson 		return 1;
317*99a2dd95SBruce Richardson 	}
318*99a2dd95SBruce Richardson 
319*99a2dd95SBruce Richardson 	i_lpm = container_of(lpm, struct __rte_lpm, lpm);
320*99a2dd95SBruce Richardson 	if (i_lpm->v != NULL) {
321*99a2dd95SBruce Richardson 		rte_errno = EEXIST;
322*99a2dd95SBruce Richardson 		return 1;
323*99a2dd95SBruce Richardson 	}
324*99a2dd95SBruce Richardson 
325*99a2dd95SBruce Richardson 	if (cfg->mode == RTE_LPM_QSBR_MODE_SYNC) {
326*99a2dd95SBruce Richardson 		/* No other things to do. */
327*99a2dd95SBruce Richardson 	} else if (cfg->mode == RTE_LPM_QSBR_MODE_DQ) {
328*99a2dd95SBruce Richardson 		/* Init QSBR defer queue. */
329*99a2dd95SBruce Richardson 		snprintf(rcu_dq_name, sizeof(rcu_dq_name),
330*99a2dd95SBruce Richardson 				"LPM_RCU_%s", i_lpm->name);
331*99a2dd95SBruce Richardson 		params.name = rcu_dq_name;
332*99a2dd95SBruce Richardson 		params.size = cfg->dq_size;
333*99a2dd95SBruce Richardson 		if (params.size == 0)
334*99a2dd95SBruce Richardson 			params.size = i_lpm->number_tbl8s;
335*99a2dd95SBruce Richardson 		params.trigger_reclaim_limit = cfg->reclaim_thd;
336*99a2dd95SBruce Richardson 		params.max_reclaim_size = cfg->reclaim_max;
337*99a2dd95SBruce Richardson 		if (params.max_reclaim_size == 0)
338*99a2dd95SBruce Richardson 			params.max_reclaim_size = RTE_LPM_RCU_DQ_RECLAIM_MAX;
339*99a2dd95SBruce Richardson 		params.esize = sizeof(uint32_t);	/* tbl8 group index */
340*99a2dd95SBruce Richardson 		params.free_fn = __lpm_rcu_qsbr_free_resource;
341*99a2dd95SBruce Richardson 		params.p = i_lpm;
342*99a2dd95SBruce Richardson 		params.v = cfg->v;
343*99a2dd95SBruce Richardson 		i_lpm->dq = rte_rcu_qsbr_dq_create(&params);
344*99a2dd95SBruce Richardson 		if (i_lpm->dq == NULL) {
345*99a2dd95SBruce Richardson 			RTE_LOG(ERR, LPM, "LPM defer queue creation failed\n");
346*99a2dd95SBruce Richardson 			return 1;
347*99a2dd95SBruce Richardson 		}
348*99a2dd95SBruce Richardson 	} else {
349*99a2dd95SBruce Richardson 		rte_errno = EINVAL;
350*99a2dd95SBruce Richardson 		return 1;
351*99a2dd95SBruce Richardson 	}
352*99a2dd95SBruce Richardson 	i_lpm->rcu_mode = cfg->mode;
353*99a2dd95SBruce Richardson 	i_lpm->v = cfg->v;
354*99a2dd95SBruce Richardson 
355*99a2dd95SBruce Richardson 	return 0;
356*99a2dd95SBruce Richardson }
357*99a2dd95SBruce Richardson 
358*99a2dd95SBruce Richardson /*
359*99a2dd95SBruce Richardson  * Adds a rule to the rule table.
360*99a2dd95SBruce Richardson  *
361*99a2dd95SBruce Richardson  * NOTE: The rule table is split into 32 groups. Each group contains rules that
362*99a2dd95SBruce Richardson  * apply to a specific prefix depth (i.e. group 1 contains rules that apply to
363*99a2dd95SBruce Richardson  * prefixes with a depth of 1 etc.). In the following code (depth - 1) is used
364*99a2dd95SBruce Richardson  * to refer to depth 1 because even though the depth range is 1 - 32, depths
365*99a2dd95SBruce Richardson  * are stored in the rule table from 0 - 31.
366*99a2dd95SBruce Richardson  * NOTE: Valid range for depth parameter is 1 .. 32 inclusive.
367*99a2dd95SBruce Richardson  */
368*99a2dd95SBruce Richardson static int32_t
369*99a2dd95SBruce Richardson rule_add(struct __rte_lpm *i_lpm, uint32_t ip_masked, uint8_t depth,
370*99a2dd95SBruce Richardson 	uint32_t next_hop)
371*99a2dd95SBruce Richardson {
372*99a2dd95SBruce Richardson 	uint32_t rule_gindex, rule_index, last_rule;
373*99a2dd95SBruce Richardson 	int i;
374*99a2dd95SBruce Richardson 
375*99a2dd95SBruce Richardson 	VERIFY_DEPTH(depth);
376*99a2dd95SBruce Richardson 
377*99a2dd95SBruce Richardson 	/* Scan through rule group to see if rule already exists. */
378*99a2dd95SBruce Richardson 	if (i_lpm->rule_info[depth - 1].used_rules > 0) {
379*99a2dd95SBruce Richardson 
380*99a2dd95SBruce Richardson 		/* rule_gindex stands for rule group index. */
381*99a2dd95SBruce Richardson 		rule_gindex = i_lpm->rule_info[depth - 1].first_rule;
382*99a2dd95SBruce Richardson 		/* Initialise rule_index to point to start of rule group. */
383*99a2dd95SBruce Richardson 		rule_index = rule_gindex;
384*99a2dd95SBruce Richardson 		/* Last rule = Last used rule in this rule group. */
385*99a2dd95SBruce Richardson 		last_rule = rule_gindex + i_lpm->rule_info[depth - 1].used_rules;
386*99a2dd95SBruce Richardson 
387*99a2dd95SBruce Richardson 		for (; rule_index < last_rule; rule_index++) {
388*99a2dd95SBruce Richardson 
389*99a2dd95SBruce Richardson 			/* If rule already exists update next hop and return. */
390*99a2dd95SBruce Richardson 			if (i_lpm->rules_tbl[rule_index].ip == ip_masked) {
391*99a2dd95SBruce Richardson 
392*99a2dd95SBruce Richardson 				if (i_lpm->rules_tbl[rule_index].next_hop
393*99a2dd95SBruce Richardson 						== next_hop)
394*99a2dd95SBruce Richardson 					return -EEXIST;
395*99a2dd95SBruce Richardson 				i_lpm->rules_tbl[rule_index].next_hop = next_hop;
396*99a2dd95SBruce Richardson 
397*99a2dd95SBruce Richardson 				return rule_index;
398*99a2dd95SBruce Richardson 			}
399*99a2dd95SBruce Richardson 		}
400*99a2dd95SBruce Richardson 
401*99a2dd95SBruce Richardson 		if (rule_index == i_lpm->max_rules)
402*99a2dd95SBruce Richardson 			return -ENOSPC;
403*99a2dd95SBruce Richardson 	} else {
404*99a2dd95SBruce Richardson 		/* Calculate the position in which the rule will be stored. */
405*99a2dd95SBruce Richardson 		rule_index = 0;
406*99a2dd95SBruce Richardson 
407*99a2dd95SBruce Richardson 		for (i = depth - 1; i > 0; i--) {
408*99a2dd95SBruce Richardson 			if (i_lpm->rule_info[i - 1].used_rules > 0) {
409*99a2dd95SBruce Richardson 				rule_index = i_lpm->rule_info[i - 1].first_rule
410*99a2dd95SBruce Richardson 						+ i_lpm->rule_info[i - 1].used_rules;
411*99a2dd95SBruce Richardson 				break;
412*99a2dd95SBruce Richardson 			}
413*99a2dd95SBruce Richardson 		}
414*99a2dd95SBruce Richardson 		if (rule_index == i_lpm->max_rules)
415*99a2dd95SBruce Richardson 			return -ENOSPC;
416*99a2dd95SBruce Richardson 
417*99a2dd95SBruce Richardson 		i_lpm->rule_info[depth - 1].first_rule = rule_index;
418*99a2dd95SBruce Richardson 	}
419*99a2dd95SBruce Richardson 
420*99a2dd95SBruce Richardson 	/* Make room for the new rule in the array. */
421*99a2dd95SBruce Richardson 	for (i = RTE_LPM_MAX_DEPTH; i > depth; i--) {
422*99a2dd95SBruce Richardson 		if (i_lpm->rule_info[i - 1].first_rule
423*99a2dd95SBruce Richardson 				+ i_lpm->rule_info[i - 1].used_rules == i_lpm->max_rules)
424*99a2dd95SBruce Richardson 			return -ENOSPC;
425*99a2dd95SBruce Richardson 
426*99a2dd95SBruce Richardson 		if (i_lpm->rule_info[i - 1].used_rules > 0) {
427*99a2dd95SBruce Richardson 			i_lpm->rules_tbl[i_lpm->rule_info[i - 1].first_rule
428*99a2dd95SBruce Richardson 				+ i_lpm->rule_info[i - 1].used_rules]
429*99a2dd95SBruce Richardson 					= i_lpm->rules_tbl[i_lpm->rule_info[i - 1].first_rule];
430*99a2dd95SBruce Richardson 			i_lpm->rule_info[i - 1].first_rule++;
431*99a2dd95SBruce Richardson 		}
432*99a2dd95SBruce Richardson 	}
433*99a2dd95SBruce Richardson 
434*99a2dd95SBruce Richardson 	/* Add the new rule. */
435*99a2dd95SBruce Richardson 	i_lpm->rules_tbl[rule_index].ip = ip_masked;
436*99a2dd95SBruce Richardson 	i_lpm->rules_tbl[rule_index].next_hop = next_hop;
437*99a2dd95SBruce Richardson 
438*99a2dd95SBruce Richardson 	/* Increment the used rules counter for this rule group. */
439*99a2dd95SBruce Richardson 	i_lpm->rule_info[depth - 1].used_rules++;
440*99a2dd95SBruce Richardson 
441*99a2dd95SBruce Richardson 	return rule_index;
442*99a2dd95SBruce Richardson }
443*99a2dd95SBruce Richardson 
444*99a2dd95SBruce Richardson /*
445*99a2dd95SBruce Richardson  * Delete a rule from the rule table.
446*99a2dd95SBruce Richardson  * NOTE: Valid range for depth parameter is 1 .. 32 inclusive.
447*99a2dd95SBruce Richardson  */
448*99a2dd95SBruce Richardson static void
449*99a2dd95SBruce Richardson rule_delete(struct __rte_lpm *i_lpm, int32_t rule_index, uint8_t depth)
450*99a2dd95SBruce Richardson {
451*99a2dd95SBruce Richardson 	int i;
452*99a2dd95SBruce Richardson 
453*99a2dd95SBruce Richardson 	VERIFY_DEPTH(depth);
454*99a2dd95SBruce Richardson 
455*99a2dd95SBruce Richardson 	i_lpm->rules_tbl[rule_index] =
456*99a2dd95SBruce Richardson 			i_lpm->rules_tbl[i_lpm->rule_info[depth - 1].first_rule
457*99a2dd95SBruce Richardson 			+ i_lpm->rule_info[depth - 1].used_rules - 1];
458*99a2dd95SBruce Richardson 
459*99a2dd95SBruce Richardson 	for (i = depth; i < RTE_LPM_MAX_DEPTH; i++) {
460*99a2dd95SBruce Richardson 		if (i_lpm->rule_info[i].used_rules > 0) {
461*99a2dd95SBruce Richardson 			i_lpm->rules_tbl[i_lpm->rule_info[i].first_rule - 1] =
462*99a2dd95SBruce Richardson 					i_lpm->rules_tbl[i_lpm->rule_info[i].first_rule
463*99a2dd95SBruce Richardson 						+ i_lpm->rule_info[i].used_rules - 1];
464*99a2dd95SBruce Richardson 			i_lpm->rule_info[i].first_rule--;
465*99a2dd95SBruce Richardson 		}
466*99a2dd95SBruce Richardson 	}
467*99a2dd95SBruce Richardson 
468*99a2dd95SBruce Richardson 	i_lpm->rule_info[depth - 1].used_rules--;
469*99a2dd95SBruce Richardson }
470*99a2dd95SBruce Richardson 
471*99a2dd95SBruce Richardson /*
472*99a2dd95SBruce Richardson  * Finds a rule in rule table.
473*99a2dd95SBruce Richardson  * NOTE: Valid range for depth parameter is 1 .. 32 inclusive.
474*99a2dd95SBruce Richardson  */
475*99a2dd95SBruce Richardson static int32_t
476*99a2dd95SBruce Richardson rule_find(struct __rte_lpm *i_lpm, uint32_t ip_masked, uint8_t depth)
477*99a2dd95SBruce Richardson {
478*99a2dd95SBruce Richardson 	uint32_t rule_gindex, last_rule, rule_index;
479*99a2dd95SBruce Richardson 
480*99a2dd95SBruce Richardson 	VERIFY_DEPTH(depth);
481*99a2dd95SBruce Richardson 
482*99a2dd95SBruce Richardson 	rule_gindex = i_lpm->rule_info[depth - 1].first_rule;
483*99a2dd95SBruce Richardson 	last_rule = rule_gindex + i_lpm->rule_info[depth - 1].used_rules;
484*99a2dd95SBruce Richardson 
485*99a2dd95SBruce Richardson 	/* Scan used rules at given depth to find rule. */
486*99a2dd95SBruce Richardson 	for (rule_index = rule_gindex; rule_index < last_rule; rule_index++) {
487*99a2dd95SBruce Richardson 		/* If rule is found return the rule index. */
488*99a2dd95SBruce Richardson 		if (i_lpm->rules_tbl[rule_index].ip == ip_masked)
489*99a2dd95SBruce Richardson 			return rule_index;
490*99a2dd95SBruce Richardson 	}
491*99a2dd95SBruce Richardson 
492*99a2dd95SBruce Richardson 	/* If rule is not found return -EINVAL. */
493*99a2dd95SBruce Richardson 	return -EINVAL;
494*99a2dd95SBruce Richardson }
495*99a2dd95SBruce Richardson 
496*99a2dd95SBruce Richardson /*
497*99a2dd95SBruce Richardson  * Find, clean and allocate a tbl8.
498*99a2dd95SBruce Richardson  */
499*99a2dd95SBruce Richardson static int32_t
500*99a2dd95SBruce Richardson _tbl8_alloc(struct __rte_lpm *i_lpm)
501*99a2dd95SBruce Richardson {
502*99a2dd95SBruce Richardson 	uint32_t group_idx; /* tbl8 group index. */
503*99a2dd95SBruce Richardson 	struct rte_lpm_tbl_entry *tbl8_entry;
504*99a2dd95SBruce Richardson 
505*99a2dd95SBruce Richardson 	/* Scan through tbl8 to find a free (i.e. INVALID) tbl8 group. */
506*99a2dd95SBruce Richardson 	for (group_idx = 0; group_idx < i_lpm->number_tbl8s; group_idx++) {
507*99a2dd95SBruce Richardson 		tbl8_entry = &i_lpm->lpm.tbl8[group_idx *
508*99a2dd95SBruce Richardson 					RTE_LPM_TBL8_GROUP_NUM_ENTRIES];
509*99a2dd95SBruce Richardson 		/* If a free tbl8 group is found clean it and set as VALID. */
510*99a2dd95SBruce Richardson 		if (!tbl8_entry->valid_group) {
511*99a2dd95SBruce Richardson 			struct rte_lpm_tbl_entry new_tbl8_entry = {
512*99a2dd95SBruce Richardson 				.next_hop = 0,
513*99a2dd95SBruce Richardson 				.valid = INVALID,
514*99a2dd95SBruce Richardson 				.depth = 0,
515*99a2dd95SBruce Richardson 				.valid_group = VALID,
516*99a2dd95SBruce Richardson 			};
517*99a2dd95SBruce Richardson 
518*99a2dd95SBruce Richardson 			memset(&tbl8_entry[0], 0,
519*99a2dd95SBruce Richardson 					RTE_LPM_TBL8_GROUP_NUM_ENTRIES *
520*99a2dd95SBruce Richardson 					sizeof(tbl8_entry[0]));
521*99a2dd95SBruce Richardson 
522*99a2dd95SBruce Richardson 			__atomic_store(tbl8_entry, &new_tbl8_entry,
523*99a2dd95SBruce Richardson 					__ATOMIC_RELAXED);
524*99a2dd95SBruce Richardson 
525*99a2dd95SBruce Richardson 			/* Return group index for allocated tbl8 group. */
526*99a2dd95SBruce Richardson 			return group_idx;
527*99a2dd95SBruce Richardson 		}
528*99a2dd95SBruce Richardson 	}
529*99a2dd95SBruce Richardson 
530*99a2dd95SBruce Richardson 	/* If there are no tbl8 groups free then return error. */
531*99a2dd95SBruce Richardson 	return -ENOSPC;
532*99a2dd95SBruce Richardson }
533*99a2dd95SBruce Richardson 
534*99a2dd95SBruce Richardson static int32_t
535*99a2dd95SBruce Richardson tbl8_alloc(struct __rte_lpm *i_lpm)
536*99a2dd95SBruce Richardson {
537*99a2dd95SBruce Richardson 	int32_t group_idx; /* tbl8 group index. */
538*99a2dd95SBruce Richardson 
539*99a2dd95SBruce Richardson 	group_idx = _tbl8_alloc(i_lpm);
540*99a2dd95SBruce Richardson 	if (group_idx == -ENOSPC && i_lpm->dq != NULL) {
541*99a2dd95SBruce Richardson 		/* If there are no tbl8 groups try to reclaim one. */
542*99a2dd95SBruce Richardson 		if (rte_rcu_qsbr_dq_reclaim(i_lpm->dq, 1,
543*99a2dd95SBruce Richardson 				NULL, NULL, NULL) == 0)
544*99a2dd95SBruce Richardson 			group_idx = _tbl8_alloc(i_lpm);
545*99a2dd95SBruce Richardson 	}
546*99a2dd95SBruce Richardson 
547*99a2dd95SBruce Richardson 	return group_idx;
548*99a2dd95SBruce Richardson }
549*99a2dd95SBruce Richardson 
550*99a2dd95SBruce Richardson static int32_t
551*99a2dd95SBruce Richardson tbl8_free(struct __rte_lpm *i_lpm, uint32_t tbl8_group_start)
552*99a2dd95SBruce Richardson {
553*99a2dd95SBruce Richardson 	struct rte_lpm_tbl_entry zero_tbl8_entry = {0};
554*99a2dd95SBruce Richardson 	int status;
555*99a2dd95SBruce Richardson 
556*99a2dd95SBruce Richardson 	if (i_lpm->v == NULL) {
557*99a2dd95SBruce Richardson 		/* Set tbl8 group invalid*/
558*99a2dd95SBruce Richardson 		__atomic_store(&i_lpm->lpm.tbl8[tbl8_group_start], &zero_tbl8_entry,
559*99a2dd95SBruce Richardson 				__ATOMIC_RELAXED);
560*99a2dd95SBruce Richardson 	} else if (i_lpm->rcu_mode == RTE_LPM_QSBR_MODE_SYNC) {
561*99a2dd95SBruce Richardson 		/* Wait for quiescent state change. */
562*99a2dd95SBruce Richardson 		rte_rcu_qsbr_synchronize(i_lpm->v,
563*99a2dd95SBruce Richardson 			RTE_QSBR_THRID_INVALID);
564*99a2dd95SBruce Richardson 		/* Set tbl8 group invalid*/
565*99a2dd95SBruce Richardson 		__atomic_store(&i_lpm->lpm.tbl8[tbl8_group_start], &zero_tbl8_entry,
566*99a2dd95SBruce Richardson 				__ATOMIC_RELAXED);
567*99a2dd95SBruce Richardson 	} else if (i_lpm->rcu_mode == RTE_LPM_QSBR_MODE_DQ) {
568*99a2dd95SBruce Richardson 		/* Push into QSBR defer queue. */
569*99a2dd95SBruce Richardson 		status = rte_rcu_qsbr_dq_enqueue(i_lpm->dq,
570*99a2dd95SBruce Richardson 				(void *)&tbl8_group_start);
571*99a2dd95SBruce Richardson 		if (status == 1) {
572*99a2dd95SBruce Richardson 			RTE_LOG(ERR, LPM, "Failed to push QSBR FIFO\n");
573*99a2dd95SBruce Richardson 			return -rte_errno;
574*99a2dd95SBruce Richardson 		}
575*99a2dd95SBruce Richardson 	}
576*99a2dd95SBruce Richardson 
577*99a2dd95SBruce Richardson 	return 0;
578*99a2dd95SBruce Richardson }
579*99a2dd95SBruce Richardson 
580*99a2dd95SBruce Richardson static __rte_noinline int32_t
581*99a2dd95SBruce Richardson add_depth_small(struct __rte_lpm *i_lpm, uint32_t ip, uint8_t depth,
582*99a2dd95SBruce Richardson 		uint32_t next_hop)
583*99a2dd95SBruce Richardson {
584*99a2dd95SBruce Richardson #define group_idx next_hop
585*99a2dd95SBruce Richardson 	uint32_t tbl24_index, tbl24_range, tbl8_index, tbl8_group_end, i, j;
586*99a2dd95SBruce Richardson 
587*99a2dd95SBruce Richardson 	/* Calculate the index into Table24. */
588*99a2dd95SBruce Richardson 	tbl24_index = ip >> 8;
589*99a2dd95SBruce Richardson 	tbl24_range = depth_to_range(depth);
590*99a2dd95SBruce Richardson 
591*99a2dd95SBruce Richardson 	for (i = tbl24_index; i < (tbl24_index + tbl24_range); i++) {
592*99a2dd95SBruce Richardson 		/*
593*99a2dd95SBruce Richardson 		 * For invalid OR valid and non-extended tbl 24 entries set
594*99a2dd95SBruce Richardson 		 * entry.
595*99a2dd95SBruce Richardson 		 */
596*99a2dd95SBruce Richardson 		if (!i_lpm->lpm.tbl24[i].valid || (i_lpm->lpm.tbl24[i].valid_group == 0 &&
597*99a2dd95SBruce Richardson 				i_lpm->lpm.tbl24[i].depth <= depth)) {
598*99a2dd95SBruce Richardson 
599*99a2dd95SBruce Richardson 			struct rte_lpm_tbl_entry new_tbl24_entry = {
600*99a2dd95SBruce Richardson 				.next_hop = next_hop,
601*99a2dd95SBruce Richardson 				.valid = VALID,
602*99a2dd95SBruce Richardson 				.valid_group = 0,
603*99a2dd95SBruce Richardson 				.depth = depth,
604*99a2dd95SBruce Richardson 			};
605*99a2dd95SBruce Richardson 
606*99a2dd95SBruce Richardson 			/* Setting tbl24 entry in one go to avoid race
607*99a2dd95SBruce Richardson 			 * conditions
608*99a2dd95SBruce Richardson 			 */
609*99a2dd95SBruce Richardson 			__atomic_store(&i_lpm->lpm.tbl24[i], &new_tbl24_entry,
610*99a2dd95SBruce Richardson 					__ATOMIC_RELEASE);
611*99a2dd95SBruce Richardson 
612*99a2dd95SBruce Richardson 			continue;
613*99a2dd95SBruce Richardson 		}
614*99a2dd95SBruce Richardson 
615*99a2dd95SBruce Richardson 		if (i_lpm->lpm.tbl24[i].valid_group == 1) {
616*99a2dd95SBruce Richardson 			/* If tbl24 entry is valid and extended calculate the
617*99a2dd95SBruce Richardson 			 *  index into tbl8.
618*99a2dd95SBruce Richardson 			 */
619*99a2dd95SBruce Richardson 			tbl8_index = i_lpm->lpm.tbl24[i].group_idx *
620*99a2dd95SBruce Richardson 					RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
621*99a2dd95SBruce Richardson 			tbl8_group_end = tbl8_index +
622*99a2dd95SBruce Richardson 					RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
623*99a2dd95SBruce Richardson 
624*99a2dd95SBruce Richardson 			for (j = tbl8_index; j < tbl8_group_end; j++) {
625*99a2dd95SBruce Richardson 				if (!i_lpm->lpm.tbl8[j].valid ||
626*99a2dd95SBruce Richardson 						i_lpm->lpm.tbl8[j].depth <= depth) {
627*99a2dd95SBruce Richardson 					struct rte_lpm_tbl_entry
628*99a2dd95SBruce Richardson 						new_tbl8_entry = {
629*99a2dd95SBruce Richardson 						.valid = VALID,
630*99a2dd95SBruce Richardson 						.valid_group = VALID,
631*99a2dd95SBruce Richardson 						.depth = depth,
632*99a2dd95SBruce Richardson 						.next_hop = next_hop,
633*99a2dd95SBruce Richardson 					};
634*99a2dd95SBruce Richardson 
635*99a2dd95SBruce Richardson 					/*
636*99a2dd95SBruce Richardson 					 * Setting tbl8 entry in one go to avoid
637*99a2dd95SBruce Richardson 					 * race conditions
638*99a2dd95SBruce Richardson 					 */
639*99a2dd95SBruce Richardson 					__atomic_store(&i_lpm->lpm.tbl8[j],
640*99a2dd95SBruce Richardson 						&new_tbl8_entry,
641*99a2dd95SBruce Richardson 						__ATOMIC_RELAXED);
642*99a2dd95SBruce Richardson 
643*99a2dd95SBruce Richardson 					continue;
644*99a2dd95SBruce Richardson 				}
645*99a2dd95SBruce Richardson 			}
646*99a2dd95SBruce Richardson 		}
647*99a2dd95SBruce Richardson 	}
648*99a2dd95SBruce Richardson #undef group_idx
649*99a2dd95SBruce Richardson 	return 0;
650*99a2dd95SBruce Richardson }
651*99a2dd95SBruce Richardson 
652*99a2dd95SBruce Richardson static __rte_noinline int32_t
653*99a2dd95SBruce Richardson add_depth_big(struct __rte_lpm *i_lpm, uint32_t ip_masked, uint8_t depth,
654*99a2dd95SBruce Richardson 		uint32_t next_hop)
655*99a2dd95SBruce Richardson {
656*99a2dd95SBruce Richardson #define group_idx next_hop
657*99a2dd95SBruce Richardson 	uint32_t tbl24_index;
658*99a2dd95SBruce Richardson 	int32_t tbl8_group_index, tbl8_group_start, tbl8_group_end, tbl8_index,
659*99a2dd95SBruce Richardson 		tbl8_range, i;
660*99a2dd95SBruce Richardson 
661*99a2dd95SBruce Richardson 	tbl24_index = (ip_masked >> 8);
662*99a2dd95SBruce Richardson 	tbl8_range = depth_to_range(depth);
663*99a2dd95SBruce Richardson 
664*99a2dd95SBruce Richardson 	if (!i_lpm->lpm.tbl24[tbl24_index].valid) {
665*99a2dd95SBruce Richardson 		/* Search for a free tbl8 group. */
666*99a2dd95SBruce Richardson 		tbl8_group_index = tbl8_alloc(i_lpm);
667*99a2dd95SBruce Richardson 
668*99a2dd95SBruce Richardson 		/* Check tbl8 allocation was successful. */
669*99a2dd95SBruce Richardson 		if (tbl8_group_index < 0) {
670*99a2dd95SBruce Richardson 			return tbl8_group_index;
671*99a2dd95SBruce Richardson 		}
672*99a2dd95SBruce Richardson 
673*99a2dd95SBruce Richardson 		/* Find index into tbl8 and range. */
674*99a2dd95SBruce Richardson 		tbl8_index = (tbl8_group_index *
675*99a2dd95SBruce Richardson 				RTE_LPM_TBL8_GROUP_NUM_ENTRIES) +
676*99a2dd95SBruce Richardson 				(ip_masked & 0xFF);
677*99a2dd95SBruce Richardson 
678*99a2dd95SBruce Richardson 		/* Set tbl8 entry. */
679*99a2dd95SBruce Richardson 		for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
680*99a2dd95SBruce Richardson 			struct rte_lpm_tbl_entry new_tbl8_entry = {
681*99a2dd95SBruce Richardson 				.valid = VALID,
682*99a2dd95SBruce Richardson 				.depth = depth,
683*99a2dd95SBruce Richardson 				.valid_group = i_lpm->lpm.tbl8[i].valid_group,
684*99a2dd95SBruce Richardson 				.next_hop = next_hop,
685*99a2dd95SBruce Richardson 			};
686*99a2dd95SBruce Richardson 			__atomic_store(&i_lpm->lpm.tbl8[i], &new_tbl8_entry,
687*99a2dd95SBruce Richardson 					__ATOMIC_RELAXED);
688*99a2dd95SBruce Richardson 		}
689*99a2dd95SBruce Richardson 
690*99a2dd95SBruce Richardson 		/*
691*99a2dd95SBruce Richardson 		 * Update tbl24 entry to point to new tbl8 entry. Note: The
692*99a2dd95SBruce Richardson 		 * ext_flag and tbl8_index need to be updated simultaneously,
693*99a2dd95SBruce Richardson 		 * so assign whole structure in one go
694*99a2dd95SBruce Richardson 		 */
695*99a2dd95SBruce Richardson 
696*99a2dd95SBruce Richardson 		struct rte_lpm_tbl_entry new_tbl24_entry = {
697*99a2dd95SBruce Richardson 			.group_idx = tbl8_group_index,
698*99a2dd95SBruce Richardson 			.valid = VALID,
699*99a2dd95SBruce Richardson 			.valid_group = 1,
700*99a2dd95SBruce Richardson 			.depth = 0,
701*99a2dd95SBruce Richardson 		};
702*99a2dd95SBruce Richardson 
703*99a2dd95SBruce Richardson 		/* The tbl24 entry must be written only after the
704*99a2dd95SBruce Richardson 		 * tbl8 entries are written.
705*99a2dd95SBruce Richardson 		 */
706*99a2dd95SBruce Richardson 		__atomic_store(&i_lpm->lpm.tbl24[tbl24_index], &new_tbl24_entry,
707*99a2dd95SBruce Richardson 				__ATOMIC_RELEASE);
708*99a2dd95SBruce Richardson 
709*99a2dd95SBruce Richardson 	} /* If valid entry but not extended calculate the index into Table8. */
710*99a2dd95SBruce Richardson 	else if (i_lpm->lpm.tbl24[tbl24_index].valid_group == 0) {
711*99a2dd95SBruce Richardson 		/* Search for free tbl8 group. */
712*99a2dd95SBruce Richardson 		tbl8_group_index = tbl8_alloc(i_lpm);
713*99a2dd95SBruce Richardson 
714*99a2dd95SBruce Richardson 		if (tbl8_group_index < 0) {
715*99a2dd95SBruce Richardson 			return tbl8_group_index;
716*99a2dd95SBruce Richardson 		}
717*99a2dd95SBruce Richardson 
718*99a2dd95SBruce Richardson 		tbl8_group_start = tbl8_group_index *
719*99a2dd95SBruce Richardson 				RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
720*99a2dd95SBruce Richardson 		tbl8_group_end = tbl8_group_start +
721*99a2dd95SBruce Richardson 				RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
722*99a2dd95SBruce Richardson 
723*99a2dd95SBruce Richardson 		/* Populate new tbl8 with tbl24 value. */
724*99a2dd95SBruce Richardson 		for (i = tbl8_group_start; i < tbl8_group_end; i++) {
725*99a2dd95SBruce Richardson 			struct rte_lpm_tbl_entry new_tbl8_entry = {
726*99a2dd95SBruce Richardson 				.valid = VALID,
727*99a2dd95SBruce Richardson 				.depth = i_lpm->lpm.tbl24[tbl24_index].depth,
728*99a2dd95SBruce Richardson 				.valid_group = i_lpm->lpm.tbl8[i].valid_group,
729*99a2dd95SBruce Richardson 				.next_hop = i_lpm->lpm.tbl24[tbl24_index].next_hop,
730*99a2dd95SBruce Richardson 			};
731*99a2dd95SBruce Richardson 			__atomic_store(&i_lpm->lpm.tbl8[i], &new_tbl8_entry,
732*99a2dd95SBruce Richardson 					__ATOMIC_RELAXED);
733*99a2dd95SBruce Richardson 		}
734*99a2dd95SBruce Richardson 
735*99a2dd95SBruce Richardson 		tbl8_index = tbl8_group_start + (ip_masked & 0xFF);
736*99a2dd95SBruce Richardson 
737*99a2dd95SBruce Richardson 		/* Insert new rule into the tbl8 entry. */
738*99a2dd95SBruce Richardson 		for (i = tbl8_index; i < tbl8_index + tbl8_range; i++) {
739*99a2dd95SBruce Richardson 			struct rte_lpm_tbl_entry new_tbl8_entry = {
740*99a2dd95SBruce Richardson 				.valid = VALID,
741*99a2dd95SBruce Richardson 				.depth = depth,
742*99a2dd95SBruce Richardson 				.valid_group = i_lpm->lpm.tbl8[i].valid_group,
743*99a2dd95SBruce Richardson 				.next_hop = next_hop,
744*99a2dd95SBruce Richardson 			};
745*99a2dd95SBruce Richardson 			__atomic_store(&i_lpm->lpm.tbl8[i], &new_tbl8_entry,
746*99a2dd95SBruce Richardson 					__ATOMIC_RELAXED);
747*99a2dd95SBruce Richardson 		}
748*99a2dd95SBruce Richardson 
749*99a2dd95SBruce Richardson 		/*
750*99a2dd95SBruce Richardson 		 * Update tbl24 entry to point to new tbl8 entry. Note: The
751*99a2dd95SBruce Richardson 		 * ext_flag and tbl8_index need to be updated simultaneously,
752*99a2dd95SBruce Richardson 		 * so assign whole structure in one go.
753*99a2dd95SBruce Richardson 		 */
754*99a2dd95SBruce Richardson 
755*99a2dd95SBruce Richardson 		struct rte_lpm_tbl_entry new_tbl24_entry = {
756*99a2dd95SBruce Richardson 				.group_idx = tbl8_group_index,
757*99a2dd95SBruce Richardson 				.valid = VALID,
758*99a2dd95SBruce Richardson 				.valid_group = 1,
759*99a2dd95SBruce Richardson 				.depth = 0,
760*99a2dd95SBruce Richardson 		};
761*99a2dd95SBruce Richardson 
762*99a2dd95SBruce Richardson 		/* The tbl24 entry must be written only after the
763*99a2dd95SBruce Richardson 		 * tbl8 entries are written.
764*99a2dd95SBruce Richardson 		 */
765*99a2dd95SBruce Richardson 		__atomic_store(&i_lpm->lpm.tbl24[tbl24_index], &new_tbl24_entry,
766*99a2dd95SBruce Richardson 				__ATOMIC_RELEASE);
767*99a2dd95SBruce Richardson 
768*99a2dd95SBruce Richardson 	} else { /*
769*99a2dd95SBruce Richardson 		* If it is valid, extended entry calculate the index into tbl8.
770*99a2dd95SBruce Richardson 		*/
771*99a2dd95SBruce Richardson 		tbl8_group_index = i_lpm->lpm.tbl24[tbl24_index].group_idx;
772*99a2dd95SBruce Richardson 		tbl8_group_start = tbl8_group_index *
773*99a2dd95SBruce Richardson 				RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
774*99a2dd95SBruce Richardson 		tbl8_index = tbl8_group_start + (ip_masked & 0xFF);
775*99a2dd95SBruce Richardson 
776*99a2dd95SBruce Richardson 		for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
777*99a2dd95SBruce Richardson 
778*99a2dd95SBruce Richardson 			if (!i_lpm->lpm.tbl8[i].valid ||
779*99a2dd95SBruce Richardson 					i_lpm->lpm.tbl8[i].depth <= depth) {
780*99a2dd95SBruce Richardson 				struct rte_lpm_tbl_entry new_tbl8_entry = {
781*99a2dd95SBruce Richardson 					.valid = VALID,
782*99a2dd95SBruce Richardson 					.depth = depth,
783*99a2dd95SBruce Richardson 					.next_hop = next_hop,
784*99a2dd95SBruce Richardson 					.valid_group = i_lpm->lpm.tbl8[i].valid_group,
785*99a2dd95SBruce Richardson 				};
786*99a2dd95SBruce Richardson 
787*99a2dd95SBruce Richardson 				/*
788*99a2dd95SBruce Richardson 				 * Setting tbl8 entry in one go to avoid race
789*99a2dd95SBruce Richardson 				 * condition
790*99a2dd95SBruce Richardson 				 */
791*99a2dd95SBruce Richardson 				__atomic_store(&i_lpm->lpm.tbl8[i], &new_tbl8_entry,
792*99a2dd95SBruce Richardson 						__ATOMIC_RELAXED);
793*99a2dd95SBruce Richardson 
794*99a2dd95SBruce Richardson 				continue;
795*99a2dd95SBruce Richardson 			}
796*99a2dd95SBruce Richardson 		}
797*99a2dd95SBruce Richardson 	}
798*99a2dd95SBruce Richardson #undef group_idx
799*99a2dd95SBruce Richardson 	return 0;
800*99a2dd95SBruce Richardson }
801*99a2dd95SBruce Richardson 
802*99a2dd95SBruce Richardson /*
803*99a2dd95SBruce Richardson  * Add a route
804*99a2dd95SBruce Richardson  */
805*99a2dd95SBruce Richardson int
806*99a2dd95SBruce Richardson rte_lpm_add(struct rte_lpm *lpm, uint32_t ip, uint8_t depth,
807*99a2dd95SBruce Richardson 		uint32_t next_hop)
808*99a2dd95SBruce Richardson {
809*99a2dd95SBruce Richardson 	int32_t rule_index, status = 0;
810*99a2dd95SBruce Richardson 	struct __rte_lpm *i_lpm;
811*99a2dd95SBruce Richardson 	uint32_t ip_masked;
812*99a2dd95SBruce Richardson 
813*99a2dd95SBruce Richardson 	/* Check user arguments. */
814*99a2dd95SBruce Richardson 	if ((lpm == NULL) || (depth < 1) || (depth > RTE_LPM_MAX_DEPTH))
815*99a2dd95SBruce Richardson 		return -EINVAL;
816*99a2dd95SBruce Richardson 
817*99a2dd95SBruce Richardson 	i_lpm = container_of(lpm, struct __rte_lpm, lpm);
818*99a2dd95SBruce Richardson 	ip_masked = ip & depth_to_mask(depth);
819*99a2dd95SBruce Richardson 
820*99a2dd95SBruce Richardson 	/* Add the rule to the rule table. */
821*99a2dd95SBruce Richardson 	rule_index = rule_add(i_lpm, ip_masked, depth, next_hop);
822*99a2dd95SBruce Richardson 
823*99a2dd95SBruce Richardson 	/* Skip table entries update if The rule is the same as
824*99a2dd95SBruce Richardson 	 * the rule in the rules table.
825*99a2dd95SBruce Richardson 	 */
826*99a2dd95SBruce Richardson 	if (rule_index == -EEXIST)
827*99a2dd95SBruce Richardson 		return 0;
828*99a2dd95SBruce Richardson 
829*99a2dd95SBruce Richardson 	/* If the is no space available for new rule return error. */
830*99a2dd95SBruce Richardson 	if (rule_index < 0) {
831*99a2dd95SBruce Richardson 		return rule_index;
832*99a2dd95SBruce Richardson 	}
833*99a2dd95SBruce Richardson 
834*99a2dd95SBruce Richardson 	if (depth <= MAX_DEPTH_TBL24) {
835*99a2dd95SBruce Richardson 		status = add_depth_small(i_lpm, ip_masked, depth, next_hop);
836*99a2dd95SBruce Richardson 	} else { /* If depth > RTE_LPM_MAX_DEPTH_TBL24 */
837*99a2dd95SBruce Richardson 		status = add_depth_big(i_lpm, ip_masked, depth, next_hop);
838*99a2dd95SBruce Richardson 
839*99a2dd95SBruce Richardson 		/*
840*99a2dd95SBruce Richardson 		 * If add fails due to exhaustion of tbl8 extensions delete
841*99a2dd95SBruce Richardson 		 * rule that was added to rule table.
842*99a2dd95SBruce Richardson 		 */
843*99a2dd95SBruce Richardson 		if (status < 0) {
844*99a2dd95SBruce Richardson 			rule_delete(i_lpm, rule_index, depth);
845*99a2dd95SBruce Richardson 
846*99a2dd95SBruce Richardson 			return status;
847*99a2dd95SBruce Richardson 		}
848*99a2dd95SBruce Richardson 	}
849*99a2dd95SBruce Richardson 
850*99a2dd95SBruce Richardson 	return 0;
851*99a2dd95SBruce Richardson }
852*99a2dd95SBruce Richardson 
853*99a2dd95SBruce Richardson /*
854*99a2dd95SBruce Richardson  * Look for a rule in the high-level rules table
855*99a2dd95SBruce Richardson  */
856*99a2dd95SBruce Richardson int
857*99a2dd95SBruce Richardson rte_lpm_is_rule_present(struct rte_lpm *lpm, uint32_t ip, uint8_t depth,
858*99a2dd95SBruce Richardson uint32_t *next_hop)
859*99a2dd95SBruce Richardson {
860*99a2dd95SBruce Richardson 	struct __rte_lpm *i_lpm;
861*99a2dd95SBruce Richardson 	uint32_t ip_masked;
862*99a2dd95SBruce Richardson 	int32_t rule_index;
863*99a2dd95SBruce Richardson 
864*99a2dd95SBruce Richardson 	/* Check user arguments. */
865*99a2dd95SBruce Richardson 	if ((lpm == NULL) ||
866*99a2dd95SBruce Richardson 		(next_hop == NULL) ||
867*99a2dd95SBruce Richardson 		(depth < 1) || (depth > RTE_LPM_MAX_DEPTH))
868*99a2dd95SBruce Richardson 		return -EINVAL;
869*99a2dd95SBruce Richardson 
870*99a2dd95SBruce Richardson 	/* Look for the rule using rule_find. */
871*99a2dd95SBruce Richardson 	i_lpm = container_of(lpm, struct __rte_lpm, lpm);
872*99a2dd95SBruce Richardson 	ip_masked = ip & depth_to_mask(depth);
873*99a2dd95SBruce Richardson 	rule_index = rule_find(i_lpm, ip_masked, depth);
874*99a2dd95SBruce Richardson 
875*99a2dd95SBruce Richardson 	if (rule_index >= 0) {
876*99a2dd95SBruce Richardson 		*next_hop = i_lpm->rules_tbl[rule_index].next_hop;
877*99a2dd95SBruce Richardson 		return 1;
878*99a2dd95SBruce Richardson 	}
879*99a2dd95SBruce Richardson 
880*99a2dd95SBruce Richardson 	/* If rule is not found return 0. */
881*99a2dd95SBruce Richardson 	return 0;
882*99a2dd95SBruce Richardson }
883*99a2dd95SBruce Richardson 
884*99a2dd95SBruce Richardson static int32_t
885*99a2dd95SBruce Richardson find_previous_rule(struct __rte_lpm *i_lpm, uint32_t ip, uint8_t depth,
886*99a2dd95SBruce Richardson 		uint8_t *sub_rule_depth)
887*99a2dd95SBruce Richardson {
888*99a2dd95SBruce Richardson 	int32_t rule_index;
889*99a2dd95SBruce Richardson 	uint32_t ip_masked;
890*99a2dd95SBruce Richardson 	uint8_t prev_depth;
891*99a2dd95SBruce Richardson 
892*99a2dd95SBruce Richardson 	for (prev_depth = (uint8_t)(depth - 1); prev_depth > 0; prev_depth--) {
893*99a2dd95SBruce Richardson 		ip_masked = ip & depth_to_mask(prev_depth);
894*99a2dd95SBruce Richardson 
895*99a2dd95SBruce Richardson 		rule_index = rule_find(i_lpm, ip_masked, prev_depth);
896*99a2dd95SBruce Richardson 
897*99a2dd95SBruce Richardson 		if (rule_index >= 0) {
898*99a2dd95SBruce Richardson 			*sub_rule_depth = prev_depth;
899*99a2dd95SBruce Richardson 			return rule_index;
900*99a2dd95SBruce Richardson 		}
901*99a2dd95SBruce Richardson 	}
902*99a2dd95SBruce Richardson 
903*99a2dd95SBruce Richardson 	return -1;
904*99a2dd95SBruce Richardson }
905*99a2dd95SBruce Richardson 
906*99a2dd95SBruce Richardson static int32_t
907*99a2dd95SBruce Richardson delete_depth_small(struct __rte_lpm *i_lpm, uint32_t ip_masked,
908*99a2dd95SBruce Richardson 	uint8_t depth, int32_t sub_rule_index, uint8_t sub_rule_depth)
909*99a2dd95SBruce Richardson {
910*99a2dd95SBruce Richardson #define group_idx next_hop
911*99a2dd95SBruce Richardson 	uint32_t tbl24_range, tbl24_index, tbl8_group_index, tbl8_index, i, j;
912*99a2dd95SBruce Richardson 
913*99a2dd95SBruce Richardson 	/* Calculate the range and index into Table24. */
914*99a2dd95SBruce Richardson 	tbl24_range = depth_to_range(depth);
915*99a2dd95SBruce Richardson 	tbl24_index = (ip_masked >> 8);
916*99a2dd95SBruce Richardson 	struct rte_lpm_tbl_entry zero_tbl24_entry = {0};
917*99a2dd95SBruce Richardson 
918*99a2dd95SBruce Richardson 	/*
919*99a2dd95SBruce Richardson 	 * Firstly check the sub_rule_index. A -1 indicates no replacement rule
920*99a2dd95SBruce Richardson 	 * and a positive number indicates a sub_rule_index.
921*99a2dd95SBruce Richardson 	 */
922*99a2dd95SBruce Richardson 	if (sub_rule_index < 0) {
923*99a2dd95SBruce Richardson 		/*
924*99a2dd95SBruce Richardson 		 * If no replacement rule exists then invalidate entries
925*99a2dd95SBruce Richardson 		 * associated with this rule.
926*99a2dd95SBruce Richardson 		 */
927*99a2dd95SBruce Richardson 		for (i = tbl24_index; i < (tbl24_index + tbl24_range); i++) {
928*99a2dd95SBruce Richardson 
929*99a2dd95SBruce Richardson 			if (i_lpm->lpm.tbl24[i].valid_group == 0 &&
930*99a2dd95SBruce Richardson 					i_lpm->lpm.tbl24[i].depth <= depth) {
931*99a2dd95SBruce Richardson 				__atomic_store(&i_lpm->lpm.tbl24[i],
932*99a2dd95SBruce Richardson 					&zero_tbl24_entry, __ATOMIC_RELEASE);
933*99a2dd95SBruce Richardson 			} else if (i_lpm->lpm.tbl24[i].valid_group == 1) {
934*99a2dd95SBruce Richardson 				/*
935*99a2dd95SBruce Richardson 				 * If TBL24 entry is extended, then there has
936*99a2dd95SBruce Richardson 				 * to be a rule with depth >= 25 in the
937*99a2dd95SBruce Richardson 				 * associated TBL8 group.
938*99a2dd95SBruce Richardson 				 */
939*99a2dd95SBruce Richardson 
940*99a2dd95SBruce Richardson 				tbl8_group_index = i_lpm->lpm.tbl24[i].group_idx;
941*99a2dd95SBruce Richardson 				tbl8_index = tbl8_group_index *
942*99a2dd95SBruce Richardson 						RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
943*99a2dd95SBruce Richardson 
944*99a2dd95SBruce Richardson 				for (j = tbl8_index; j < (tbl8_index +
945*99a2dd95SBruce Richardson 					RTE_LPM_TBL8_GROUP_NUM_ENTRIES); j++) {
946*99a2dd95SBruce Richardson 
947*99a2dd95SBruce Richardson 					if (i_lpm->lpm.tbl8[j].depth <= depth)
948*99a2dd95SBruce Richardson 						i_lpm->lpm.tbl8[j].valid = INVALID;
949*99a2dd95SBruce Richardson 				}
950*99a2dd95SBruce Richardson 			}
951*99a2dd95SBruce Richardson 		}
952*99a2dd95SBruce Richardson 	} else {
953*99a2dd95SBruce Richardson 		/*
954*99a2dd95SBruce Richardson 		 * If a replacement rule exists then modify entries
955*99a2dd95SBruce Richardson 		 * associated with this rule.
956*99a2dd95SBruce Richardson 		 */
957*99a2dd95SBruce Richardson 
958*99a2dd95SBruce Richardson 		struct rte_lpm_tbl_entry new_tbl24_entry = {
959*99a2dd95SBruce Richardson 			.next_hop = i_lpm->rules_tbl[sub_rule_index].next_hop,
960*99a2dd95SBruce Richardson 			.valid = VALID,
961*99a2dd95SBruce Richardson 			.valid_group = 0,
962*99a2dd95SBruce Richardson 			.depth = sub_rule_depth,
963*99a2dd95SBruce Richardson 		};
964*99a2dd95SBruce Richardson 
965*99a2dd95SBruce Richardson 		struct rte_lpm_tbl_entry new_tbl8_entry = {
966*99a2dd95SBruce Richardson 			.valid = VALID,
967*99a2dd95SBruce Richardson 			.valid_group = VALID,
968*99a2dd95SBruce Richardson 			.depth = sub_rule_depth,
969*99a2dd95SBruce Richardson 			.next_hop = i_lpm->rules_tbl
970*99a2dd95SBruce Richardson 			[sub_rule_index].next_hop,
971*99a2dd95SBruce Richardson 		};
972*99a2dd95SBruce Richardson 
973*99a2dd95SBruce Richardson 		for (i = tbl24_index; i < (tbl24_index + tbl24_range); i++) {
974*99a2dd95SBruce Richardson 
975*99a2dd95SBruce Richardson 			if (i_lpm->lpm.tbl24[i].valid_group == 0 &&
976*99a2dd95SBruce Richardson 					i_lpm->lpm.tbl24[i].depth <= depth) {
977*99a2dd95SBruce Richardson 				__atomic_store(&i_lpm->lpm.tbl24[i], &new_tbl24_entry,
978*99a2dd95SBruce Richardson 						__ATOMIC_RELEASE);
979*99a2dd95SBruce Richardson 			} else  if (i_lpm->lpm.tbl24[i].valid_group == 1) {
980*99a2dd95SBruce Richardson 				/*
981*99a2dd95SBruce Richardson 				 * If TBL24 entry is extended, then there has
982*99a2dd95SBruce Richardson 				 * to be a rule with depth >= 25 in the
983*99a2dd95SBruce Richardson 				 * associated TBL8 group.
984*99a2dd95SBruce Richardson 				 */
985*99a2dd95SBruce Richardson 
986*99a2dd95SBruce Richardson 				tbl8_group_index = i_lpm->lpm.tbl24[i].group_idx;
987*99a2dd95SBruce Richardson 				tbl8_index = tbl8_group_index *
988*99a2dd95SBruce Richardson 						RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
989*99a2dd95SBruce Richardson 
990*99a2dd95SBruce Richardson 				for (j = tbl8_index; j < (tbl8_index +
991*99a2dd95SBruce Richardson 					RTE_LPM_TBL8_GROUP_NUM_ENTRIES); j++) {
992*99a2dd95SBruce Richardson 
993*99a2dd95SBruce Richardson 					if (i_lpm->lpm.tbl8[j].depth <= depth)
994*99a2dd95SBruce Richardson 						__atomic_store(&i_lpm->lpm.tbl8[j],
995*99a2dd95SBruce Richardson 							&new_tbl8_entry,
996*99a2dd95SBruce Richardson 							__ATOMIC_RELAXED);
997*99a2dd95SBruce Richardson 				}
998*99a2dd95SBruce Richardson 			}
999*99a2dd95SBruce Richardson 		}
1000*99a2dd95SBruce Richardson 	}
1001*99a2dd95SBruce Richardson #undef group_idx
1002*99a2dd95SBruce Richardson 	return 0;
1003*99a2dd95SBruce Richardson }
1004*99a2dd95SBruce Richardson 
1005*99a2dd95SBruce Richardson /*
1006*99a2dd95SBruce Richardson  * Checks if table 8 group can be recycled.
1007*99a2dd95SBruce Richardson  *
1008*99a2dd95SBruce Richardson  * Return of -EEXIST means tbl8 is in use and thus can not be recycled.
1009*99a2dd95SBruce Richardson  * Return of -EINVAL means tbl8 is empty and thus can be recycled
1010*99a2dd95SBruce Richardson  * Return of value > -1 means tbl8 is in use but has all the same values and
1011*99a2dd95SBruce Richardson  * thus can be recycled
1012*99a2dd95SBruce Richardson  */
1013*99a2dd95SBruce Richardson static int32_t
1014*99a2dd95SBruce Richardson tbl8_recycle_check(struct rte_lpm_tbl_entry *tbl8,
1015*99a2dd95SBruce Richardson 		uint32_t tbl8_group_start)
1016*99a2dd95SBruce Richardson {
1017*99a2dd95SBruce Richardson 	uint32_t tbl8_group_end, i;
1018*99a2dd95SBruce Richardson 	tbl8_group_end = tbl8_group_start + RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
1019*99a2dd95SBruce Richardson 
1020*99a2dd95SBruce Richardson 	/*
1021*99a2dd95SBruce Richardson 	 * Check the first entry of the given tbl8. If it is invalid we know
1022*99a2dd95SBruce Richardson 	 * this tbl8 does not contain any rule with a depth < RTE_LPM_MAX_DEPTH
1023*99a2dd95SBruce Richardson 	 *  (As they would affect all entries in a tbl8) and thus this table
1024*99a2dd95SBruce Richardson 	 *  can not be recycled.
1025*99a2dd95SBruce Richardson 	 */
1026*99a2dd95SBruce Richardson 	if (tbl8[tbl8_group_start].valid) {
1027*99a2dd95SBruce Richardson 		/*
1028*99a2dd95SBruce Richardson 		 * If first entry is valid check if the depth is less than 24
1029*99a2dd95SBruce Richardson 		 * and if so check the rest of the entries to verify that they
1030*99a2dd95SBruce Richardson 		 * are all of this depth.
1031*99a2dd95SBruce Richardson 		 */
1032*99a2dd95SBruce Richardson 		if (tbl8[tbl8_group_start].depth <= MAX_DEPTH_TBL24) {
1033*99a2dd95SBruce Richardson 			for (i = (tbl8_group_start + 1); i < tbl8_group_end;
1034*99a2dd95SBruce Richardson 					i++) {
1035*99a2dd95SBruce Richardson 
1036*99a2dd95SBruce Richardson 				if (tbl8[i].depth !=
1037*99a2dd95SBruce Richardson 						tbl8[tbl8_group_start].depth) {
1038*99a2dd95SBruce Richardson 
1039*99a2dd95SBruce Richardson 					return -EEXIST;
1040*99a2dd95SBruce Richardson 				}
1041*99a2dd95SBruce Richardson 			}
1042*99a2dd95SBruce Richardson 			/* If all entries are the same return the tb8 index */
1043*99a2dd95SBruce Richardson 			return tbl8_group_start;
1044*99a2dd95SBruce Richardson 		}
1045*99a2dd95SBruce Richardson 
1046*99a2dd95SBruce Richardson 		return -EEXIST;
1047*99a2dd95SBruce Richardson 	}
1048*99a2dd95SBruce Richardson 	/*
1049*99a2dd95SBruce Richardson 	 * If the first entry is invalid check if the rest of the entries in
1050*99a2dd95SBruce Richardson 	 * the tbl8 are invalid.
1051*99a2dd95SBruce Richardson 	 */
1052*99a2dd95SBruce Richardson 	for (i = (tbl8_group_start + 1); i < tbl8_group_end; i++) {
1053*99a2dd95SBruce Richardson 		if (tbl8[i].valid)
1054*99a2dd95SBruce Richardson 			return -EEXIST;
1055*99a2dd95SBruce Richardson 	}
1056*99a2dd95SBruce Richardson 	/* If no valid entries are found then return -EINVAL. */
1057*99a2dd95SBruce Richardson 	return -EINVAL;
1058*99a2dd95SBruce Richardson }
1059*99a2dd95SBruce Richardson 
1060*99a2dd95SBruce Richardson static int32_t
1061*99a2dd95SBruce Richardson delete_depth_big(struct __rte_lpm *i_lpm, uint32_t ip_masked,
1062*99a2dd95SBruce Richardson 	uint8_t depth, int32_t sub_rule_index, uint8_t sub_rule_depth)
1063*99a2dd95SBruce Richardson {
1064*99a2dd95SBruce Richardson #define group_idx next_hop
1065*99a2dd95SBruce Richardson 	uint32_t tbl24_index, tbl8_group_index, tbl8_group_start, tbl8_index,
1066*99a2dd95SBruce Richardson 			tbl8_range, i;
1067*99a2dd95SBruce Richardson 	int32_t tbl8_recycle_index, status = 0;
1068*99a2dd95SBruce Richardson 
1069*99a2dd95SBruce Richardson 	/*
1070*99a2dd95SBruce Richardson 	 * Calculate the index into tbl24 and range. Note: All depths larger
1071*99a2dd95SBruce Richardson 	 * than MAX_DEPTH_TBL24 are associated with only one tbl24 entry.
1072*99a2dd95SBruce Richardson 	 */
1073*99a2dd95SBruce Richardson 	tbl24_index = ip_masked >> 8;
1074*99a2dd95SBruce Richardson 
1075*99a2dd95SBruce Richardson 	/* Calculate the index into tbl8 and range. */
1076*99a2dd95SBruce Richardson 	tbl8_group_index = i_lpm->lpm.tbl24[tbl24_index].group_idx;
1077*99a2dd95SBruce Richardson 	tbl8_group_start = tbl8_group_index * RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
1078*99a2dd95SBruce Richardson 	tbl8_index = tbl8_group_start + (ip_masked & 0xFF);
1079*99a2dd95SBruce Richardson 	tbl8_range = depth_to_range(depth);
1080*99a2dd95SBruce Richardson 
1081*99a2dd95SBruce Richardson 	if (sub_rule_index < 0) {
1082*99a2dd95SBruce Richardson 		/*
1083*99a2dd95SBruce Richardson 		 * Loop through the range of entries on tbl8 for which the
1084*99a2dd95SBruce Richardson 		 * rule_to_delete must be removed or modified.
1085*99a2dd95SBruce Richardson 		 */
1086*99a2dd95SBruce Richardson 		for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
1087*99a2dd95SBruce Richardson 			if (i_lpm->lpm.tbl8[i].depth <= depth)
1088*99a2dd95SBruce Richardson 				i_lpm->lpm.tbl8[i].valid = INVALID;
1089*99a2dd95SBruce Richardson 		}
1090*99a2dd95SBruce Richardson 	} else {
1091*99a2dd95SBruce Richardson 		/* Set new tbl8 entry. */
1092*99a2dd95SBruce Richardson 		struct rte_lpm_tbl_entry new_tbl8_entry = {
1093*99a2dd95SBruce Richardson 			.valid = VALID,
1094*99a2dd95SBruce Richardson 			.depth = sub_rule_depth,
1095*99a2dd95SBruce Richardson 			.valid_group = i_lpm->lpm.tbl8[tbl8_group_start].valid_group,
1096*99a2dd95SBruce Richardson 			.next_hop = i_lpm->rules_tbl[sub_rule_index].next_hop,
1097*99a2dd95SBruce Richardson 		};
1098*99a2dd95SBruce Richardson 
1099*99a2dd95SBruce Richardson 		/*
1100*99a2dd95SBruce Richardson 		 * Loop through the range of entries on tbl8 for which the
1101*99a2dd95SBruce Richardson 		 * rule_to_delete must be modified.
1102*99a2dd95SBruce Richardson 		 */
1103*99a2dd95SBruce Richardson 		for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
1104*99a2dd95SBruce Richardson 			if (i_lpm->lpm.tbl8[i].depth <= depth)
1105*99a2dd95SBruce Richardson 				__atomic_store(&i_lpm->lpm.tbl8[i], &new_tbl8_entry,
1106*99a2dd95SBruce Richardson 						__ATOMIC_RELAXED);
1107*99a2dd95SBruce Richardson 		}
1108*99a2dd95SBruce Richardson 	}
1109*99a2dd95SBruce Richardson 
1110*99a2dd95SBruce Richardson 	/*
1111*99a2dd95SBruce Richardson 	 * Check if there are any valid entries in this tbl8 group. If all
1112*99a2dd95SBruce Richardson 	 * tbl8 entries are invalid we can free the tbl8 and invalidate the
1113*99a2dd95SBruce Richardson 	 * associated tbl24 entry.
1114*99a2dd95SBruce Richardson 	 */
1115*99a2dd95SBruce Richardson 
1116*99a2dd95SBruce Richardson 	tbl8_recycle_index = tbl8_recycle_check(i_lpm->lpm.tbl8, tbl8_group_start);
1117*99a2dd95SBruce Richardson 
1118*99a2dd95SBruce Richardson 	if (tbl8_recycle_index == -EINVAL) {
1119*99a2dd95SBruce Richardson 		/* Set tbl24 before freeing tbl8 to avoid race condition.
1120*99a2dd95SBruce Richardson 		 * Prevent the free of the tbl8 group from hoisting.
1121*99a2dd95SBruce Richardson 		 */
1122*99a2dd95SBruce Richardson 		i_lpm->lpm.tbl24[tbl24_index].valid = 0;
1123*99a2dd95SBruce Richardson 		__atomic_thread_fence(__ATOMIC_RELEASE);
1124*99a2dd95SBruce Richardson 		status = tbl8_free(i_lpm, tbl8_group_start);
1125*99a2dd95SBruce Richardson 	} else if (tbl8_recycle_index > -1) {
1126*99a2dd95SBruce Richardson 		/* Update tbl24 entry. */
1127*99a2dd95SBruce Richardson 		struct rte_lpm_tbl_entry new_tbl24_entry = {
1128*99a2dd95SBruce Richardson 			.next_hop = i_lpm->lpm.tbl8[tbl8_recycle_index].next_hop,
1129*99a2dd95SBruce Richardson 			.valid = VALID,
1130*99a2dd95SBruce Richardson 			.valid_group = 0,
1131*99a2dd95SBruce Richardson 			.depth = i_lpm->lpm.tbl8[tbl8_recycle_index].depth,
1132*99a2dd95SBruce Richardson 		};
1133*99a2dd95SBruce Richardson 
1134*99a2dd95SBruce Richardson 		/* Set tbl24 before freeing tbl8 to avoid race condition.
1135*99a2dd95SBruce Richardson 		 * Prevent the free of the tbl8 group from hoisting.
1136*99a2dd95SBruce Richardson 		 */
1137*99a2dd95SBruce Richardson 		__atomic_store(&i_lpm->lpm.tbl24[tbl24_index], &new_tbl24_entry,
1138*99a2dd95SBruce Richardson 				__ATOMIC_RELAXED);
1139*99a2dd95SBruce Richardson 		__atomic_thread_fence(__ATOMIC_RELEASE);
1140*99a2dd95SBruce Richardson 		status = tbl8_free(i_lpm, tbl8_group_start);
1141*99a2dd95SBruce Richardson 	}
1142*99a2dd95SBruce Richardson #undef group_idx
1143*99a2dd95SBruce Richardson 	return status;
1144*99a2dd95SBruce Richardson }
1145*99a2dd95SBruce Richardson 
1146*99a2dd95SBruce Richardson /*
1147*99a2dd95SBruce Richardson  * Deletes a rule
1148*99a2dd95SBruce Richardson  */
1149*99a2dd95SBruce Richardson int
1150*99a2dd95SBruce Richardson rte_lpm_delete(struct rte_lpm *lpm, uint32_t ip, uint8_t depth)
1151*99a2dd95SBruce Richardson {
1152*99a2dd95SBruce Richardson 	int32_t rule_to_delete_index, sub_rule_index;
1153*99a2dd95SBruce Richardson 	struct __rte_lpm *i_lpm;
1154*99a2dd95SBruce Richardson 	uint32_t ip_masked;
1155*99a2dd95SBruce Richardson 	uint8_t sub_rule_depth;
1156*99a2dd95SBruce Richardson 	/*
1157*99a2dd95SBruce Richardson 	 * Check input arguments. Note: IP must be a positive integer of 32
1158*99a2dd95SBruce Richardson 	 * bits in length therefore it need not be checked.
1159*99a2dd95SBruce Richardson 	 */
1160*99a2dd95SBruce Richardson 	if ((lpm == NULL) || (depth < 1) || (depth > RTE_LPM_MAX_DEPTH)) {
1161*99a2dd95SBruce Richardson 		return -EINVAL;
1162*99a2dd95SBruce Richardson 	}
1163*99a2dd95SBruce Richardson 
1164*99a2dd95SBruce Richardson 	i_lpm = container_of(lpm, struct __rte_lpm, lpm);
1165*99a2dd95SBruce Richardson 	ip_masked = ip & depth_to_mask(depth);
1166*99a2dd95SBruce Richardson 
1167*99a2dd95SBruce Richardson 	/*
1168*99a2dd95SBruce Richardson 	 * Find the index of the input rule, that needs to be deleted, in the
1169*99a2dd95SBruce Richardson 	 * rule table.
1170*99a2dd95SBruce Richardson 	 */
1171*99a2dd95SBruce Richardson 	rule_to_delete_index = rule_find(i_lpm, ip_masked, depth);
1172*99a2dd95SBruce Richardson 
1173*99a2dd95SBruce Richardson 	/*
1174*99a2dd95SBruce Richardson 	 * Check if rule_to_delete_index was found. If no rule was found the
1175*99a2dd95SBruce Richardson 	 * function rule_find returns -EINVAL.
1176*99a2dd95SBruce Richardson 	 */
1177*99a2dd95SBruce Richardson 	if (rule_to_delete_index < 0)
1178*99a2dd95SBruce Richardson 		return -EINVAL;
1179*99a2dd95SBruce Richardson 
1180*99a2dd95SBruce Richardson 	/* Delete the rule from the rule table. */
1181*99a2dd95SBruce Richardson 	rule_delete(i_lpm, rule_to_delete_index, depth);
1182*99a2dd95SBruce Richardson 
1183*99a2dd95SBruce Richardson 	/*
1184*99a2dd95SBruce Richardson 	 * Find rule to replace the rule_to_delete. If there is no rule to
1185*99a2dd95SBruce Richardson 	 * replace the rule_to_delete we return -1 and invalidate the table
1186*99a2dd95SBruce Richardson 	 * entries associated with this rule.
1187*99a2dd95SBruce Richardson 	 */
1188*99a2dd95SBruce Richardson 	sub_rule_depth = 0;
1189*99a2dd95SBruce Richardson 	sub_rule_index = find_previous_rule(i_lpm, ip, depth, &sub_rule_depth);
1190*99a2dd95SBruce Richardson 
1191*99a2dd95SBruce Richardson 	/*
1192*99a2dd95SBruce Richardson 	 * If the input depth value is less than 25 use function
1193*99a2dd95SBruce Richardson 	 * delete_depth_small otherwise use delete_depth_big.
1194*99a2dd95SBruce Richardson 	 */
1195*99a2dd95SBruce Richardson 	if (depth <= MAX_DEPTH_TBL24) {
1196*99a2dd95SBruce Richardson 		return delete_depth_small(i_lpm, ip_masked, depth,
1197*99a2dd95SBruce Richardson 				sub_rule_index, sub_rule_depth);
1198*99a2dd95SBruce Richardson 	} else { /* If depth > MAX_DEPTH_TBL24 */
1199*99a2dd95SBruce Richardson 		return delete_depth_big(i_lpm, ip_masked, depth, sub_rule_index,
1200*99a2dd95SBruce Richardson 				sub_rule_depth);
1201*99a2dd95SBruce Richardson 	}
1202*99a2dd95SBruce Richardson }
1203*99a2dd95SBruce Richardson 
1204*99a2dd95SBruce Richardson /*
1205*99a2dd95SBruce Richardson  * Delete all rules from the LPM table.
1206*99a2dd95SBruce Richardson  */
1207*99a2dd95SBruce Richardson void
1208*99a2dd95SBruce Richardson rte_lpm_delete_all(struct rte_lpm *lpm)
1209*99a2dd95SBruce Richardson {
1210*99a2dd95SBruce Richardson 	struct __rte_lpm *i_lpm;
1211*99a2dd95SBruce Richardson 
1212*99a2dd95SBruce Richardson 	i_lpm = container_of(lpm, struct __rte_lpm, lpm);
1213*99a2dd95SBruce Richardson 	/* Zero rule information. */
1214*99a2dd95SBruce Richardson 	memset(i_lpm->rule_info, 0, sizeof(i_lpm->rule_info));
1215*99a2dd95SBruce Richardson 
1216*99a2dd95SBruce Richardson 	/* Zero tbl24. */
1217*99a2dd95SBruce Richardson 	memset(i_lpm->lpm.tbl24, 0, sizeof(i_lpm->lpm.tbl24));
1218*99a2dd95SBruce Richardson 
1219*99a2dd95SBruce Richardson 	/* Zero tbl8. */
1220*99a2dd95SBruce Richardson 	memset(i_lpm->lpm.tbl8, 0, sizeof(i_lpm->lpm.tbl8[0])
1221*99a2dd95SBruce Richardson 			* RTE_LPM_TBL8_GROUP_NUM_ENTRIES * i_lpm->number_tbl8s);
1222*99a2dd95SBruce Richardson 
1223*99a2dd95SBruce Richardson 	/* Delete all rules form the rules table. */
1224*99a2dd95SBruce Richardson 	memset(i_lpm->rules_tbl, 0, sizeof(i_lpm->rules_tbl[0]) * i_lpm->max_rules);
1225*99a2dd95SBruce Richardson }
1226