net/mlx5e: When RXFCS is set, add FCS data into checksum calculation
authorEran Ben Elisha <eranbe@mellanox.com>
Tue, 1 May 2018 13:25:07 +0000 (16:25 +0300)
committerSaeed Mahameed <saeedm@mellanox.com>
Thu, 24 May 2018 21:40:39 +0000 (14:40 -0700)
When RXFCS feature is enabled, the HW do not strip the FCS data,
however it is not present in the checksum calculated by the HW.

Fix that by manually calculating the FCS checksum and adding it to the SKB
checksum field.

Add helper function to find the FCS data for all SKB forms (linear,
one fragment or more).

Fixes: 102722fc6832 ("net/mlx5e: Add support for RXFCS feature flag")
Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
drivers/net/ethernet/mellanox/mlx5/core/en_rx.c

index 176645762e491687dae7ae0fb28da282fa29c49f..1ff0b0e9380410510c094ea1cb5699545ef9fc4f 100644 (file)
@@ -615,6 +615,45 @@ static inline bool is_last_ethertype_ip(struct sk_buff *skb, int *network_depth)
        return (ethertype == htons(ETH_P_IP) || ethertype == htons(ETH_P_IPV6));
 }
 
+static __be32 mlx5e_get_fcs(struct sk_buff *skb)
+{
+       int last_frag_sz, bytes_in_prev, nr_frags;
+       u8 *fcs_p1, *fcs_p2;
+       skb_frag_t *last_frag;
+       __be32 fcs_bytes;
+
+       if (!skb_is_nonlinear(skb))
+               return *(__be32 *)(skb->data + skb->len - ETH_FCS_LEN);
+
+       nr_frags = skb_shinfo(skb)->nr_frags;
+       last_frag = &skb_shinfo(skb)->frags[nr_frags - 1];
+       last_frag_sz = skb_frag_size(last_frag);
+
+       /* If all FCS data is in last frag */
+       if (last_frag_sz >= ETH_FCS_LEN)
+               return *(__be32 *)(skb_frag_address(last_frag) +
+                                  last_frag_sz - ETH_FCS_LEN);
+
+       fcs_p2 = (u8 *)skb_frag_address(last_frag);
+       bytes_in_prev = ETH_FCS_LEN - last_frag_sz;
+
+       /* Find where the other part of the FCS is - Linear or another frag */
+       if (nr_frags == 1) {
+               fcs_p1 = skb_tail_pointer(skb);
+       } else {
+               skb_frag_t *prev_frag = &skb_shinfo(skb)->frags[nr_frags - 2];
+
+               fcs_p1 = skb_frag_address(prev_frag) +
+                           skb_frag_size(prev_frag);
+       }
+       fcs_p1 -= bytes_in_prev;
+
+       memcpy(&fcs_bytes, fcs_p1, bytes_in_prev);
+       memcpy(((u8 *)&fcs_bytes) + bytes_in_prev, fcs_p2, last_frag_sz);
+
+       return fcs_bytes;
+}
+
 static inline void mlx5e_handle_csum(struct net_device *netdev,
                                     struct mlx5_cqe64 *cqe,
                                     struct mlx5e_rq *rq,
@@ -643,6 +682,9 @@ static inline void mlx5e_handle_csum(struct net_device *netdev,
                        skb->csum = csum_partial(skb->data + ETH_HLEN,
                                                 network_depth - ETH_HLEN,
                                                 skb->csum);
+               if (unlikely(netdev->features & NETIF_F_RXFCS))
+                       skb->csum = csum_add(skb->csum,
+                                            (__force __wsum)mlx5e_get_fcs(skb));
                rq->stats.csum_complete++;
                return;
        }