mbuf: enhance the API documentation of offload flags
[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 rte_cpu_to_be_16() on a constant in a switch/case */
83 #if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
84 #define _htons(x) ((uint16_t)((((x) & 0x00ffU) << 8) | (((x) & 0xff00U) >> 8)))
85 #else
86 #define _htons(x) (x)
87 #endif
88
89 static uint16_t
90 get_psd_sum(void *l3_hdr, uint16_t ethertype, uint64_t ol_flags)
91 {
92         if (ethertype == _htons(ETHER_TYPE_IPv4))
93                 return rte_ipv4_phdr_cksum(l3_hdr, ol_flags);
94         else /* assume ethertype == ETHER_TYPE_IPv6 */
95                 return rte_ipv6_phdr_cksum(l3_hdr, ol_flags);
96 }
97
98 static uint16_t
99 get_udptcp_checksum(void *l3_hdr, void *l4_hdr, uint16_t ethertype)
100 {
101         if (ethertype == _htons(ETHER_TYPE_IPv4))
102                 return rte_ipv4_udptcp_cksum(l3_hdr, l4_hdr);
103         else /* assume ethertype == ETHER_TYPE_IPv6 */
104                 return rte_ipv6_udptcp_cksum(l3_hdr, l4_hdr);
105 }
106
107 /*
108  * Parse an ethernet header to fill the ethertype, l2_len, l3_len and
109  * ipproto. This function is able to recognize IPv4/IPv6 with one optional vlan
110  * header. The l4_len argument is only set in case of TCP (useful for TSO).
111  */
112 static void
113 parse_ethernet(struct ether_hdr *eth_hdr, uint16_t *ethertype, uint16_t *l2_len,
114         uint16_t *l3_len, uint8_t *l4_proto, uint16_t *l4_len)
115 {
116         struct ipv4_hdr *ipv4_hdr;
117         struct ipv6_hdr *ipv6_hdr;
118         struct tcp_hdr *tcp_hdr;
119
120         *l2_len = sizeof(struct ether_hdr);
121         *ethertype = eth_hdr->ether_type;
122
123         if (*ethertype == _htons(ETHER_TYPE_VLAN)) {
124                 struct vlan_hdr *vlan_hdr = (struct vlan_hdr *)(eth_hdr + 1);
125
126                 *l2_len  += sizeof(struct vlan_hdr);
127                 *ethertype = vlan_hdr->eth_proto;
128         }
129
130         switch (*ethertype) {
131         case _htons(ETHER_TYPE_IPv4):
132                 ipv4_hdr = (struct ipv4_hdr *) ((char *)eth_hdr + *l2_len);
133                 *l3_len = (ipv4_hdr->version_ihl & 0x0f) * 4;
134                 *l4_proto = ipv4_hdr->next_proto_id;
135                 break;
136         case _htons(ETHER_TYPE_IPv6):
137                 ipv6_hdr = (struct ipv6_hdr *) ((char *)eth_hdr + *l2_len);
138                 *l3_len = sizeof(struct ipv6_hdr);
139                 *l4_proto = ipv6_hdr->proto;
140                 break;
141         default:
142                 *l3_len = 0;
143                 *l4_proto = 0;
144                 break;
145         }
146
147         if (*l4_proto == IPPROTO_TCP) {
148                 tcp_hdr = (struct tcp_hdr *)((char *)eth_hdr +
149                         *l2_len + *l3_len);
150                 *l4_len = (tcp_hdr->data_off & 0xf0) >> 2;
151         } else
152                 *l4_len = 0;
153 }
154
155 /* modify the IPv4 or IPv4 source address of a packet */
156 static void
157 change_ip_addresses(void *l3_hdr, uint16_t ethertype)
158 {
159         struct ipv4_hdr *ipv4_hdr = l3_hdr;
160         struct ipv6_hdr *ipv6_hdr = l3_hdr;
161
162         if (ethertype == _htons(ETHER_TYPE_IPv4)) {
163                 ipv4_hdr->src_addr =
164                         rte_cpu_to_be_32(rte_be_to_cpu_32(ipv4_hdr->src_addr) + 1);
165         } else if (ethertype == _htons(ETHER_TYPE_IPv6)) {
166                 ipv6_hdr->src_addr[15] = ipv6_hdr->src_addr[15] + 1;
167         }
168 }
169
170 /* if possible, calculate the checksum of a packet in hw or sw,
171  * depending on the testpmd command line configuration */
172 static uint64_t
173 process_inner_cksums(void *l3_hdr, uint16_t ethertype, uint16_t l3_len,
174         uint8_t l4_proto, uint16_t tso_segsz, uint16_t testpmd_ol_flags)
175 {
176         struct ipv4_hdr *ipv4_hdr = l3_hdr;
177         struct udp_hdr *udp_hdr;
178         struct tcp_hdr *tcp_hdr;
179         struct sctp_hdr *sctp_hdr;
180         uint64_t ol_flags = 0;
181
182         if (ethertype == _htons(ETHER_TYPE_IPv4)) {
183                 ipv4_hdr = l3_hdr;
184                 ipv4_hdr->hdr_checksum = 0;
185
186                 ol_flags |= PKT_TX_IPV4;
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         } else if (ethertype == _htons(ETHER_TYPE_IPv6))
197                 ol_flags |= PKT_TX_IPV6;
198         else
199                 return 0; /* packet type not supported, nothing to do */
200
201         if (l4_proto == IPPROTO_UDP) {
202                 udp_hdr = (struct udp_hdr *)((char *)l3_hdr + l3_len);
203                 /* do not recalculate udp cksum if it was 0 */
204                 if (udp_hdr->dgram_cksum != 0) {
205                         udp_hdr->dgram_cksum = 0;
206                         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_UDP_CKSUM) {
207                                 ol_flags |= PKT_TX_UDP_CKSUM;
208                                 udp_hdr->dgram_cksum = get_psd_sum(l3_hdr,
209                                         ethertype, ol_flags);
210                         } else {
211                                 udp_hdr->dgram_cksum =
212                                         get_udptcp_checksum(l3_hdr, udp_hdr,
213                                                 ethertype);
214                         }
215                 }
216         } else if (l4_proto == IPPROTO_TCP) {
217                 tcp_hdr = (struct tcp_hdr *)((char *)l3_hdr + l3_len);
218                 tcp_hdr->cksum = 0;
219                 if (tso_segsz != 0) {
220                         ol_flags |= PKT_TX_TCP_SEG;
221                         tcp_hdr->cksum = get_psd_sum(l3_hdr, ethertype, ol_flags);
222                 } else if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_TCP_CKSUM) {
223                         ol_flags |= PKT_TX_TCP_CKSUM;
224                         tcp_hdr->cksum = get_psd_sum(l3_hdr, ethertype, ol_flags);
225                 } else {
226                         tcp_hdr->cksum =
227                                 get_udptcp_checksum(l3_hdr, tcp_hdr, ethertype);
228                 }
229         } else if (l4_proto == IPPROTO_SCTP) {
230                 sctp_hdr = (struct sctp_hdr *)((char *)l3_hdr + l3_len);
231                 sctp_hdr->cksum = 0;
232                 /* sctp payload must be a multiple of 4 to be
233                  * offloaded */
234                 if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_SCTP_CKSUM) &&
235                         ((ipv4_hdr->total_length & 0x3) == 0)) {
236                         ol_flags |= PKT_TX_SCTP_CKSUM;
237                 } else {
238                         /* XXX implement CRC32c, example available in
239                          * RFC3309 */
240                 }
241         }
242
243         return ol_flags;
244 }
245
246 /* Calculate the checksum of outer header (only vxlan is supported,
247  * meaning IP + UDP). The caller already checked that it's a vxlan
248  * packet */
249 static uint64_t
250 process_outer_cksums(void *outer_l3_hdr, uint16_t outer_ethertype,
251         uint16_t outer_l3_len, uint16_t testpmd_ol_flags)
252 {
253         struct ipv4_hdr *ipv4_hdr = outer_l3_hdr;
254         struct ipv6_hdr *ipv6_hdr = outer_l3_hdr;
255         struct udp_hdr *udp_hdr;
256         uint64_t ol_flags = 0;
257
258         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM)
259                 ol_flags |= PKT_TX_UDP_TUNNEL_PKT;
260
261         if (outer_ethertype == _htons(ETHER_TYPE_IPv4)) {
262                 ipv4_hdr->hdr_checksum = 0;
263                 ol_flags |= PKT_TX_OUTER_IPV4;
264
265                 if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM)
266                         ol_flags |= PKT_TX_OUTER_IP_CKSUM;
267                 else
268                         ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
269         } else if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM)
270                 ol_flags |= PKT_TX_OUTER_IPV6;
271
272         udp_hdr = (struct udp_hdr *)((char *)outer_l3_hdr + outer_l3_len);
273         /* do not recalculate udp cksum if it was 0 */
274         if (udp_hdr->dgram_cksum != 0) {
275                 udp_hdr->dgram_cksum = 0;
276                 if (outer_ethertype == _htons(ETHER_TYPE_IPv4))
277                         udp_hdr->dgram_cksum =
278                                 rte_ipv4_udptcp_cksum(ipv4_hdr, udp_hdr);
279                 else
280                         udp_hdr->dgram_cksum =
281                                 rte_ipv6_udptcp_cksum(ipv6_hdr, udp_hdr);
282         }
283
284         return ol_flags;
285 }
286
287 /*
288  * Receive a burst of packets, and for each packet:
289  *  - parse packet, and try to recognize a supported packet type (1)
290  *  - if it's not a supported packet type, don't touch the packet, else:
291  *  - modify the IPs in inner headers and in outer headers if any
292  *  - reprocess the checksum of all supported layers. This is done in SW
293  *    or HW, depending on testpmd command line configuration
294  *  - if TSO is enabled in testpmd command line, also flag the mbuf for TCP
295  *    segmentation offload (this implies HW TCP checksum)
296  * Then transmit packets on the output port.
297  *
298  * (1) Supported packets are:
299  *   Ether / (vlan) / IP|IP6 / UDP|TCP|SCTP .
300  *   Ether / (vlan) / outer IP|IP6 / outer UDP / VxLAN / Ether / IP|IP6 /
301  *           UDP|TCP|SCTP
302  *
303  * The testpmd command line for this forward engine sets the flags
304  * TESTPMD_TX_OFFLOAD_* in ports[tx_port].tx_ol_flags. They control
305  * wether a checksum must be calculated in software or in hardware. The
306  * IP, UDP, TCP and SCTP flags always concern the inner layer.  The
307  * VxLAN flag concerns the outer IP (if packet is 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, l4_tun_len = 0;
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                 l4_tun_len = 0;
364                 m = pkts_burst[i];
365
366                 /* Update the L3/L4 checksum error packet statistics */
367                 rx_bad_ip_csum += ((m->ol_flags & PKT_RX_IP_CKSUM_BAD) != 0);
368                 rx_bad_l4_csum += ((m->ol_flags & PKT_RX_L4_CKSUM_BAD) != 0);
369
370                 /* step 1: dissect packet, parsing optional vlan, ip4/ip6, vxlan
371                  * and inner headers */
372
373                 eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
374                 parse_ethernet(eth_hdr, &ethertype, &l2_len, &l3_len,
375                         &l4_proto, &l4_len);
376                 l3_hdr = (char *)eth_hdr + l2_len;
377
378                 /* check if it's a supported tunnel (only vxlan for now) */
379                 if (l4_proto == IPPROTO_UDP) {
380                         udp_hdr = (struct udp_hdr *)((char *)l3_hdr + l3_len);
381
382                         /* check udp destination port, 4789 is the default
383                          * vxlan port (rfc7348) */
384                         if (udp_hdr->dst_port == _htons(4789)) {
385                                 l4_tun_len = ETHER_VXLAN_HLEN;
386                                 tunnel = 1;
387
388                         /* currently, this flag is set by i40e only if the
389                          * packet is vxlan */
390                         } else if (m->ol_flags & (PKT_RX_TUNNEL_IPV4_HDR |
391                                         PKT_RX_TUNNEL_IPV6_HDR))
392                                 tunnel = 1;
393
394                         if (tunnel == 1) {
395                                 outer_ethertype = ethertype;
396                                 outer_l2_len = l2_len;
397                                 outer_l3_len = l3_len;
398                                 outer_l3_hdr = l3_hdr;
399
400                                 eth_hdr = (struct ether_hdr *)((char *)udp_hdr +
401                                         sizeof(struct udp_hdr) +
402                                         sizeof(struct vxlan_hdr));
403
404                                 parse_ethernet(eth_hdr, &ethertype, &l2_len,
405                                         &l3_len, &l4_proto, &l4_len);
406                                 l3_hdr = (char *)eth_hdr + l2_len;
407                         }
408                 }
409
410                 /* step 2: change all source IPs (v4 or v6) so we need
411                  * to recompute the chksums even if they were correct */
412
413                 change_ip_addresses(l3_hdr, ethertype);
414                 if (tunnel == 1)
415                         change_ip_addresses(outer_l3_hdr, outer_ethertype);
416
417                 /* step 3: depending on user command line configuration,
418                  * recompute checksum either in software or flag the
419                  * mbuf to offload the calculation to the NIC. If TSO
420                  * is configured, prepare the mbuf for TCP segmentation. */
421
422                 /* process checksums of inner headers first */
423                 ol_flags |= process_inner_cksums(l3_hdr, ethertype,
424                         l3_len, l4_proto, tso_segsz, testpmd_ol_flags);
425
426                 /* Then process outer headers if any. Note that the software
427                  * checksum will be wrong if one of the inner checksums is
428                  * processed in hardware. */
429                 if (tunnel == 1) {
430                         ol_flags |= process_outer_cksums(outer_l3_hdr,
431                                 outer_ethertype, outer_l3_len, testpmd_ol_flags);
432                 }
433
434                 /* step 4: fill the mbuf meta data (flags and header lengths) */
435
436                 if (tunnel == 1) {
437                         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) {
438                                 m->outer_l2_len = outer_l2_len;
439                                 m->outer_l3_len = outer_l3_len;
440                                 m->l2_len = l4_tun_len + l2_len;
441                                 m->l3_len = l3_len;
442                         }
443                         else {
444                                 /* if we don't do vxlan cksum in hw,
445                                    outer checksum will be wrong because
446                                    we changed the ip, but it shows that
447                                    we can process the inner header cksum
448                                    in the nic */
449                                 m->l2_len = outer_l2_len + outer_l3_len +
450                                         sizeof(struct udp_hdr) +
451                                         sizeof(struct vxlan_hdr) + l2_len;
452                                 m->l3_len = l3_len;
453                                 m->l4_len = l4_len;
454                         }
455                 } else {
456                         /* this is only useful if an offload flag is
457                          * set, but it does not hurt to fill it in any
458                          * case */
459                         m->l2_len = l2_len;
460                         m->l3_len = l3_len;
461                         m->l4_len = l4_len;
462                 }
463                 m->tso_segsz = tso_segsz;
464                 m->ol_flags = ol_flags;
465
466                 /* if verbose mode is enabled, dump debug info */
467                 if (verbose_level > 0) {
468                         struct {
469                                 uint64_t flag;
470                                 uint64_t mask;
471                         } tx_flags[] = {
472                                 { PKT_TX_IP_CKSUM, PKT_TX_IP_CKSUM },
473                                 { PKT_TX_UDP_CKSUM, PKT_TX_L4_MASK },
474                                 { PKT_TX_TCP_CKSUM, PKT_TX_L4_MASK },
475                                 { PKT_TX_SCTP_CKSUM, PKT_TX_L4_MASK },
476                                 { PKT_TX_UDP_TUNNEL_PKT, PKT_TX_UDP_TUNNEL_PKT },
477                                 { PKT_TX_IPV4, PKT_TX_IPV4 },
478                                 { PKT_TX_IPV6, PKT_TX_IPV6 },
479                                 { PKT_TX_OUTER_IP_CKSUM, PKT_TX_OUTER_IP_CKSUM },
480                                 { PKT_TX_OUTER_IPV4, PKT_TX_OUTER_IPV4 },
481                                 { PKT_TX_OUTER_IPV6, PKT_TX_OUTER_IPV6 },
482                                 { PKT_TX_TCP_SEG, PKT_TX_TCP_SEG },
483                         };
484                         unsigned j;
485                         const char *name;
486
487                         printf("-----------------\n");
488                         /* dump rx parsed packet info */
489                         printf("rx: l2_len=%d ethertype=%x l3_len=%d "
490                                 "l4_proto=%d l4_len=%d\n",
491                                 l2_len, rte_be_to_cpu_16(ethertype),
492                                 l3_len, l4_proto, l4_len);
493                         if (tunnel == 1)
494                                 printf("rx: outer_l2_len=%d outer_ethertype=%x "
495                                         "outer_l3_len=%d\n", outer_l2_len,
496                                         rte_be_to_cpu_16(outer_ethertype),
497                                         outer_l3_len);
498                         /* dump tx packet info */
499                         if ((testpmd_ol_flags & (TESTPMD_TX_OFFLOAD_IP_CKSUM |
500                                                 TESTPMD_TX_OFFLOAD_UDP_CKSUM |
501                                                 TESTPMD_TX_OFFLOAD_TCP_CKSUM |
502                                                 TESTPMD_TX_OFFLOAD_SCTP_CKSUM)) ||
503                                 tso_segsz != 0)
504                                 printf("tx: m->l2_len=%d m->l3_len=%d "
505                                         "m->l4_len=%d\n",
506                                         m->l2_len, m->l3_len, m->l4_len);
507                         if ((tunnel == 1) &&
508                                 (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM))
509                                 printf("tx: m->outer_l2_len=%d m->outer_l3_len=%d\n",
510                                         m->outer_l2_len, m->outer_l3_len);
511                         if (tso_segsz != 0)
512                                 printf("tx: m->tso_segsz=%d\n", m->tso_segsz);
513                         printf("tx: flags=");
514                         for (j = 0; j < sizeof(tx_flags)/sizeof(*tx_flags); j++) {
515                                 name = rte_get_tx_ol_flag_name(tx_flags[j].flag);
516                                 if ((m->ol_flags & tx_flags[j].mask) ==
517                                         tx_flags[j].flag)
518                                         printf("%s ", name);
519                         }
520                         printf("\n");
521                 }
522         }
523         nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, pkts_burst, nb_rx);
524         fs->tx_packets += nb_tx;
525         fs->rx_bad_ip_csum += rx_bad_ip_csum;
526         fs->rx_bad_l4_csum += rx_bad_l4_csum;
527
528 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
529         fs->tx_burst_stats.pkt_burst_spread[nb_tx]++;
530 #endif
531         if (unlikely(nb_tx < nb_rx)) {
532                 fs->fwd_dropped += (nb_rx - nb_tx);
533                 do {
534                         rte_pktmbuf_free(pkts_burst[nb_tx]);
535                 } while (++nb_tx < nb_rx);
536         }
537 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
538         end_tsc = rte_rdtsc();
539         core_cycles = (end_tsc - start_tsc);
540         fs->core_cycles = (uint64_t) (fs->core_cycles + core_cycles);
541 #endif
542 }
543
544 struct fwd_engine csum_fwd_engine = {
545         .fwd_mode_name  = "csum",
546         .port_fwd_begin = NULL,
547         .port_fwd_end   = NULL,
548         .packet_fwd     = pkt_burst_checksum_forward,
549 };
550