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