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