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