From b51c47536a9ef3d040e97e1239e28606dc7599ab Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Wed, 26 Nov 2014 16:04:54 +0100 Subject: [PATCH] app/testpmd: support TSO in checksum forward engine Add two new commands in testpmd: - tso set - tso show These commands can be used enable TSO when transmitting TCP packets in the csum forward engine. Ex: set fwd csum tx_checksum set ip hw 0 tso set 800 0 start Signed-off-by: Olivier Matz --- app/test-pmd/cmdline.c | 92 +++++++++++++++++++++++++++++++++++++++++ app/test-pmd/csumonly.c | 61 +++++++++++++++++++-------- app/test-pmd/testpmd.h | 1 + 3 files changed, 136 insertions(+), 18 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 722cd76720..2a8c2602be 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -329,6 +329,14 @@ static void cmd_help_long_parsed(void *parsed_result, "tx_checksum show (port_id)\n" " Display tx checksum offload configuration\n\n" + "tso set (segsize) (portid)\n" + " Enable TCP Segmentation Offload in csum forward" + " engine.\n" + " Please check the NIC datasheet for HW limits.\n\n" + + "tso show (portid)" + " Display the status of TCP Segmentation Offload.\n\n" + "set fwd (%s)\n" " Set packet forwarding mode.\n\n" @@ -2984,6 +2992,88 @@ cmdline_parse_inst_t cmd_tx_cksum_show = { }, }; +/* *** ENABLE HARDWARE SEGMENTATION IN TX PACKETS *** */ +struct cmd_tso_set_result { + cmdline_fixed_string_t tso; + cmdline_fixed_string_t mode; + uint16_t tso_segsz; + uint8_t port_id; +}; + +static void +cmd_tso_set_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_tso_set_result *res = parsed_result; + struct rte_eth_dev_info dev_info; + + if (port_id_is_invalid(res->port_id)) + return; + + if (!strcmp(res->mode, "set")) + ports[res->port_id].tso_segsz = res->tso_segsz; + + if (ports[res->port_id].tso_segsz == 0) + printf("TSO is disabled\n"); + else + printf("TSO segment size is %d\n", + ports[res->port_id].tso_segsz); + + /* display warnings if configuration is not supported by the NIC */ + rte_eth_dev_info_get(res->port_id, &dev_info); + if ((ports[res->port_id].tso_segsz != 0) && + (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_TCP_TSO) == 0) { + printf("Warning: TSO enabled but not " + "supported by port %d\n", res->port_id); + } +} + +cmdline_parse_token_string_t cmd_tso_set_tso = + TOKEN_STRING_INITIALIZER(struct cmd_tso_set_result, + tso, "tso"); +cmdline_parse_token_string_t cmd_tso_set_mode = + TOKEN_STRING_INITIALIZER(struct cmd_tso_set_result, + mode, "set"); +cmdline_parse_token_num_t cmd_tso_set_tso_segsz = + TOKEN_NUM_INITIALIZER(struct cmd_tso_set_result, + tso_segsz, UINT16); +cmdline_parse_token_num_t cmd_tso_set_portid = + TOKEN_NUM_INITIALIZER(struct cmd_tso_set_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_tso_set = { + .f = cmd_tso_set_parsed, + .data = NULL, + .help_str = "Set TSO segment size for csum engine (0 to disable): " + "tso set ", + .tokens = { + (void *)&cmd_tso_set_tso, + (void *)&cmd_tso_set_mode, + (void *)&cmd_tso_set_tso_segsz, + (void *)&cmd_tso_set_portid, + NULL, + }, +}; + +cmdline_parse_token_string_t cmd_tso_show_mode = + TOKEN_STRING_INITIALIZER(struct cmd_tso_set_result, + mode, "show"); + + +cmdline_parse_inst_t cmd_tso_show = { + .f = cmd_tso_set_parsed, + .data = NULL, + .help_str = "Show TSO segment size for csum engine: " + "tso show ", + .tokens = { + (void *)&cmd_tso_set_tso, + (void *)&cmd_tso_show_mode, + (void *)&cmd_tso_set_portid, + NULL, + }, +}; + /* *** ENABLE/DISABLE FLUSH ON RX STREAMS *** */ struct cmd_set_flush_rx { cmdline_fixed_string_t set; @@ -8660,6 +8750,8 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_tx_vlan_set_pvid, (cmdline_parse_inst_t *)&cmd_tx_cksum_set, (cmdline_parse_inst_t *)&cmd_tx_cksum_show, + (cmdline_parse_inst_t *)&cmd_tso_set, + (cmdline_parse_inst_t *)&cmd_tso_show, (cmdline_parse_inst_t *)&cmd_link_flow_control_set, (cmdline_parse_inst_t *)&cmd_link_flow_control_set_rx, (cmdline_parse_inst_t *)&cmd_link_flow_control_set_tx, diff --git a/app/test-pmd/csumonly.c b/app/test-pmd/csumonly.c index 2fe3727094..724bc5459e 100644 --- a/app/test-pmd/csumonly.c +++ b/app/test-pmd/csumonly.c @@ -88,12 +88,12 @@ #endif static uint16_t -get_psd_sum(void *l3_hdr, uint16_t ethertype) +get_psd_sum(void *l3_hdr, uint16_t ethertype, uint64_t ol_flags) { if (ethertype == _htons(ETHER_TYPE_IPv4)) - return rte_ipv4_phdr_cksum(l3_hdr); + return rte_ipv4_phdr_cksum(l3_hdr, ol_flags); else /* assume ethertype == ETHER_TYPE_IPv6 */ - return rte_ipv6_phdr_cksum(l3_hdr); + return rte_ipv6_phdr_cksum(l3_hdr, ol_flags); } static uint16_t @@ -108,14 +108,15 @@ get_udptcp_checksum(void *l3_hdr, void *l4_hdr, uint16_t ethertype) /* * Parse an ethernet header to fill the ethertype, l2_len, l3_len and * ipproto. This function is able to recognize IPv4/IPv6 with one optional vlan - * header. + * header. The l4_len argument is only set in case of TCP (useful for TSO). */ static void parse_ethernet(struct ether_hdr *eth_hdr, uint16_t *ethertype, uint16_t *l2_len, - uint16_t *l3_len, uint8_t *l4_proto) + uint16_t *l3_len, uint8_t *l4_proto, uint16_t *l4_len) { struct ipv4_hdr *ipv4_hdr; struct ipv6_hdr *ipv6_hdr; + struct tcp_hdr *tcp_hdr; *l2_len = sizeof(struct ether_hdr); *ethertype = eth_hdr->ether_type; @@ -143,6 +144,13 @@ parse_ethernet(struct ether_hdr *eth_hdr, uint16_t *ethertype, uint16_t *l2_len, *l4_proto = 0; break; } + + if (*l4_proto == IPPROTO_TCP) { + tcp_hdr = (struct tcp_hdr *)((char *)eth_hdr + + *l2_len + *l3_len); + *l4_len = (tcp_hdr->data_off & 0xf0) >> 2; + } else + *l4_len = 0; } /* modify the IPv4 or IPv4 source address of a packet */ @@ -164,7 +172,7 @@ change_ip_addresses(void *l3_hdr, uint16_t ethertype) * depending on the testpmd command line configuration */ static uint64_t process_inner_cksums(void *l3_hdr, uint16_t ethertype, uint16_t l3_len, - uint8_t l4_proto, uint16_t testpmd_ol_flags) + uint8_t l4_proto, uint16_t tso_segsz, uint16_t testpmd_ol_flags) { struct ipv4_hdr *ipv4_hdr = l3_hdr; struct udp_hdr *udp_hdr; @@ -176,11 +184,15 @@ process_inner_cksums(void *l3_hdr, uint16_t ethertype, uint16_t l3_len, ipv4_hdr = l3_hdr; ipv4_hdr->hdr_checksum = 0; - if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_IP_CKSUM) + if (tso_segsz != 0 && l4_proto == IPPROTO_TCP) { ol_flags |= PKT_TX_IP_CKSUM; - else - ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr); - + } else { + if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_IP_CKSUM) + ol_flags |= PKT_TX_IP_CKSUM; + else + ipv4_hdr->hdr_checksum = + rte_ipv4_cksum(ipv4_hdr); + } ol_flags |= PKT_TX_IPV4; } else if (ethertype == _htons(ETHER_TYPE_IPv6)) ol_flags |= PKT_TX_IPV6; @@ -195,7 +207,7 @@ process_inner_cksums(void *l3_hdr, uint16_t ethertype, uint16_t l3_len, if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_UDP_CKSUM) { ol_flags |= PKT_TX_UDP_CKSUM; udp_hdr->dgram_cksum = get_psd_sum(l3_hdr, - ethertype); + ethertype, ol_flags); } else { udp_hdr->dgram_cksum = get_udptcp_checksum(l3_hdr, udp_hdr, @@ -205,9 +217,12 @@ process_inner_cksums(void *l3_hdr, uint16_t ethertype, uint16_t l3_len, } else if (l4_proto == IPPROTO_TCP) { tcp_hdr = (struct tcp_hdr *)((char *)l3_hdr + l3_len); tcp_hdr->cksum = 0; - if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_TCP_CKSUM) { + if (tso_segsz != 0) { + ol_flags |= PKT_TX_TCP_SEG; + tcp_hdr->cksum = get_psd_sum(l3_hdr, ethertype, ol_flags); + } else if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_TCP_CKSUM) { ol_flags |= PKT_TX_TCP_CKSUM; - tcp_hdr->cksum = get_psd_sum(l3_hdr, ethertype); + tcp_hdr->cksum = get_psd_sum(l3_hdr, ethertype, ol_flags); } else { tcp_hdr->cksum = get_udptcp_checksum(l3_hdr, tcp_hdr, ethertype); @@ -275,6 +290,8 @@ process_outer_cksums(void *outer_l3_hdr, uint16_t outer_ethertype, * - modify the IPs in inner headers and in outer headers if any * - reprocess the checksum of all supported layers. This is done in SW * or HW, depending on testpmd command line configuration + * - if TSO is enabled in testpmd command line, also flag the mbuf for TCP + * segmentation offload (this implies HW TCP checksum) * Then transmit packets on the output port. * * (1) Supported packets are: @@ -305,7 +322,9 @@ pkt_burst_checksum_forward(struct fwd_stream *fs) uint16_t testpmd_ol_flags; uint8_t l4_proto; uint16_t ethertype = 0, outer_ethertype = 0; - uint16_t l2_len = 0, l3_len = 0, outer_l2_len = 0, outer_l3_len = 0; + uint16_t l2_len = 0, l3_len = 0, l4_len = 0; + uint16_t outer_l2_len = 0, outer_l3_len = 0; + uint16_t tso_segsz; int tunnel = 0; uint32_t rx_bad_ip_csum; uint32_t rx_bad_l4_csum; @@ -335,6 +354,7 @@ pkt_burst_checksum_forward(struct fwd_stream *fs) txp = &ports[fs->tx_port]; testpmd_ol_flags = txp->tx_ol_flags; + tso_segsz = txp->tso_segsz; for (i = 0; i < nb_rx; i++) { @@ -350,7 +370,8 @@ pkt_burst_checksum_forward(struct fwd_stream *fs) * and inner headers */ eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *); - parse_ethernet(eth_hdr, ðertype, &l2_len, &l3_len, &l4_proto); + parse_ethernet(eth_hdr, ðertype, &l2_len, &l3_len, + &l4_proto, &l4_len); l3_hdr = (char *)eth_hdr + l2_len; /* check if it's a supported tunnel (only vxlan for now) */ @@ -378,7 +399,7 @@ pkt_burst_checksum_forward(struct fwd_stream *fs) sizeof(struct vxlan_hdr)); parse_ethernet(eth_hdr, ðertype, &l2_len, - &l3_len, &l4_proto); + &l3_len, &l4_proto, &l4_len); l3_hdr = (char *)eth_hdr + l2_len; } } @@ -392,11 +413,12 @@ pkt_burst_checksum_forward(struct fwd_stream *fs) /* step 3: depending on user command line configuration, * recompute checksum either in software or flag the - * mbuf to offload the calculation to the NIC */ + * mbuf to offload the calculation to the NIC. If TSO + * is configured, prepare the mbuf for TCP segmentation. */ /* process checksums of inner headers first */ ol_flags |= process_inner_cksums(l3_hdr, ethertype, - l3_len, l4_proto, testpmd_ol_flags); + l3_len, l4_proto, tso_segsz, testpmd_ol_flags); /* Then process outer headers if any. Note that the software * checksum will be wrong if one of the inner checksums is @@ -425,6 +447,7 @@ pkt_burst_checksum_forward(struct fwd_stream *fs) sizeof(struct udp_hdr) + sizeof(struct vxlan_hdr) + l2_len; m->l3_len = l3_len; + m->l4_len = l4_len; } } else { /* this is only useful if an offload flag is @@ -432,7 +455,9 @@ pkt_burst_checksum_forward(struct fwd_stream *fs) * case */ m->l2_len = l2_len; m->l3_len = l3_len; + m->l4_len = l4_len; } + m->tso_segsz = tso_segsz; m->ol_flags = ol_flags; } diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 83311fa16d..16c6fbf2a3 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -149,6 +149,7 @@ struct rte_port { struct fwd_stream *tx_stream; /**< Port TX stream, if unique */ unsigned int socket_id; /**< For NUMA support */ uint16_t tx_ol_flags;/**< TX Offload Flags (TESTPMD_TX_OFFLOAD...). */ + uint16_t tso_segsz; /**< MSS for segmentation offload. */ uint16_t tx_vlan_id; /**< Tag Id. in TX VLAN packets. */ void *fwd_ctx; /**< Forwarding mode context */ uint64_t rx_bad_ip_csum; /**< rx pkts with bad ip checksum */ -- 2.20.1