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