Implement the generic part of TCP segmentation offload:
 - rte_mbuf modifications
 - testpmd for validation

To delegate the TCP segmentation to the hardware, the user has to:

- set the PKT_TX_TCP_SEG flag in mbuf->ol_flags (this flag implies
  PKT_TX_IP_CKSUM and PKT_TX_TCP_CKSUM)
- fill the mbuf->hw_offload information: l2_len, l3_len, l4_len, mss
- calculate the pseudo header checksum and set it in the TCP header,
  as required when doing hardware TCP checksum offload
- set the IP checksum to 0

The API is inspired from ixgbe hardware (the next commit adds the
support for ixgbe), but it seems generic enough to be used for other
hw/drivers in the future.

Signed-off-by: Olivier Matz <olivier.matz at 6wind.com>
---
 app/test-pmd/cmdline.c     | 45 +++++++++++++++++++++++++++++++++++++++++++++
 app/test-pmd/config.c      |  8 ++++++++
 app/test-pmd/csumonly.c    | 16 ++++++++++++++++
 app/test-pmd/testpmd.h     |  2 ++
 lib/librte_mbuf/rte_mbuf.h | 17 +++++++++++++++--
 5 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 8f155e9..c686901 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -2298,6 +2298,50 @@ cmdline_parse_inst_t cmd_tx_cksum_set = {
        },
 };

+/* *** ENABLE HARDWARE SEGMENTATION IN TX PACKETS *** */
+struct cmd_tso_set_result {
+       cmdline_fixed_string_t tso;
+       cmdline_fixed_string_t set;
+       uint16_t mss;
+       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;
+       tso_set(res->port_id, res->mss);
+}
+
+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_set =
+       TOKEN_STRING_INITIALIZER(struct cmd_tso_set_result,
+                               set, "set");
+cmdline_parse_token_num_t cmd_tso_set_mss =
+       TOKEN_NUM_INITIALIZER(struct cmd_tso_set_result,
+                               mss, 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 = "Enable hardware segmentation (set MSS to 0 to disable): "
+       "tso set <MSS> <PORT>",
+       .tokens = {
+               (void *)&cmd_tso_set_tso,
+               (void *)&cmd_tso_set_set,
+               (void *)&cmd_tso_set_mss,
+               (void *)&cmd_tso_set_portid,
+               NULL,
+       },
+};
+
 /* *** ENABLE/DISABLE FLUSH ON RX STREAMS *** */
 struct cmd_set_flush_rx {
        cmdline_fixed_string_t set;
@@ -5197,6 +5241,7 @@ cmdline_parse_ctx_t main_ctx[] = {
        (cmdline_parse_inst_t *)&cmd_tx_vlan_set,
        (cmdline_parse_inst_t *)&cmd_tx_vlan_reset,
        (cmdline_parse_inst_t *)&cmd_tx_cksum_set,
+       (cmdline_parse_inst_t *)&cmd_tso_set,
        (cmdline_parse_inst_t *)&cmd_link_flow_control_set,
        (cmdline_parse_inst_t *)&cmd_priority_flow_control_set,
        (cmdline_parse_inst_t *)&cmd_config_dcb,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 018a278..184efdb 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1545,6 +1545,14 @@ tx_cksum_set(portid_t port_id, uint32_t ol_flags)
 }

 void
+tso_set(portid_t port_id, uint16_t mss)
+{
+       if (port_id_is_invalid(port_id))
+               return;
+       ports[port_id].tx_mss = mss;
+}
+
+void
 fdir_add_signature_filter(portid_t port_id, uint8_t queue_id,
                          struct rte_fdir_filter *fdir_filter)
 {
diff --git a/app/test-pmd/csumonly.c b/app/test-pmd/csumonly.c
index e93d75f..9983618 100644
--- a/app/test-pmd/csumonly.c
+++ b/app/test-pmd/csumonly.c
@@ -220,10 +220,12 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
        uint32_t ol_flags;
        uint32_t pkt_ol_flags;
        uint32_t tx_ol_flags;
+       uint16_t tx_mss;
        uint16_t l4_proto;
        uint16_t eth_type;
        uint8_t  l2_len;
        uint8_t  l3_len;
+       uint8_t  l4_len;

        uint32_t rx_bad_ip_csum;
        uint32_t rx_bad_l4_csum;
@@ -255,6 +257,7 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)

        txp = &ports[fs->tx_port];
        tx_ol_flags = txp->tx_ol_flags;
+       tx_mss = txp->tx_mss;

        for (i = 0; i < nb_rx; i++) {

@@ -272,6 +275,7 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
                                ((uintptr_t)&eth_hdr->ether_type +
                                sizeof(struct vlan_hdr)));
                }
+               l4_len  = 0;

                /* Update the L3/L4 checksum error packet count  */
                rx_bad_ip_csum += ((pkt_ol_flags & PKT_RX_IP_CKSUM_BAD) != 0);
@@ -347,6 +351,11 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
                                        tcp_hdr->cksum = 
