Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

r8169: fix offloaded tx checksum for small packets.

8168evl offloaded checksums are wrong since commit
e5195c1f31f399289347e043d6abf3ffa80f0005 ("r8169: fix 8168evl frame padding.")
pads small packets to 60 bytes (without ethernet checksum). Typical symptoms
appear as UDP checksums which are wrong by the count of added bytes.

It isn't worth compensating. Let the driver checksum.

Due to the skb length changes, TSO code is moved before the Tx descriptor gets
written.

Signed-off-by: Francois Romieu <romieu@fr.zoreil.com>
Tested-by: Holger Hoffstätte <holger.hoffstaette@googlemail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

françois romieu and committed by
David S. Miller
b423e9ae 318debd8

+27 -14
+27 -14
drivers/net/ethernet/realtek/r8169.c
··· 5856 5856 return -EIO; 5857 5857 } 5858 5858 5859 - static inline void rtl8169_tso_csum(struct rtl8169_private *tp, 5859 + static bool rtl_skb_pad(struct sk_buff *skb) 5860 + { 5861 + if (skb_padto(skb, ETH_ZLEN)) 5862 + return false; 5863 + skb_put(skb, ETH_ZLEN - skb->len); 5864 + return true; 5865 + } 5866 + 5867 + static bool rtl_test_hw_pad_bug(struct rtl8169_private *tp, struct sk_buff *skb) 5868 + { 5869 + return skb->len < ETH_ZLEN && tp->mac_version == RTL_GIGA_MAC_VER_34; 5870 + } 5871 + 5872 + static inline bool rtl8169_tso_csum(struct rtl8169_private *tp, 5860 5873 struct sk_buff *skb, u32 *opts) 5861 5874 { 5862 5875 const struct rtl_tx_desc_info *info = tx_desc_info + tp->txd_version; ··· 5882 5869 } else if (skb->ip_summed == CHECKSUM_PARTIAL) { 5883 5870 const struct iphdr *ip = ip_hdr(skb); 5884 5871 5872 + if (unlikely(rtl_test_hw_pad_bug(tp, skb))) 5873 + return skb_checksum_help(skb) == 0 && rtl_skb_pad(skb); 5874 + 5885 5875 if (ip->protocol == IPPROTO_TCP) 5886 5876 opts[offset] |= info->checksum.tcp; 5887 5877 else if (ip->protocol == IPPROTO_UDP) 5888 5878 opts[offset] |= info->checksum.udp; 5889 5879 else 5890 5880 WARN_ON_ONCE(1); 5881 + } else { 5882 + if (unlikely(rtl_test_hw_pad_bug(tp, skb))) 5883 + return rtl_skb_pad(skb); 5891 5884 } 5885 + return true; 5892 5886 } 5893 5887 5894 5888 static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, ··· 5916 5896 goto err_stop_0; 5917 5897 } 5918 5898 5919 - /* 8168evl does not automatically pad to minimum length. */ 5920 - if (unlikely(tp->mac_version == RTL_GIGA_MAC_VER_34 && 5921 - skb->len < ETH_ZLEN)) { 5922 - if (skb_padto(skb, ETH_ZLEN)) 5923 - goto err_update_stats; 5924 - skb_put(skb, ETH_ZLEN - skb->len); 5925 - } 5926 - 5927 5899 if (unlikely(le32_to_cpu(txd->opts1) & DescOwn)) 5928 5900 goto err_stop_0; 5901 + 5902 + opts[1] = cpu_to_le32(rtl8169_tx_vlan_tag(skb)); 5903 + opts[0] = DescOwn; 5904 + 5905 + if (!rtl8169_tso_csum(tp, skb, opts)) 5906 + goto err_update_stats; 5929 5907 5930 5908 len = skb_headlen(skb); 5931 5909 mapping = dma_map_single(d, skb->data, len, DMA_TO_DEVICE); ··· 5935 5917 5936 5918 tp->tx_skb[entry].len = len; 5937 5919 txd->addr = cpu_to_le64(mapping); 5938 - 5939 - opts[1] = cpu_to_le32(rtl8169_tx_vlan_tag(skb)); 5940 - opts[0] = DescOwn; 5941 - 5942 - rtl8169_tso_csum(tp, skb, opts); 5943 5920 5944 5921 frags = rtl8169_xmit_frags(tp, skb, opts); 5945 5922 if (frags < 0)