net/hns3: fix RSS TC mode entry
[dpdk.git] / lib / table / rte_table_lpm_ipv6.c
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 };