ibmveth: Set CHECKSUM_PARTIAL if NULL TCP CSUM.
authorDavid Wilder <dwilder@us.ibm.com>
Tue, 22 Jun 2021 21:52:15 +0000 (14:52 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 23 Jun 2021 19:54:11 +0000 (12:54 -0700)
TCP checksums on received packets may be set to NULL by the sender if CSO
is enabled. The hypervisor flags these packets as check-sum-ok and the
skb is then flagged CHECKSUM_UNNECESSARY. If these packets are then
forwarded the sender will not request CSO due to the CHECKSUM_UNNECESSARY
flag. The result is a TCP packet sent with a bad checksum. This change
sets up CHECKSUM_PARTIAL on these packets causing the sender to correctly
request CSUM offload.

Signed-off-by: David Wilder <dwilder@us.ibm.com>
Reviewed-by: Pradeep Satyanarayana <pradeeps@linux.vnet.ibm.com>
Tested-by: Cristobal Forno <cforno12@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ibm/ibmveth.c

index bc67a7e..737ba85 100644 (file)
@@ -1285,36 +1285,41 @@ static void ibmveth_rx_csum_helper(struct sk_buff *skb,
                iph_proto = iph6->nexthdr;
        }
 
-       /* In OVS environment, when a flow is not cached, specifically for a
-        * new TCP connection, the first packet information is passed up
+       /* When CSO is enabled the TCP checksum may have be set to NULL by
+        * the sender given that we zeroed out TCP checksum field in
+        * transmit path (refer ibmveth_start_xmit routine). In this case set
+        * up CHECKSUM_PARTIAL. If the packet is forwarded, the checksum will
+        * then be recalculated by the destination NIC (CSO must be enabled
+        * on the destination NIC).
+        *
+        * In an OVS environment, when a flow is not cached, specifically for a
+        * new TCP connection, the first packet information is passed up to
         * the user space for finding a flow. During this process, OVS computes
         * checksum on the first packet when CHECKSUM_PARTIAL flag is set.
         *
-        * Given that we zeroed out TCP checksum field in transmit path
-        * (refer ibmveth_start_xmit routine) as we set "no checksum bit",
-        * OVS computed checksum will be incorrect w/o TCP pseudo checksum
-        * in the packet. This leads to OVS dropping the packet and hence
-        * TCP retransmissions are seen.
-        *
-        * So, re-compute TCP pseudo header checksum.
+        * So, re-compute TCP pseudo header checksum when configured for
+        * trunk mode.
         */
-       if (iph_proto == IPPROTO_TCP && adapter->is_active_trunk) {
+       if (iph_proto == IPPROTO_TCP) {
                struct tcphdr *tcph = (struct tcphdr *)(skb->data + iphlen);
-
-               tcphdrlen = skb->len - iphlen;
-
-               /* Recompute TCP pseudo header checksum */
-               if (skb_proto == ETH_P_IP)
-                       tcph->check = ~csum_tcpudp_magic(iph->saddr,
+               if (tcph->check == 0x0000) {
+                       /* Recompute TCP pseudo header checksum  */
+                       if (adapter->is_active_trunk) {
+                               tcphdrlen = skb->len - iphlen;
+                               if (skb_proto == ETH_P_IP)
+                                       tcph->check =
+                                        ~csum_tcpudp_magic(iph->saddr,
                                        iph->daddr, tcphdrlen, iph_proto, 0);
-               else if (skb_proto == ETH_P_IPV6)
-                       tcph->check = ~csum_ipv6_magic(&iph6->saddr,
+                               else if (skb_proto == ETH_P_IPV6)
+                                       tcph->check =
+                                        ~csum_ipv6_magic(&iph6->saddr,
                                        &iph6->daddr, tcphdrlen, iph_proto, 0);
-
-               /* Setup SKB fields for checksum offload */
-               skb_partial_csum_set(skb, iphlen,
-                                    offsetof(struct tcphdr, check));
-               skb_reset_network_header(skb);
+                       }
+                       /* Setup SKB fields for checksum offload */
+                       skb_partial_csum_set(skb, iphlen,
+                                            offsetof(struct tcphdr, check));
+                       skb_reset_network_header(skb);
+               }
        }
 }