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