add prefix to cache line macros
[dpdk.git] / lib / librte_table / rte_table_lpm_ipv6.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <string.h>
35 #include <stdio.h>
36
37 #include <rte_common.h>
38 #include <rte_mbuf.h>
39 #include <rte_malloc.h>
40 #include <rte_byteorder.h>
41 #include <rte_log.h>
42 #include <rte_lpm6.h>
43
44 #include "rte_table_lpm_ipv6.h"
45
46 #define RTE_TABLE_LPM_MAX_NEXT_HOPS                        256
47
48 struct rte_table_lpm_ipv6 {
49         /* Input parameters */
50         uint32_t entry_size;
51         uint32_t entry_unique_size;
52         uint32_t n_rules;
53         uint32_t offset;
54
55         /* Handle to low-level LPM table */
56         struct rte_lpm6 *lpm;
57
58         /* Next Hop Table (NHT) */
59         uint32_t nht_users[RTE_TABLE_LPM_MAX_NEXT_HOPS];
60         uint8_t nht[0] __rte_cache_aligned;
61 };
62
63 static void *
64 rte_table_lpm_ipv6_create(void *params, int socket_id, uint32_t entry_size)
65 {
66         struct rte_table_lpm_ipv6_params *p =
67                 (struct rte_table_lpm_ipv6_params *) params;
68         struct rte_table_lpm_ipv6 *lpm;
69         struct rte_lpm6_config lpm6_config;
70         uint32_t total_size, nht_size;
71
72         /* Check input parameters */
73         if (p == NULL) {
74                 RTE_LOG(ERR, TABLE, "%s: NULL input parameters\n", __func__);
75                 return NULL;
76         }
77         if (p->n_rules == 0) {
78                 RTE_LOG(ERR, TABLE, "%s: Invalid n_rules\n", __func__);
79                 return NULL;
80         }
81         if (p->number_tbl8s == 0) {
82                 RTE_LOG(ERR, TABLE, "%s: Invalid n_rules\n", __func__);
83                 return NULL;
84         }
85         if (p->entry_unique_size == 0) {
86                 RTE_LOG(ERR, TABLE, "%s: Invalid entry_unique_size\n",
87                         __func__);
88                 return NULL;
89         }
90         if (p->entry_unique_size > entry_size) {
91                 RTE_LOG(ERR, TABLE, "%s: Invalid entry_unique_size\n",
92                         __func__);
93                 return NULL;
94         }
95         if ((p->offset & 0x3) != 0) {
96                 RTE_LOG(ERR, TABLE, "%s: Invalid offset\n", __func__);
97                 return NULL;
98         }
99
100         entry_size = RTE_ALIGN(entry_size, sizeof(uint64_t));
101
102         /* Memory allocation */
103         nht_size = RTE_TABLE_LPM_MAX_NEXT_HOPS * entry_size;
104         total_size = sizeof(struct rte_table_lpm_ipv6) + nht_size;
105         lpm = rte_zmalloc_socket("TABLE", total_size, RTE_CACHE_LINE_SIZE,
106                 socket_id);
107         if (lpm == NULL) {
108                 RTE_LOG(ERR, TABLE,
109                         "%s: Cannot allocate %u bytes for LPM IPv6 table\n",
110                         __func__, total_size);
111                 return NULL;
112         }
113
114         /* LPM low-level table creation */
115         lpm6_config.max_rules = p->n_rules;
116         lpm6_config.number_tbl8s = p->number_tbl8s;
117         lpm6_config.flags = 0;
118         lpm->lpm = rte_lpm6_create("LPM IPv6", socket_id, &lpm6_config);
119         if (lpm->lpm == NULL) {
120                 rte_free(lpm);
121                 RTE_LOG(ERR, TABLE,
122                         "Unable to create low-level LPM IPv6 table\n");
123                 return NULL;
124         }
125
126         /* Memory initialization */
127         lpm->entry_size = entry_size;
128         lpm->entry_unique_size = p->entry_unique_size;
129         lpm->n_rules = p->n_rules;
130         lpm->offset = p->offset;
131
132         return lpm;
133 }
134
135 static int
136 rte_table_lpm_ipv6_free(void *table)
137 {
138         struct rte_table_lpm_ipv6 *lpm = (struct rte_table_lpm_ipv6 *) table;
139
140         /* Check input parameters */
141         if (lpm == NULL) {
142                 RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__);
143                 return -EINVAL;
144         }
145
146         /* Free previously allocated resources */
147         rte_lpm6_free(lpm->lpm);
148         rte_free(lpm);
149
150         return 0;
151 }
152
153 static int
154 nht_find_free(struct rte_table_lpm_ipv6 *lpm, uint32_t *pos)
155 {
156         uint32_t i;
157
158         for (i = 0; i < RTE_TABLE_LPM_MAX_NEXT_HOPS; i++) {
159                 if (lpm->nht_users[i] == 0) {
160                         *pos = i;
161                         return 1;
162                 }
163         }
164
165         return 0;
166 }
167
168 static int
169 nht_find_existing(struct rte_table_lpm_ipv6 *lpm, void *entry, uint32_t *pos)
170 {
171         uint32_t i;
172
173         for (i = 0; i < RTE_TABLE_LPM_MAX_NEXT_HOPS; i++) {
174                 uint8_t *nht_entry = &lpm->nht[i * lpm->entry_size];
175
176                 if ((lpm->nht_users[i] > 0) && (memcmp(nht_entry, entry,
177                         lpm->entry_unique_size) == 0)) {
178                         *pos = i;
179                         return 1;
180                 }
181         }
182
183         return 0;
184 }
185
186 static int
187 rte_table_lpm_ipv6_entry_add(
188         void *table,
189         void *key,
190         void *entry,
191         int *key_found,
192         void **entry_ptr)
193 {
194         struct rte_table_lpm_ipv6 *lpm = (struct rte_table_lpm_ipv6 *) table;
195         struct rte_table_lpm_ipv6_key *ip_prefix =
196                 (struct rte_table_lpm_ipv6_key *) key;
197         uint32_t nht_pos, nht_pos0_valid;
198         int status;
199         uint8_t nht_pos0;
200
201         /* Check input parameters */
202         if (lpm == NULL) {
203                 RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__);
204                 return -EINVAL;
205         }
206         if (ip_prefix == NULL) {
207                 RTE_LOG(ERR, TABLE, "%s: ip_prefix parameter is NULL\n",
208                         __func__);
209                 return -EINVAL;
210         }
211         if (entry == NULL) {
212                 RTE_LOG(ERR, TABLE, "%s: entry parameter is NULL\n", __func__);
213                 return -EINVAL;
214         }
215
216         if ((ip_prefix->depth == 0) || (ip_prefix->depth > 128)) {
217                 RTE_LOG(ERR, TABLE, "%s: invalid depth (%d)\n", __func__,
218                         ip_prefix->depth);
219                 return -EINVAL;
220         }
221
222         /* Check if rule is already present in the table */
223         status = rte_lpm6_is_rule_present(lpm->lpm, ip_prefix->ip,
224                 ip_prefix->depth, &nht_pos0);
225         nht_pos0_valid = status > 0;
226
227         /* Find existing or free NHT entry */
228         if (nht_find_existing(lpm, entry, &nht_pos) == 0) {
229                 uint8_t *nht_entry;
230
231                 if (nht_find_free(lpm, &nht_pos) == 0) {
232                         RTE_LOG(ERR, TABLE, "%s: NHT full\n", __func__);
233                         return -1;
234                 }
235
236                 nht_entry = &lpm->nht[nht_pos * lpm->entry_size];
237                 memcpy(nht_entry, entry, lpm->entry_size);
238         }
239
240         /* Add rule to low level LPM table */
241         if (rte_lpm6_add(lpm->lpm, ip_prefix->ip, ip_prefix->depth,
242                 (uint8_t) nht_pos) < 0) {
243                 RTE_LOG(ERR, TABLE, "%s: LPM IPv6 rule add failed\n", __func__);
244                 return -1;
245         }
246
247         /* Commit NHT changes */
248         lpm->nht_users[nht_pos]++;
249         lpm->nht_users[nht_pos0] -= nht_pos0_valid;
250
251         *key_found = nht_pos0_valid;
252         *entry_ptr = (void *) &lpm->nht[nht_pos * lpm->entry_size];
253         return 0;
254 }
255
256 static int
257 rte_table_lpm_ipv6_entry_delete(
258         void *table,
259         void *key,
260         int *key_found,
261         void *entry)
262 {
263         struct rte_table_lpm_ipv6 *lpm = (struct rte_table_lpm_ipv6 *) table;
264         struct rte_table_lpm_ipv6_key *ip_prefix =
265                 (struct rte_table_lpm_ipv6_key *) key;
266         uint8_t nht_pos;
267         int status;
268
269         /* Check input parameters */
270         if (lpm == NULL) {
271                 RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__);
272                 return -EINVAL;
273         }
274         if (ip_prefix == NULL) {
275                 RTE_LOG(ERR, TABLE, "%s: ip_prefix parameter is NULL\n",
276                         __func__);
277                 return -EINVAL;
278         }
279         if ((ip_prefix->depth == 0) || (ip_prefix->depth > 128)) {
280                 RTE_LOG(ERR, TABLE, "%s: invalid depth (%d)\n", __func__,
281                         ip_prefix->depth);
282                 return -EINVAL;
283         }
284
285         /* Return if rule is not present in the table */
286         status = rte_lpm6_is_rule_present(lpm->lpm, ip_prefix->ip,
287                 ip_prefix->depth, &nht_pos);
288         if (status < 0) {
289                 RTE_LOG(ERR, TABLE, "%s: LPM IPv6 algorithmic error\n",
290                         __func__);
291                 return -1;
292         }
293         if (status == 0) {
294                 *key_found = 0;
295                 return 0;
296         }
297
298         /* Delete rule from the low-level LPM table */
299         status = rte_lpm6_delete(lpm->lpm, ip_prefix->ip, ip_prefix->depth);
300         if (status) {
301                 RTE_LOG(ERR, TABLE, "%s: LPM IPv6 rule delete failed\n",
302                         __func__);
303                 return -1;
304         }
305
306         /* Commit NHT changes */
307         lpm->nht_users[nht_pos]--;
308
309         *key_found = 1;
310         if (entry)
311                 memcpy(entry, &lpm->nht[nht_pos * lpm->entry_size],
312                         lpm->entry_size);
313
314         return 0;
315 }
316
317 static int
318 rte_table_lpm_ipv6_lookup(
319         void *table,
320         struct rte_mbuf **pkts,
321         uint64_t pkts_mask,
322         uint64_t *lookup_hit_mask,
323         void **entries)
324 {
325         struct rte_table_lpm_ipv6 *lpm = (struct rte_table_lpm_ipv6 *) table;
326         uint64_t pkts_out_mask = 0;
327         uint32_t i;
328
329         pkts_out_mask = 0;
330         for (i = 0; i < (uint32_t)(RTE_PORT_IN_BURST_SIZE_MAX -
331                 __builtin_clzll(pkts_mask)); i++) {
332                 uint64_t pkt_mask = 1LLU << i;
333
334                 if (pkt_mask & pkts_mask) {
335                         struct rte_mbuf *pkt = pkts[i];
336                         uint8_t *ip = RTE_MBUF_METADATA_UINT8_PTR(pkt,
337                                 lpm->offset);
338                         int status;
339                         uint8_t nht_pos;
340
341                         status = rte_lpm6_lookup(lpm->lpm, ip, &nht_pos);
342                         if (status == 0) {
343                                 pkts_out_mask |= pkt_mask;
344                                 entries[i] = (void *) &lpm->nht[nht_pos *
345                                         lpm->entry_size];
346                         }
347                 }
348         }
349
350         *lookup_hit_mask = pkts_out_mask;
351
352         return 0;
353 }
354
355 struct rte_table_ops rte_table_lpm_ipv6_ops = {
356         .f_create = rte_table_lpm_ipv6_create,
357         .f_free = rte_table_lpm_ipv6_free,
358         .f_add = rte_table_lpm_ipv6_entry_add,
359         .f_delete = rte_table_lpm_ipv6_entry_delete,
360         .f_lookup = rte_table_lpm_ipv6_lookup,
361 };