app/testpmd: support TSO in checksum forward engine
[dpdk.git] / app / test-pmd / csumonly.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   Copyright 2014 6WIND S.A.
6  *   All rights reserved.
7  *
8  *   Redistribution and use in source and binary forms, with or without
9  *   modification, are permitted provided that the following conditions
10  *   are met:
11  *
12  *     * Redistributions of source code must retain the above copyright
13  *       notice, this list of conditions and the following disclaimer.
14  *     * Redistributions in binary form must reproduce the above copyright
15  *       notice, this list of conditions and the following disclaimer in
16  *       the documentation and/or other materials provided with the
17  *       distribution.
18  *     * Neither the name of Intel Corporation nor the names of its
19  *       contributors may be used to endorse or promote products derived
20  *       from this software without specific prior written permission.
21  *
22  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <stdint.h>
39 #include <unistd.h>
40 #include <inttypes.h>
41
42 #include <sys/queue.h>
43 #include <sys/stat.h>
44
45 #include <rte_common.h>
46 #include <rte_byteorder.h>
47 #include <rte_log.h>
48 #include <rte_debug.h>
49 #include <rte_cycles.h>
50 #include <rte_memory.h>
51 #include <rte_memcpy.h>
52 #include <rte_memzone.h>
53 #include <rte_launch.h>
54 #include <rte_tailq.h>
55 #include <rte_eal.h>
56 #include <rte_per_lcore.h>
57 #include <rte_lcore.h>
58 #include <rte_atomic.h>
59 #include <rte_branch_prediction.h>
60 #include <rte_ring.h>
61 #include <rte_memory.h>
62 #include <rte_mempool.h>
63 #include <rte_mbuf.h>
64 #include <rte_memcpy.h>
65 #include <rte_interrupts.h>
66 #include <rte_pci.h>
67 #include <rte_ether.h>
68 #include <rte_ethdev.h>
69 #include <rte_ip.h>
70 #include <rte_tcp.h>
71 #include <rte_udp.h>
72 #include <rte_sctp.h>
73 #include <rte_prefetch.h>
74 #include <rte_string_fns.h>
75 #include "testpmd.h"
76
77 #define IP_DEFTTL  64   /* from RFC 1340. */
78 #define IP_VERSION 0x40
79 #define IP_HDRLEN  0x05 /* default IP header length == five 32-bits words. */
80 #define IP_VHL_DEF (IP_VERSION | IP_HDRLEN)
81
82 /* we cannot use htons() from arpa/inet.h due to name conflicts, and we
83  * cannot use rte_cpu_to_be_16() on a constant in a switch/case */
84 #if __BYTE_ORDER == __LITTLE_ENDIAN
85 #define _htons(x) ((uint16_t)((((x) & 0x00ffU) << 8) | (((x) & 0xff00U) >> 8)))
86 #else
87 #define _htons(x) (x)
88 #endif
89
90 static uint16_t
91 get_psd_sum(void *l3_hdr, uint16_t ethertype, uint64_t ol_flags)
92 {
93         if (ethertype == _htons(ETHER_TYPE_IPv4))
94                 return rte_ipv4_phdr_cksum(l3_hdr, ol_flags);
95         else /* assume ethertype == ETHER_TYPE_IPv6 */
96                 return rte_ipv6_phdr_cksum(l3_hdr, ol_flags);
97 }
98
99 static uint16_t
100 get_udptcp_checksum(void *l3_hdr, void *l4_hdr, uint16_t ethertype)
101 {
102         if (ethertype == _htons(ETHER_TYPE_IPv4))
103                 return rte_ipv4_udptcp_cksum(l3_hdr, l4_hdr);
104         else /* assume ethertype == ETHER_TYPE_IPv6 */
105                 return rte_ipv6_udptcp_cksum(l3_hdr, l4_hdr);
106 }
107
108 /*
109  * Parse an ethernet header to fill the ethertype, l2_len, l3_len and
110  * ipproto. This function is able to recognize IPv4/IPv6 with one optional vlan
111  * header. The l4_len argument is only set in case of TCP (useful for TSO).
112  */
113 static void
114 parse_ethernet(struct ether_hdr *eth_hdr, uint16_t *ethertype, uint16_t *l2_len,
115         uint16_t *l3_len, uint8_t *l4_proto, uint16_t *l4_len)
116 {
117         struct ipv4_hdr *ipv4_hdr;
118         struct ipv6_hdr *ipv6_hdr;
119         struct tcp_hdr *tcp_hdr;
120
121         *l2_len = sizeof(struct ether_hdr);
122         *ethertype = eth_hdr->ether_type;
123
124         if (*ethertype == _htons(ETHER_TYPE_VLAN)) {
125                 struct vlan_hdr *vlan_hdr = (struct vlan_hdr *)(eth_hdr + 1);
126
127                 *l2_len  += sizeof(struct vlan_hdr);
128                 *ethertype = vlan_hdr->eth_proto;
129         }
130
131         switch (*ethertype) {
132         case _htons(ETHER_TYPE_IPv4):
133                 ipv4_hdr = (struct ipv4_hdr *) ((char *)eth_hdr + *l2_len);
134                 *l3_len = (ipv4_hdr->version_ihl & 0x0f) * 4;
135                 *l4_proto = ipv4_hdr->next_proto_id;
136                 break;
137         case _htons(ETHER_TYPE_IPv6):
138                 ipv6_hdr = (struct ipv6_hdr *) ((char *)eth_hdr + *l2_len);
139                 *l3_len = sizeof(struct ipv6_hdr);
140                 *l4_proto = ipv6_hdr->proto;
141                 break;
142         default:
143                 *l3_len = 0;
144                 *l4_proto = 0;
145                 break;
146         }
147
148         if (*l4_proto == IPPROTO_TCP) {
149                 tcp_hdr = (struct tcp_hdr *)((char *)eth_hdr +
150                         *l2_len + *l3_len);
151                 *l4_len = (tcp_hdr->data_off & 0xf0) >> 2;
152         } else
153                 *l4_len = 0;
154 }
155
156 /* modify the IPv4 or IPv4 source address of a packet */
157 static void
158 change_ip_addresses(void *l3_hdr, uint16_t ethertype)
159 {
160         struct ipv4_hdr *ipv4_hdr = l3_hdr;
161         struct ipv6_hdr *ipv6_hdr = l3_hdr;
162
163         if (ethertype == _htons(ETHER_TYPE_IPv4)) {
164                 ipv4_hdr->src_addr =
165                         rte_cpu_to_be_32(rte_be_to_cpu_32(ipv4_hdr->src_addr) + 1);
166         } else if (ethertype == _htons(ETHER_TYPE_IPv6)) {
167                 ipv6_hdr->src_addr[15] = ipv6_hdr->src_addr[15] + 1;
168         }
169 }
170
171 /* if possible, calculate the checksum of a packet in hw or sw,
172  * depending on the testpmd command line configuration */
173 static uint64_t
174 process_inner_cksums(void *l3_hdr, uint16_t ethertype, uint16_t l3_len,
175         uint8_t l4_proto, uint16_t tso_segsz, uint16_t testpmd_ol_flags)
176 {
177         struct ipv4_hdr *ipv4_hdr = l3_hdr;
178         struct udp_hdr *udp_hdr;
179         struct tcp_hdr *tcp_hdr;
180         struct sctp_hdr *sctp_hdr;
181         uint64_t ol_flags = 0;
182
183         if (ethertype == _htons(ETHER_TYPE_IPv4)) {
184                 ipv4_hdr = l3_hdr;
185                 ipv4_hdr->hdr_checksum = 0;
186
187                 if (tso_segsz != 0 && l4_proto == IPPROTO_TCP) {
188                         ol_flags |= PKT_TX_IP_CKSUM;
189                 } else {
190                         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_IP_CKSUM)
191                                 ol_flags |= PKT_TX_IP_CKSUM;
192                         else
193                                 ipv4_hdr->hdr_checksum =
194                                         rte_ipv4_cksum(ipv4_hdr);
195                 }
196                 ol_flags |= PKT_TX_IPV4;
197         } else if (ethertype == _htons(ETHER_TYPE_IPv6))
198                 ol_flags |= PKT_TX_IPV6;
199         else
200                 return 0; /* packet type not supported, nothing to do */
201
202         if (l4_proto == IPPROTO_UDP) {
203                 udp_hdr = (struct udp_hdr *)((char *)l3_hdr + l3_len);
204                 /* do not recalculate udp cksum if it was 0 */
205                 if (udp_hdr->dgram_cksum != 0) {
206                         udp_hdr->dgram_cksum = 0;
207                         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_UDP_CKSUM) {
208                                 ol_flags |= PKT_TX_UDP_CKSUM;
209                                 udp_hdr->dgram_cksum = get_psd_sum(l3_hdr,
210                                         ethertype, ol_flags);
211                         } else {
212                                 udp_hdr->dgram_cksum =
213                                         get_udptcp_checksum(l3_hdr, udp_hdr,
214                                                 ethertype);
215                         }
216                 }
217         } else if (l4_proto == IPPROTO_TCP) {
218                 tcp_hdr = (struct tcp_hdr *)((char *)l3_hdr + l3_len);
219                 tcp_hdr->cksum = 0;
220                 if (tso_segsz != 0) {
221                         ol_flags |= PKT_TX_TCP_SEG;
222                         tcp_hdr->cksum = get_psd_sum(l3_hdr, ethertype, ol_flags);
223                 } else if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_TCP_CKSUM) {
224                         ol_flags |= PKT_TX_TCP_CKSUM;
225                         tcp_hdr->cksum = get_psd_sum(l3_hdr, ethertype, ol_flags);
226                 } else {
227                         tcp_hdr->cksum =
228                                 get_udptcp_checksum(l3_hdr, tcp_hdr, ethertype);
229                 }
230         } else if (l4_proto == IPPROTO_SCTP) {
231                 sctp_hdr = (struct sctp_hdr *)((char *)l3_hdr + l3_len);
232                 sctp_hdr->cksum = 0;
233                 /* sctp payload must be a multiple of 4 to be
234                  * offloaded */
235                 if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_SCTP_CKSUM) &&
236                         ((ipv4_hdr->total_length & 0x3) == 0)) {
237                         ol_flags |= PKT_TX_SCTP_CKSUM;
238                 } else {
239                         /* XXX implement CRC32c, example available in
240                          * RFC3309 */
241                 }
242         }
243
244         return ol_flags;
245 }
246
247 /* Calculate the checksum of outer header (only vxlan is supported,
248  * meaning IP + UDP). The caller already checked that it's a vxlan
249  * packet */
250 static uint64_t
251 process_outer_cksums(void *outer_l3_hdr, uint16_t outer_ethertype,
252         uint16_t outer_l3_len, uint16_t testpmd_ol_flags)
253 {
254         struct ipv4_hdr *ipv4_hdr = outer_l3_hdr;
255         struct ipv6_hdr *ipv6_hdr = outer_l3_hdr;
256         struct udp_hdr *udp_hdr;
257         uint64_t ol_flags = 0;
258
259         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM)
260                 ol_flags |= PKT_TX_VXLAN_CKSUM;
261
262         if (outer_ethertype == _htons(ETHER_TYPE_IPv4)) {
263                 ipv4_hdr->hdr_checksum = 0;
264
265                 if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) == 0)
266                         ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
267         }
268
269         udp_hdr = (struct udp_hdr *)((char *)outer_l3_hdr + outer_l3_len);
270         /* do not recalculate udp cksum if it was 0 */
271         if (udp_hdr->dgram_cksum != 0) {
272                 udp_hdr->dgram_cksum = 0;
273                 if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) == 0) {
274                         if (outer_ethertype == _htons(ETHER_TYPE_IPv4))
275                                 udp_hdr->dgram_cksum =
276                                         rte_ipv4_udptcp_cksum(ipv4_hdr, udp_hdr);
277                         else
278                                 udp_hdr->dgram_cksum =
279                                         rte_ipv6_udptcp_cksum(ipv6_hdr, udp_hdr);
280                 }
281         }
282
283         return ol_flags;
284 }
285
286 /*
287  * Receive a burst of packets, and for each packet:
288  *  - parse packet, and try to recognize a supported packet type (1)
289  *  - if it's not a supported packet type, don't touch the packet, else:
290  *  - modify the IPs in inner headers and in outer headers if any
291  *  - reprocess the checksum of all supported layers. This is done in SW
292  *    or HW, depending on testpmd command line configuration
293  *  - if TSO is enabled in testpmd command line, also flag the mbuf for TCP
294  *    segmentation offload (this implies HW TCP checksum)
295  * Then transmit packets on the output port.
296  *
297  * (1) Supported packets are:
298  *   Ether / (vlan) / IP|IP6 / UDP|TCP|SCTP .
299  *   Ether / (vlan) / outer IP|IP6 / outer UDP / VxLAN / Ether / IP|IP6 /
300  *           UDP|TCP|SCTP
301  *
302  * The testpmd command line for this forward engine sets the flags
303  * TESTPMD_TX_OFFLOAD_* in ports[tx_port].tx_ol_flags. They control
304  * wether a checksum must be calculated in software or in hardware. The
305  * IP, UDP, TCP and SCTP flags always concern the inner layer.  The
306  * VxLAN flag concerns the outer IP and UDP layer (if packet is
307  * recognized as a vxlan packet).
308  */
309 static void
310 pkt_burst_checksum_forward(struct fwd_stream *fs)
311 {
312         struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
313         struct rte_port *txp;
314         struct rte_mbuf *m;
315         struct ether_hdr *eth_hdr;
316         void *l3_hdr = NULL, *outer_l3_hdr = NULL; /* can be IPv4 or IPv6 */
317         struct udp_hdr *udp_hdr;
318         uint16_t nb_rx;
319         uint16_t nb_tx;
320         uint16_t i;
321         uint64_t ol_flags;
322         uint16_t testpmd_ol_flags;
323         uint8_t l4_proto;
324         uint16_t ethertype = 0, outer_ethertype = 0;
325         uint16_t l2_len = 0, l3_len = 0, l4_len = 0;
326         uint16_t outer_l2_len = 0, outer_l3_len = 0;
327         uint16_t tso_segsz;
328         int tunnel = 0;
329         uint32_t rx_bad_ip_csum;
330         uint32_t rx_bad_l4_csum;
331
332 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
333         uint64_t start_tsc;
334         uint64_t end_tsc;
335         uint64_t core_cycles;
336 #endif
337
338 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
339         start_tsc = rte_rdtsc();
340 #endif
341
342         /* receive a burst of packet */
343         nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
344                                  nb_pkt_per_burst);
345         if (unlikely(nb_rx == 0))
346                 return;
347
348 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
349         fs->rx_burst_stats.pkt_burst_spread[nb_rx]++;
350 #endif
351         fs->rx_packets += nb_rx;
352         rx_bad_ip_csum = 0;
353         rx_bad_l4_csum = 0;
354
355         txp = &ports[fs->tx_port];
356         testpmd_ol_flags = txp->tx_ol_flags;
357         tso_segsz = txp->tso_segsz;
358
359         for (i = 0; i < nb_rx; i++) {
360
361                 ol_flags = 0;
362                 tunnel = 0;
363                 m = pkts_burst[i];
364
365                 /* Update the L3/L4 checksum error packet statistics */
366                 rx_bad_ip_csum += ((m->ol_flags & PKT_RX_IP_CKSUM_BAD) != 0);
367                 rx_bad_l4_csum += ((m->ol_flags & PKT_RX_L4_CKSUM_BAD) != 0);
368
369                 /* step 1: dissect packet, parsing optional vlan, ip4/ip6, vxlan
370                  * and inner headers */
371
372                 eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
373                 parse_ethernet(eth_hdr, &ethertype, &l2_len, &l3_len,
374                         &l4_proto, &l4_len);
375                 l3_hdr = (char *)eth_hdr + l2_len;
376
377                 /* check if it's a supported tunnel (only vxlan for now) */
378                 if (l4_proto == IPPROTO_UDP) {
379                         udp_hdr = (struct udp_hdr *)((char *)l3_hdr + l3_len);
380
381                         /* currently, this flag is set by i40e only if the
382                          * packet is vxlan */
383                         if (((m->ol_flags & PKT_RX_TUNNEL_IPV4_HDR) ||
384                                         (m->ol_flags & PKT_RX_TUNNEL_IPV6_HDR)))
385                                 tunnel = 1;
386                         /* else check udp destination port, 4789 is the default
387                          * vxlan port (rfc7348) */
388                         else if (udp_hdr->dst_port == _htons(4789))
389                                 tunnel = 1;
390
391                         if (tunnel == 1) {
392                                 outer_ethertype = ethertype;
393                                 outer_l2_len = l2_len;
394                                 outer_l3_len = l3_len;
395                                 outer_l3_hdr = l3_hdr;
396
397                                 eth_hdr = (struct ether_hdr *)((char *)udp_hdr +
398                                         sizeof(struct udp_hdr) +
399                                         sizeof(struct vxlan_hdr));
400
401                                 parse_ethernet(eth_hdr, &ethertype, &l2_len,
402                                         &l3_len, &l4_proto, &l4_len);
403                                 l3_hdr = (char *)eth_hdr + l2_len;
404                         }
405                 }
406
407                 /* step 2: change all source IPs (v4 or v6) so we need
408                  * to recompute the chksums even if they were correct */
409
410                 change_ip_addresses(l3_hdr, ethertype);
411                 if (tunnel == 1)
412                         change_ip_addresses(outer_l3_hdr, outer_ethertype);
413
414                 /* step 3: depending on user command line configuration,
415                  * recompute checksum either in software or flag the
416                  * mbuf to offload the calculation to the NIC. If TSO
417                  * is configured, prepare the mbuf for TCP segmentation. */
418
419                 /* process checksums of inner headers first */
420                 ol_flags |= process_inner_cksums(l3_hdr, ethertype,
421                         l3_len, l4_proto, tso_segsz, testpmd_ol_flags);
422
423                 /* Then process outer headers if any. Note that the software
424                  * checksum will be wrong if one of the inner checksums is
425                  * processed in hardware. */
426                 if (tunnel == 1) {
427                         ol_flags |= process_outer_cksums(outer_l3_hdr,
428                                 outer_ethertype, outer_l3_len, testpmd_ol_flags);
429                 }
430
431                 /* step 4: fill the mbuf meta data (flags and header lengths) */
432
433                 if (tunnel == 1) {
434                         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) {
435                                 m->l2_len = outer_l2_len;
436                                 m->l3_len = outer_l3_len;
437                                 m->inner_l2_len = l2_len;
438                                 m->inner_l3_len = l3_len;
439                         }
440                         else {
441                                 /* if we don't do vxlan cksum in hw,
442                                    outer checksum will be wrong because
443                                    we changed the ip, but it shows that
444                                    we can process the inner header cksum
445                                    in the nic */
446                                 m->l2_len = outer_l2_len + outer_l3_len +
447                                         sizeof(struct udp_hdr) +
448                                         sizeof(struct vxlan_hdr) + l2_len;
449                                 m->l3_len = l3_len;
450                                 m->l4_len = l4_len;
451                         }
452                 } else {
453                         /* this is only useful if an offload flag is
454                          * set, but it does not hurt to fill it in any
455                          * case */
456                         m->l2_len = l2_len;
457                         m->l3_len = l3_len;
458                         m->l4_len = l4_len;
459                 }
460                 m->tso_segsz = tso_segsz;
461                 m->ol_flags = ol_flags;
462
463         }
464         nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, pkts_burst, nb_rx);
465         fs->tx_packets += nb_tx;
466         fs->rx_bad_ip_csum += rx_bad_ip_csum;
467         fs->rx_bad_l4_csum += rx_bad_l4_csum;
468
469 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
470         fs->tx_burst_stats.pkt_burst_spread[nb_tx]++;
471 #endif
472         if (unlikely(nb_tx < nb_rx)) {
473                 fs->fwd_dropped += (nb_rx - nb_tx);
474                 do {
475                         rte_pktmbuf_free(pkts_burst[nb_tx]);
476                 } while (++nb_tx < nb_rx);
477         }
478 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
479         end_tsc = rte_rdtsc();
480         core_cycles = (end_tsc - start_tsc);
481         fs->core_cycles = (uint64_t) (fs->core_cycles + core_cycles);
482 #endif
483 }
484
485 struct fwd_engine csum_fwd_engine = {
486         .fwd_mode_name  = "csum",
487         .port_fwd_begin = NULL,
488         .port_fwd_end   = NULL,
489         .packet_fwd     = pkt_burst_checksum_forward,
490 };
491