port: add IPv6 reassembly port
[dpdk.git] / lib / librte_port / rte_port_ras.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 #include <string.h>
34
35 #include <rte_ether.h>
36 #include <rte_ip_frag.h>
37 #include <rte_cycles.h>
38 #include <rte_log.h>
39
40 #include "rte_port_ras.h"
41
42 #ifndef RTE_PORT_RAS_N_BUCKETS
43 #define RTE_PORT_RAS_N_BUCKETS                                 4094
44 #endif
45
46 #ifndef RTE_PORT_RAS_N_ENTRIES_PER_BUCKET
47 #define RTE_PORT_RAS_N_ENTRIES_PER_BUCKET                      8
48 #endif
49
50 #ifndef RTE_PORT_RAS_N_ENTRIES
51 #define RTE_PORT_RAS_N_ENTRIES (RTE_PORT_RAS_N_BUCKETS * RTE_PORT_RAS_N_ENTRIES_PER_BUCKET)
52 #endif
53
54 struct rte_port_ring_writer_ras;
55
56 typedef void (*ras_op)(
57                 struct rte_port_ring_writer_ras *p,
58                 struct rte_mbuf *pkt);
59
60 static void
61 process_ipv4(struct rte_port_ring_writer_ras *p, struct rte_mbuf *pkt);
62 static void
63 process_ipv6(struct rte_port_ring_writer_ras *p, struct rte_mbuf *pkt);
64
65 struct rte_port_ring_writer_ras {
66         struct rte_mbuf *tx_buf[RTE_PORT_IN_BURST_SIZE_MAX];
67         struct rte_ring *ring;
68         uint32_t tx_burst_sz;
69         uint32_t tx_buf_count;
70         struct rte_ip_frag_tbl *frag_tbl;
71         struct rte_ip_frag_death_row death_row;
72
73         ras_op f_ras;
74 };
75
76 static void *
77 rte_port_ring_writer_ras_create(void *params, int socket_id, int is_ipv4)
78 {
79         struct rte_port_ring_writer_ras_params *conf =
80                         (struct rte_port_ring_writer_ras_params *) params;
81         struct rte_port_ring_writer_ras *port;
82         uint64_t frag_cycles;
83
84         /* Check input parameters */
85         if (conf == NULL) {
86                 RTE_LOG(ERR, PORT, "%s: Parameter conf is NULL\n", __func__);
87                 return NULL;
88         }
89         if (conf->ring == NULL) {
90                 RTE_LOG(ERR, PORT, "%s: Parameter ring is NULL\n", __func__);
91                 return NULL;
92         }
93         if ((conf->tx_burst_sz == 0) ||
94             (conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX)) {
95                 RTE_LOG(ERR, PORT, "%s: Parameter tx_burst_sz is invalid\n",
96                         __func__);
97                 return NULL;
98         }
99
100         /* Memory allocation */
101         port = rte_zmalloc_socket("PORT", sizeof(*port),
102                         RTE_CACHE_LINE_SIZE, socket_id);
103         if (port == NULL) {
104                 RTE_LOG(ERR, PORT, "%s: Failed to allocate socket\n", __func__);
105                 return NULL;
106         }
107
108         /* Create fragmentation table */
109         frag_cycles = (rte_get_tsc_hz() + MS_PER_S - 1) / MS_PER_S * MS_PER_S;
110         frag_cycles *= 100;
111
112         port->frag_tbl = rte_ip_frag_table_create(
113                 RTE_PORT_RAS_N_BUCKETS,
114                 RTE_PORT_RAS_N_ENTRIES_PER_BUCKET,
115                 RTE_PORT_RAS_N_ENTRIES,
116                 frag_cycles,
117                 socket_id);
118
119         if (port->frag_tbl == NULL) {
120                 RTE_LOG(ERR, PORT, "%s: rte_ip_frag_table_create failed\n",
121                         __func__);
122                 rte_free(port);
123                 return NULL;
124         }
125
126         /* Initialization */
127         port->ring = conf->ring;
128         port->tx_burst_sz = conf->tx_burst_sz;
129         port->tx_buf_count = 0;
130
131         port->f_ras = (is_ipv4 == 0) ? process_ipv4 : process_ipv6;
132
133         return port;
134 }
135
136 static void *
137 rte_port_ring_writer_ipv4_ras_create(void *params, int socket_id)
138 {
139         return rte_port_ring_writer_ras_create(params, socket_id, 1);
140 }
141
142 static void *
143 rte_port_ring_writer_ipv6_ras_create(void *params, int socket_id)
144 {
145         return rte_port_ring_writer_ras_create(params, socket_id, 0);
146 }
147
148 static inline void
149 send_burst(struct rte_port_ring_writer_ras *p)
150 {
151         uint32_t nb_tx;
152
153         nb_tx = rte_ring_sp_enqueue_burst(p->ring, (void **)p->tx_buf,
154                         p->tx_buf_count);
155
156         for ( ; nb_tx < p->tx_buf_count; nb_tx++)
157                 rte_pktmbuf_free(p->tx_buf[nb_tx]);
158
159         p->tx_buf_count = 0;
160 }
161
162 static void
163 process_ipv4(struct rte_port_ring_writer_ras *p, struct rte_mbuf *pkt)
164 {
165         /* Assume there is no ethernet header */
166         struct ipv4_hdr *pkt_hdr = (struct ipv4_hdr *)
167                         (rte_pktmbuf_mtod(pkt, unsigned char *));
168
169         /* Get "Do not fragment" flag and fragment offset */
170         uint16_t frag_field = rte_be_to_cpu_16(pkt_hdr->fragment_offset);
171         uint16_t frag_offset = (uint16_t)(frag_field & IPV4_HDR_OFFSET_MASK);
172         uint16_t frag_flag = (uint16_t)(frag_field & IPV4_HDR_MF_FLAG);
173
174         /* If it is a fragmented packet, then try to reassemble */
175         if ((frag_flag == 0) && (frag_offset == 0))
176                 p->tx_buf[p->tx_buf_count++] = pkt;
177         else {
178                 struct rte_mbuf *mo;
179                 struct rte_ip_frag_tbl *tbl = p->frag_tbl;
180                 struct rte_ip_frag_death_row *dr = &p->death_row;
181
182                 /* Process this fragment */
183                 mo = rte_ipv4_frag_reassemble_packet(tbl, dr, pkt, rte_rdtsc(),
184                                 pkt_hdr);
185                 if (mo != NULL)
186                         p->tx_buf[p->tx_buf_count++] = mo;
187
188                 rte_ip_frag_free_death_row(&p->death_row, 3);
189         }
190 }
191
192 static void
193 process_ipv6(struct rte_port_ring_writer_ras *p, struct rte_mbuf *pkt)
194 {
195         /* Assume there is no ethernet header */
196         struct ipv6_hdr *pkt_hdr = (struct ipv6_hdr *)
197                         (rte_pktmbuf_mtod(pkt, unsigned char *));
198
199         struct ipv6_extension_fragment *frag_hdr;
200         frag_hdr = rte_ipv6_frag_get_ipv6_fragment_header(pkt_hdr);
201         uint16_t frag_offset = frag_hdr->frag_offset;
202         uint16_t frag_flag = frag_hdr->more_frags;
203
204         /* If it is a fragmented packet, then try to reassemble */
205         if ((frag_flag == 0) && (frag_offset == 0))
206                 p->tx_buf[p->tx_buf_count++] = pkt;
207         else {
208                 struct rte_mbuf *mo;
209                 struct rte_ip_frag_tbl *tbl = p->frag_tbl;
210                 struct rte_ip_frag_death_row *dr = &p->death_row;
211
212                 /* Process this fragment */
213                 mo = rte_ipv6_frag_reassemble_packet(tbl, dr, pkt, rte_rdtsc(), pkt_hdr,
214                                 frag_hdr);
215                 if (mo != NULL)
216                         p->tx_buf[p->tx_buf_count++] = mo;
217
218                 rte_ip_frag_free_death_row(&p->death_row, 3);
219         }
220 }
221
222 static int
223 rte_port_ring_writer_ras_tx(void *port, struct rte_mbuf *pkt)
224 {
225         struct rte_port_ring_writer_ras *p =
226                         (struct rte_port_ring_writer_ras *) port;
227
228         p->f_ras(p, pkt);
229         if (p->tx_buf_count >= p->tx_burst_sz)
230                 send_burst(p);
231
232         return 0;
233 }
234
235 static int
236 rte_port_ring_writer_ras_tx_bulk(void *port,
237                 struct rte_mbuf **pkts,
238                 uint64_t pkts_mask)
239 {
240         struct rte_port_ring_writer_ras *p =
241                         (struct rte_port_ring_writer_ras *) port;
242
243         if ((pkts_mask & (pkts_mask + 1)) == 0) {
244                 uint64_t n_pkts = __builtin_popcountll(pkts_mask);
245                 uint32_t i;
246
247                 for (i = 0; i < n_pkts; i++) {
248                         struct rte_mbuf *pkt = pkts[i];
249
250                         p->f_ras(p, pkt);
251                         if (p->tx_buf_count >= p->tx_burst_sz)
252                                 send_burst(p);
253                 }
254         } else {
255                 for ( ; pkts_mask; ) {
256                         uint32_t pkt_index = __builtin_ctzll(pkts_mask);
257                         uint64_t pkt_mask = 1LLU << pkt_index;
258                         struct rte_mbuf *pkt = pkts[pkt_index];
259
260                         p->f_ras(p, pkt);
261                         if (p->tx_buf_count >= p->tx_burst_sz)
262                                 send_burst(p);
263
264                         pkts_mask &= ~pkt_mask;
265                 }
266         }
267
268         return 0;
269 }
270
271 static int
272 rte_port_ring_writer_ras_flush(void *port)
273 {
274         struct rte_port_ring_writer_ras *p =
275                         (struct rte_port_ring_writer_ras *) port;
276
277         if (p->tx_buf_count > 0)
278                 send_burst(p);
279
280         return 0;
281 }
282
283 static int
284 rte_port_ring_writer_ras_free(void *port)
285 {
286         struct rte_port_ring_writer_ras *p =
287                         (struct rte_port_ring_writer_ras *) port;
288
289         if (port == NULL) {
290                 RTE_LOG(ERR, PORT, "%s: Parameter port is NULL\n", __func__);
291                 return -1;
292         }
293
294         rte_port_ring_writer_ras_flush(port);
295         rte_ip_frag_table_destroy(p->frag_tbl);
296         rte_free(port);
297
298         return 0;
299 }
300
301 /*
302  * Summary of port operations
303  */
304 struct rte_port_out_ops rte_port_ring_writer_ipv4_ras_ops = {
305         .f_create = rte_port_ring_writer_ipv4_ras_create,
306         .f_free = rte_port_ring_writer_ras_free,
307         .f_tx = rte_port_ring_writer_ras_tx,
308         .f_tx_bulk = rte_port_ring_writer_ras_tx_bulk,
309         .f_flush = rte_port_ring_writer_ras_flush,
310 };
311
312 struct rte_port_out_ops rte_port_ring_writer_ipv6_ras_ops = {
313         .f_create = rte_port_ring_writer_ipv6_ras_create,
314         .f_free = rte_port_ring_writer_ras_free,
315         .f_tx = rte_port_ring_writer_ras_tx,
316         .f_tx_bulk = rte_port_ring_writer_ras_tx_bulk,
317         .f_flush = rte_port_ring_writer_ras_flush,
318 };