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

r8169: work around RTL8125 UDP hw bug

It was reported that on RTL8125 network breaks under heavy UDP load,
e.g. torrent traffic ([0], from comment 27). Realtek confirmed a hw bug
and provided me with a test version of the r8125 driver including a
workaround. Tests confirmed that the workaround fixes the issue.
I modified the original version of the workaround to meet mainline
code style.

[0] https://bugzilla.kernel.org/show_bug.cgi?id=209839

v2:
- rebased to net
v3:
- make rtl_skb_is_udp() more robust and use skb_header_pointer()
to access the ip(v6) header
v4:
- remove dependency on ptp_classify.h
- replace magic number with offsetof(struct udphdr, len)

Fixes: f1bce4ad2f1c ("r8169: add support for RTL8125")
Tested-by: xplo <xplo.bn@gmail.com>
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Acked-by: Willem de Bruijn <willemb@google.com>
Link: https://lore.kernel.org/r/6e453d49-1801-e6de-d5f7-d7e6c7526c8f@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Heiner Kallweit and committed by
Jakub Kicinski
8d520b4d 01365633

+65 -6
+65 -6
drivers/net/ethernet/realtek/r8169_main.c
··· 4046 4046 return -EIO; 4047 4047 } 4048 4048 4049 - static bool rtl_test_hw_pad_bug(struct rtl8169_private *tp) 4049 + static bool rtl_skb_is_udp(struct sk_buff *skb) 4050 4050 { 4051 + int no = skb_network_offset(skb); 4052 + struct ipv6hdr *i6h, _i6h; 4053 + struct iphdr *ih, _ih; 4054 + 4055 + switch (vlan_get_protocol(skb)) { 4056 + case htons(ETH_P_IP): 4057 + ih = skb_header_pointer(skb, no, sizeof(_ih), &_ih); 4058 + return ih && ih->protocol == IPPROTO_UDP; 4059 + case htons(ETH_P_IPV6): 4060 + i6h = skb_header_pointer(skb, no, sizeof(_i6h), &_i6h); 4061 + return i6h && i6h->nexthdr == IPPROTO_UDP; 4062 + default: 4063 + return false; 4064 + } 4065 + } 4066 + 4067 + #define RTL_MIN_PATCH_LEN 47 4068 + 4069 + /* see rtl8125_get_patch_pad_len() in r8125 vendor driver */ 4070 + static unsigned int rtl8125_quirk_udp_padto(struct rtl8169_private *tp, 4071 + struct sk_buff *skb) 4072 + { 4073 + unsigned int padto = 0, len = skb->len; 4074 + 4075 + if (rtl_is_8125(tp) && len < 128 + RTL_MIN_PATCH_LEN && 4076 + rtl_skb_is_udp(skb) && skb_transport_header_was_set(skb)) { 4077 + unsigned int trans_data_len = skb_tail_pointer(skb) - 4078 + skb_transport_header(skb); 4079 + 4080 + if (trans_data_len >= offsetof(struct udphdr, len) && 4081 + trans_data_len < RTL_MIN_PATCH_LEN) { 4082 + u16 dest = ntohs(udp_hdr(skb)->dest); 4083 + 4084 + /* dest is a standard PTP port */ 4085 + if (dest == 319 || dest == 320) 4086 + padto = len + RTL_MIN_PATCH_LEN - trans_data_len; 4087 + } 4088 + 4089 + if (trans_data_len < sizeof(struct udphdr)) 4090 + padto = max_t(unsigned int, padto, 4091 + len + sizeof(struct udphdr) - trans_data_len); 4092 + } 4093 + 4094 + return padto; 4095 + } 4096 + 4097 + static unsigned int rtl_quirk_packet_padto(struct rtl8169_private *tp, 4098 + struct sk_buff *skb) 4099 + { 4100 + unsigned int padto; 4101 + 4102 + padto = rtl8125_quirk_udp_padto(tp, skb); 4103 + 4051 4104 switch (tp->mac_version) { 4052 4105 case RTL_GIGA_MAC_VER_34: 4053 4106 case RTL_GIGA_MAC_VER_60: 4054 4107 case RTL_GIGA_MAC_VER_61: 4055 4108 case RTL_GIGA_MAC_VER_63: 4056 - return true; 4109 + padto = max_t(unsigned int, padto, ETH_ZLEN); 4057 4110 default: 4058 - return false; 4111 + break; 4059 4112 } 4113 + 4114 + return padto; 4060 4115 } 4061 4116 4062 4117 static void rtl8169_tso_csum_v1(struct sk_buff *skb, u32 *opts) ··· 4183 4128 4184 4129 opts[1] |= transport_offset << TCPHO_SHIFT; 4185 4130 } else { 4186 - if (unlikely(skb->len < ETH_ZLEN && rtl_test_hw_pad_bug(tp))) 4187 - /* eth_skb_pad would free the skb on error */ 4188 - return !__skb_put_padto(skb, ETH_ZLEN, false); 4131 + unsigned int padto = rtl_quirk_packet_padto(tp, skb); 4132 + 4133 + /* skb_padto would free the skb on error */ 4134 + return !__skb_put_padto(skb, padto, false); 4189 4135 } 4190 4136 4191 4137 return true; ··· 4361 4305 } else if (skb->ip_summed == CHECKSUM_PARTIAL) { 4362 4306 /* work around hw bug on some chip versions */ 4363 4307 if (skb->len < ETH_ZLEN) 4308 + features &= ~NETIF_F_CSUM_MASK; 4309 + 4310 + if (rtl_quirk_packet_padto(tp, skb)) 4364 4311 features &= ~NETIF_F_CSUM_MASK; 4365 4312 4366 4313 if (transport_offset > TCPHO_MAX &&