4343#include <linux/ip.h>
4444#include <linux/ipv6.h>
4545#include <linux/indirect_call_wrapper.h>
46+ #include <net/ipv6.h>
4647
4748#include "mlx4_en.h"
4849
@@ -634,19 +635,28 @@ static int get_real_size(const struct sk_buff *skb,
634635 struct net_device * dev ,
635636 int * lso_header_size ,
636637 bool * inline_ok ,
637- void * * pfrag )
638+ void * * pfrag ,
639+ int * hopbyhop )
638640{
639641 struct mlx4_en_priv * priv = netdev_priv (dev );
640642 int real_size ;
641643
642644 if (shinfo -> gso_size ) {
643645 * inline_ok = false;
644- if (skb -> encapsulation )
646+ * hopbyhop = 0 ;
647+ if (skb -> encapsulation ) {
645648 * lso_header_size = skb_inner_tcp_all_headers (skb );
646- else
649+ } else {
650+ /* Detects large IPV6 TCP packets and prepares for removal of
651+ * HBH header that has been pushed by ip6_xmit(),
652+ * mainly so that tcpdump can dissect them.
653+ */
654+ if (ipv6_has_hopopt_jumbo (skb ))
655+ * hopbyhop = sizeof (struct hop_jumbo_hdr );
647656 * lso_header_size = skb_tcp_all_headers (skb );
657+ }
648658 real_size = CTRL_SIZE + shinfo -> nr_frags * DS_SIZE +
649- ALIGN (* lso_header_size + 4 , DS_SIZE );
659+ ALIGN (* lso_header_size - * hopbyhop + 4 , DS_SIZE );
650660 if (unlikely (* lso_header_size != skb_headlen (skb ))) {
651661 /* We add a segment for the skb linear buffer only if
652662 * it contains data */
@@ -873,6 +883,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
873883 int desc_size ;
874884 int real_size ;
875885 u32 index , bf_index ;
886+ struct ipv6hdr * h6 ;
876887 __be32 op_own ;
877888 int lso_header_size ;
878889 void * fragptr = NULL ;
@@ -881,6 +892,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
881892 bool stop_queue ;
882893 bool inline_ok ;
883894 u8 data_offset ;
895+ int hopbyhop ;
884896 bool bf_ok ;
885897
886898 tx_ind = skb_get_queue_mapping (skb );
@@ -890,7 +902,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
890902 goto tx_drop ;
891903
892904 real_size = get_real_size (skb , shinfo , dev , & lso_header_size ,
893- & inline_ok , & fragptr );
905+ & inline_ok , & fragptr , & hopbyhop );
894906 if (unlikely (!real_size ))
895907 goto tx_drop_count ;
896908
@@ -943,7 +955,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
943955 data = & tx_desc -> data ;
944956 data_offset = offsetof(struct mlx4_en_tx_desc , data );
945957 } else {
946- int lso_align = ALIGN (lso_header_size + 4 , DS_SIZE );
958+ int lso_align = ALIGN (lso_header_size - hopbyhop + 4 , DS_SIZE );
947959
948960 data = (void * )& tx_desc -> lso + lso_align ;
949961 data_offset = offsetof(struct mlx4_en_tx_desc , lso ) + lso_align ;
@@ -1008,14 +1020,31 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
10081020 ((ring -> prod & ring -> size ) ?
10091021 cpu_to_be32 (MLX4_EN_BIT_DESC_OWN ) : 0 );
10101022
1023+ lso_header_size -= hopbyhop ;
10111024 /* Fill in the LSO prefix */
10121025 tx_desc -> lso .mss_hdr_size = cpu_to_be32 (
10131026 shinfo -> gso_size << 16 | lso_header_size );
10141027
1015- /* Copy headers;
1016- * note that we already verified that it is linear */
1017- memcpy (tx_desc -> lso .header , skb -> data , lso_header_size );
10181028
1029+ if (unlikely (hopbyhop )) {
1030+ /* remove the HBH header.
1031+ * Layout: [Ethernet header][IPv6 header][HBH][TCP header]
1032+ */
1033+ memcpy (tx_desc -> lso .header , skb -> data , ETH_HLEN + sizeof (* h6 ));
1034+ h6 = (struct ipv6hdr * )((char * )tx_desc -> lso .header + ETH_HLEN );
1035+ h6 -> nexthdr = IPPROTO_TCP ;
1036+ /* Copy the TCP header after the IPv6 one */
1037+ memcpy (h6 + 1 ,
1038+ skb -> data + ETH_HLEN + sizeof (* h6 ) +
1039+ sizeof (struct hop_jumbo_hdr ),
1040+ tcp_hdrlen (skb ));
1041+ /* Leave ipv6 payload_len set to 0, as LSO v2 specs request. */
1042+ } else {
1043+ /* Copy headers;
1044+ * note that we already verified that it is linear
1045+ */
1046+ memcpy (tx_desc -> lso .header , skb -> data , lso_header_size );
1047+ }
10191048 ring -> tso_packets ++ ;
10201049
10211050 i = shinfo -> gso_segs ;
0 commit comments