|
@@ -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;
|
|
|
}
|