xref: /dpdk/lib/table/rte_table_lpm_ipv6.c (revision 30a1de105a5f40d77b344a891c4a68f79e815c43)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4 
5 #include <string.h>
6 #include <stdio.h>
7 
8 #include <rte_common.h>
9 #include <rte_malloc.h>
10 #include <rte_log.h>
11 #include <rte_lpm6.h>
12 
13 #include "rte_table_lpm_ipv6.h"
14 
15 #define RTE_TABLE_LPM_MAX_NEXT_HOPS                        256
16 
17 #ifdef RTE_TABLE_STATS_COLLECT
18 
19 #define RTE_TABLE_LPM_IPV6_STATS_PKTS_IN_ADD(table, val) \
20 	table->stats.n_pkts_in += val
21 #define RTE_TABLE_LPM_IPV6_STATS_PKTS_LOOKUP_MISS(table, val) \
22 	table->stats.n_pkts_lookup_miss += val
23 
24 #else
25 
26 #define RTE_TABLE_LPM_IPV6_STATS_PKTS_IN_ADD(table, val)
27 #define RTE_TABLE_LPM_IPV6_STATS_PKTS_LOOKUP_MISS(table, val)
28 
29 #endif
30 
31 struct rte_table_lpm_ipv6 {
32 	struct rte_table_stats stats;
33 
34 	/* Input parameters */
35 	uint32_t entry_size;
36 	uint32_t entry_unique_size;
37 	uint32_t n_rules;
38 	uint32_t offset;
39 
40 	/* Handle to low-level LPM table */
41 	struct rte_lpm6 *lpm;
42 
43 	/* Next Hop Table (NHT) */
44 	uint32_t nht_users[RTE_TABLE_LPM_MAX_NEXT_HOPS];
45 	uint8_t nht[0] __rte_cache_aligned;
46 };
47 
48 static void *
49 rte_table_lpm_ipv6_create(void *params, int socket_id, uint32_t entry_size)
50 {
51 	struct rte_table_lpm_ipv6_params *p =
52 		params;
53 	struct rte_table_lpm_ipv6 *lpm;
54 	struct rte_lpm6_config lpm6_config;
55 	uint32_t total_size, nht_size;
56 
57 	/* Check input parameters */
58 	if (p == NULL) {
59 		RTE_LOG(ERR, TABLE, "%s: NULL input parameters\n", __func__);
60 		return NULL;
61 	}
62 	if (p->n_rules == 0) {
63 		RTE_LOG(ERR, TABLE, "%s: Invalid n_rules\n", __func__);
64 		return NULL;
65 	}
66 	if (p->number_tbl8s == 0) {
67 		RTE_LOG(ERR, TABLE, "%s: Invalid n_rules\n", __func__);
68 		return NULL;
69 	}
70 	if (p->entry_unique_size == 0) {
71 		RTE_LOG(ERR, TABLE, "%s: Invalid entry_unique_size\n",
72 			__func__);
73 		return NULL;
74 	}
75 	if (p->entry_unique_size > entry_size) {
76 		RTE_LOG(ERR, TABLE, "%s: Invalid entry_unique_size\n",
77 			__func__);
78 		return NULL;
79 	}
80 	if (p->name == NULL) {
81 		RTE_LOG(ERR, TABLE, "%s: Table name is NULL\n",
82 			__func__);
83 		return NULL;
84 	}
85 	entry_size = RTE_ALIGN(entry_size, sizeof(uint64_t));
86 
87 	/* Memory allocation */
88 	nht_size = RTE_TABLE_LPM_MAX_NEXT_HOPS * entry_size;
89 	total_size = sizeof(struct rte_table_lpm_ipv6) + nht_size;
90 	lpm = rte_zmalloc_socket("TABLE", total_size, RTE_CACHE_LINE_SIZE,
91 		socket_id);
92 	if (lpm == NULL) {
93 		RTE_LOG(ERR, TABLE,
94 			"%s: Cannot allocate %u bytes for LPM IPv6 table\n",
95 			__func__, total_size);
96 		return NULL;
97 	}
98 
99 	/* LPM low-level table creation */
100 	lpm6_config.max_rules = p->n_rules;
101 	lpm6_config.number_tbl8s = p->number_tbl8s;
102 	lpm6_config.flags = 0;
103 	lpm->lpm = rte_lpm6_create(p->name, socket_id, &lpm6_config);
104 	if (lpm->lpm == NULL) {
105 		rte_free(lpm);
106 		RTE_LOG(ERR, TABLE,
107 			"Unable to create low-level LPM IPv6 table\n");
108 		return NULL;
109 	}
110 
111 	/* Memory initialization */
112 	lpm->entry_size = entry_size;
113 	lpm->entry_unique_size = p->entry_unique_size;
114 	lpm->n_rules = p->n_rules;
115 	lpm->offset = p->offset;
116 
117 	return lpm;
118 }
119 
120 static int
121 rte_table_lpm_ipv6_free(void *table)
122 {
123 	struct rte_table_lpm_ipv6 *lpm = table;
124 
125 	/* Check input parameters */
126 	if (lpm == NULL) {
127 		RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__);
128 		return -EINVAL;
129 	}
130 
131 	/* Free previously allocated resources */
132 	rte_lpm6_free(lpm->lpm);
133 	rte_free(lpm);
134 
135 	return 0;
136 }
137 
138 static int
139 nht_find_free(struct rte_table_lpm_ipv6 *lpm, uint32_t *pos)
140 {
141 	uint32_t i;
142 
143 	for (i = 0; i < RTE_TABLE_LPM_MAX_NEXT_HOPS; i++) {
144 		if (lpm->nht_users[i] == 0) {
145 			*pos = i;
146 			return 1;
147 		}
148 	}
149 
150 	return 0;
151 }
152 
153 static int
154 nht_find_existing(struct rte_table_lpm_ipv6 *lpm, void *entry, uint32_t *pos)
155 {
156 	uint32_t i;
157 
158 	for (i = 0; i < RTE_TABLE_LPM_MAX_NEXT_HOPS; i++) {
159 		uint8_t *nht_entry = &lpm->nht[i * lpm->entry_size];
160 
161 		if ((lpm->nht_users[i] > 0) && (memcmp(nht_entry, entry,
162 			lpm->entry_unique_size) == 0)) {
163 			*pos = i;
164 			return 1;
165 		}
166 	}
167 
168 	return 0;
169 }
170 
171 static int
172 rte_table_lpm_ipv6_entry_add(
173 	void *table,
174 	void *key,
175 	void *entry,
176 	int *key_found,
177 	void **entry_ptr)
178 {
179 	struct rte_table_lpm_ipv6 *lpm = table;
180 	struct rte_table_lpm_ipv6_key *ip_prefix =
181 		key;
182 	uint32_t nht_pos = 0, nht_pos0 = 0, nht_pos0_valid = 0;
183 	int status;
184 
185 	/* Check input parameters */
186 	if (lpm == NULL) {
187 		RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__);
188 		return -EINVAL;
189 	}
190 	if (ip_prefix == NULL) {
191 		RTE_LOG(ERR, TABLE, "%s: ip_prefix parameter is NULL\n",
192 			__func__);
193 		return -EINVAL;
194 	}
195 	if (entry == NULL) {
196 		RTE_LOG(ERR, TABLE, "%s: entry parameter is NULL\n", __func__);
197 		return -EINVAL;
198 	}
199 
200 	if ((ip_prefix->depth == 0) || (ip_prefix->depth > 128)) {
201 		RTE_LOG(ERR, TABLE, "%s: invalid depth (%d)\n", __func__,
202 			ip_prefix->depth);
203 		return -EINVAL;
204 	}
205 
206 	/* Check if rule is already present in the table */
207 	status = rte_lpm6_is_rule_present(lpm->lpm, ip_prefix->ip,
208 		ip_prefix->depth, &nht_pos0);
209 	nht_pos0_valid = status > 0;
210 
211 	/* Find existing or free NHT entry */
212 	if (nht_find_existing(lpm, entry, &nht_pos) == 0) {
213 		uint8_t *nht_entry;
214 
215 		if (nht_find_free(lpm, &nht_pos) == 0) {
216 			RTE_LOG(ERR, TABLE, "%s: NHT full\n", __func__);
217 			return -1;
218 		}
219 
220 		nht_entry = &lpm->nht[nht_pos * lpm->entry_size];
221 		memcpy(nht_entry, entry, lpm->entry_size);
222 	}
223 
224 	/* Add rule to low level LPM table */
225 	if (rte_lpm6_add(lpm->lpm, ip_prefix->ip, ip_prefix->depth,
226 		nht_pos) < 0) {
227 		RTE_LOG(ERR, TABLE, "%s: LPM IPv6 rule add failed\n", __func__);
228 		return -1;
229 	}
230 
231 	/* Commit NHT changes */
232 	lpm->nht_users[nht_pos]++;
233 	lpm->nht_users[nht_pos0] -= nht_pos0_valid;
234 
235 	*key_found = nht_pos0_valid;
236 	*entry_ptr = (void *) &lpm->nht[nht_pos * lpm->entry_size];
237 	return 0;
238 }
239 
240 static int
241 rte_table_lpm_ipv6_entry_delete(
242 	void *table,
243 	void *key,
244 	int *key_found,
245 	void *entry)
246 {
247 	struct rte_table_lpm_ipv6 *lpm = table;
248 	struct rte_table_lpm_ipv6_key *ip_prefix =
249 		key;
250 	uint32_t nht_pos;
251 	int status;
252 
253 	/* Check input parameters */
254 	if (lpm == NULL) {
255 		RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__);
256 		return -EINVAL;
257 	}
258 	if (ip_prefix == NULL) {
259 		RTE_LOG(ERR, TABLE, "%s: ip_prefix parameter is NULL\n",
260 			__func__);
261 		return -EINVAL;
262 	}
263 	if ((ip_prefix->depth == 0) || (ip_prefix->depth > 128)) {
264 		RTE_LOG(ERR, TABLE, "%s: invalid depth (%d)\n", __func__,
265 			ip_prefix->depth);
266 		return -EINVAL;
267 	}
268 
269 	/* Return if rule is not present in the table */
270 	status = rte_lpm6_is_rule_present(lpm->lpm, ip_prefix->ip,
271 		ip_prefix->depth, &nht_pos);
272 	if (status < 0) {
273 		RTE_LOG(ERR, TABLE, "%s: LPM IPv6 algorithmic error\n",
274 			__func__);
275 		return -1;
276 	}
277 	if (status == 0) {
278 		*key_found = 0;
279 		return 0;
280 	}
281 
282 	/* Delete rule from the low-level LPM table */
283 	status = rte_lpm6_delete(lpm->lpm, ip_prefix->ip, ip_prefix->depth);
284 	if (status) {
285 		RTE_LOG(ERR, TABLE, "%s: LPM IPv6 rule delete failed\n",
286 			__func__);
287 		return -1;
288 	}
289 
290 	/* Commit NHT changes */
291 	lpm->nht_users[nht_pos]--;
292 
293 	*key_found = 1;
294 	if (entry)
295 		memcpy(entry, &lpm->nht[nht_pos * lpm->entry_size],
296 			lpm->entry_size);
297 
298 	return 0;
299 }
300 
301 static int
302 rte_table_lpm_ipv6_lookup(
303 	void *table,
304 	struct rte_mbuf **pkts,
305 	uint64_t pkts_mask,
306 	uint64_t *lookup_hit_mask,
307 	void **entries)
308 {
309 	struct rte_table_lpm_ipv6 *lpm = (struct rte_table_lpm_ipv6 *) table;
310 	uint64_t pkts_out_mask = 0;
311 	uint32_t i;
312 
313 	__rte_unused uint32_t n_pkts_in = __builtin_popcountll(pkts_mask);
314 	RTE_TABLE_LPM_IPV6_STATS_PKTS_IN_ADD(lpm, n_pkts_in);
315 
316 	pkts_out_mask = 0;
317 	for (i = 0; i < (uint32_t)(RTE_PORT_IN_BURST_SIZE_MAX -
318 		__builtin_clzll(pkts_mask)); i++) {
319 		uint64_t pkt_mask = 1LLU << i;
320 
321 		if (pkt_mask & pkts_mask) {
322 			struct rte_mbuf *pkt = pkts[i];
323 			uint8_t *ip = RTE_MBUF_METADATA_UINT8_PTR(pkt,
324 				lpm->offset);
325 			int status;
326 			uint32_t nht_pos;
327 
328 			status = rte_lpm6_lookup(lpm->lpm, ip, &nht_pos);
329 			if (status == 0) {
330 				pkts_out_mask |= pkt_mask;
331 				entries[i] = (void *) &lpm->nht[nht_pos *
332 					lpm->entry_size];
333 			}
334 		}
335 	}
336 
337 	*lookup_hit_mask = pkts_out_mask;
338 	RTE_TABLE_LPM_IPV6_STATS_PKTS_LOOKUP_MISS(lpm, n_pkts_in - __builtin_popcountll(pkts_out_mask));
339 	return 0;
340 }
341 
342 static int
343 rte_table_lpm_ipv6_stats_read(void *table, struct rte_table_stats *stats, int clear)
344 {
345 	struct rte_table_lpm_ipv6 *t = table;
346 
347 	if (stats != NULL)
348 		memcpy(stats, &t->stats, sizeof(t->stats));
349 
350 	if (clear)
351 		memset(&t->stats, 0, sizeof(t->stats));
352 
353 	return 0;
354 }
355 
356 struct rte_table_ops rte_table_lpm_ipv6_ops = {
357 	.f_create = rte_table_lpm_ipv6_create,
358 	.f_free = rte_table_lpm_ipv6_free,
359 	.f_add = rte_table_lpm_ipv6_entry_add,
360 	.f_delete = rte_table_lpm_ipv6_entry_delete,
361 	.f_add_bulk = NULL,
362 	.f_delete_bulk = NULL,
363 	.f_lookup = rte_table_lpm_ipv6_lookup,
364 	.f_stats = rte_table_lpm_ipv6_stats_read,
365 };
366