get_ipv4_udptcp_checksum(ipv4_hdr,
                                                        (uint16_t*)tcp_hdr);
                                }
+
+                               if (tx_mss != 0) {
+                                       ol_flags |= PKT_TX_TCP_SEG;
+                                       l4_len = (tcp_hdr->data_off & 0xf0) >> 
2;
+                               }
                        }
                        else if (l4_proto == IPPROTO_SCTP) {
                                sctp_hdr = (struct sctp_hdr*) 
(rte_pktmbuf_mtod(mb,
@@ -404,6 +413,11 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
                                        tcp_hdr->cksum = 
get_ipv6_udptcp_checksum(ipv6_hdr,
                                                        (uint16_t*)tcp_hdr);
                                }
+
+                               if (tx_mss != 0) {
+                                       ol_flags |= PKT_TX_TCP_SEG;
+                                       l4_len = (tcp_hdr->data_off & 0xf0) >> 
2;
+                               }
                        }
                        else if (l4_proto == IPPROTO_SCTP) {
                                sctp_hdr = (struct sctp_hdr*) 
(rte_pktmbuf_mtod(mb,
@@ -434,6 +448,8 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
                /* Combine the packet header write. VLAN is not consider here */
                mb->hw_offload.l2_len = l2_len;
                mb->hw_offload.l3_len = l3_len;
+               mb->hw_offload.l4_len = l4_len;
+               mb->hw_offload.mss = tx_mss;
                mb->ol_flags = ol_flags;
        }
        nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, pkts_burst, nb_rx);
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 68eccfa..b093911 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -135,6 +135,7 @@ struct rte_port {
        struct fwd_stream       *tx_stream; /**< Port TX stream, if unique */
        unsigned int            socket_id;  /**< For NUMA support */
        uint32_t                tx_ol_flags;/**< Offload Flags of TX packets. */
+       uint16_t                tx_mss;     /**< 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  */
@@ -486,6 +487,7 @@ void tx_vlan_reset(portid_t port_id);
 void set_qmap(portid_t port_id, uint8_t is_rx, uint16_t queue_id, uint8_t 
map_value);

 void tx_cksum_set(portid_t port_id, uint32_t ol_flags);
+void tso_set(portid_t port_id, uint16_t mss);

 void set_verbose_level(uint16_t vb_level);
 void set_tx_pkt_segments(unsigned *seg_lengths, unsigned nb_segs);
diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h
index 1e63511..c65ab81 100644
--- a/lib/librte_mbuf/rte_mbuf.h
+++ b/lib/librte_mbuf/rte_mbuf.h
@@ -96,6 +96,17 @@ extern "C" {
 #define PKT_TX_SCTP_CKSUM    0x00080000 /**< SCTP cksum of TX pkt. computed by 
NIC. */
 #define PKT_TX_UDP_CKSUM     0x000C0000 /**< UDP cksum of TX pkt. computed by 
NIC. */
 #define PKT_TX_IEEE1588_TMST 0x00100000 /**< TX IEEE1588 packet to timestamp. 
*/
+/**
+ * TCP segmentation offload. To enable this offload feature for a
+ * packet to be transmitted on hardware supporting TSO:
+ *  - set the PKT_TX_TCP_SEG flag in mbuf->ol_flags (this flag implies
+ *    PKT_TX_IP_CKSUM and PKT_TX_TCP_CKSUM)
+ *  - fill the mbuf->hw_offload information: l2_len, l3_len, l4_len, mss
+ *  - calculate the pseudo header checksum and set it in the TCP header,
+ *    as required when doing hardware TCP checksum offload
+ *  - set the IP checksum to 0
+ */
+#define PKT_TX_TCP_SEG       0x00200000

 /**
  * Get the name of a RX offload flag
@@ -144,6 +155,7 @@ static inline const char *rte_get_tx_ol_flag_name(uint32_t 
mask)
        case PKT_TX_SCTP_CKSUM: return "PKT_TX_SCTP_CKSUM";
        case PKT_TX_UDP_CKSUM: return "PKT_TX_UDP_CKSUM";
        case PKT_TX_IEEE1588_TMST: return "PKT_TX_IEEE1588_TMST";
+       case PKT_TX_TCP_SEG: return "PKT_TX_TCP_SEG";
        default: return NULL;
        }
 }
@@ -157,11 +169,12 @@ union rte_hw_offload {
 #define HW_OFFLOAD_L4_LEN_MASK 0xff
                uint32_t l2_len:7; /**< L2 (MAC) Header Length. */
                uint32_t l3_len:9; /**< L3 (IP) Header Length. */
-               uint32_t reserved:16;
+               uint32_t l4_len:8; /**< L4 (TCP/UDP) Header Length. */
+               uint32_t reserved:8;

                uint16_t vlan_tci;
                /**< VLAN Tag Control Identifier (CPU order). */
-               uint16_t reserved2;
+               uint16_t mss; /**< Maximum segment size. */
        };
 };

-- 
1.9.2

Reply via email to