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

udp: gso: do not drop small packets when PMTU reduces

Commit 4094871db1d6 ("udp: only do GSO if # of segs > 1") avoided GSO
for small packets. But the kernel currently dismisses GSO requests only
after checking MTU/PMTU on gso_size. This means any packets, regardless
of their payload sizes, could be dropped when PMTU becomes smaller than
requested gso_size. We encountered this issue in production and it
caused a reliability problem that new QUIC connection cannot be
established before PMTU cache expired, while non GSO sockets still
worked fine at the same time.

Ideally, do not check any GSO related constraints when payload size is
smaller than requested gso_size, and return EMSGSIZE instead of EINVAL
on MTU/PMTU check failure to be more specific on the error cause.

Fixes: 4094871db1d6 ("udp: only do GSO if # of segs > 1")
Signed-off-by: Yan Zhai <yan@cloudflare.com>
Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Yan Zhai and committed by
David S. Miller
235174b2 e0efe83e

+30 -4
+2 -2
net/ipv4/udp.c
··· 1141 1141 const int hlen = skb_network_header_len(skb) + 1142 1142 sizeof(struct udphdr); 1143 1143 1144 - if (hlen + cork->gso_size > cork->fragsize) { 1144 + if (hlen + min(datalen, cork->gso_size) > cork->fragsize) { 1145 1145 kfree_skb(skb); 1146 - return -EINVAL; 1146 + return -EMSGSIZE; 1147 1147 } 1148 1148 if (datalen > cork->gso_size * UDP_MAX_SEGMENTS) { 1149 1149 kfree_skb(skb);
+2 -2
net/ipv6/udp.c
··· 1389 1389 const int hlen = skb_network_header_len(skb) + 1390 1390 sizeof(struct udphdr); 1391 1391 1392 - if (hlen + cork->gso_size > cork->fragsize) { 1392 + if (hlen + min(datalen, cork->gso_size) > cork->fragsize) { 1393 1393 kfree_skb(skb); 1394 - return -EINVAL; 1394 + return -EMSGSIZE; 1395 1395 } 1396 1396 if (datalen > cork->gso_size * UDP_MAX_SEGMENTS) { 1397 1397 kfree_skb(skb);
+26
tools/testing/selftests/net/udpgso.c
··· 103 103 .r_num_mss = 1, 104 104 }, 105 105 { 106 + /* datalen <= MSS < gso_len: will fall back to no GSO */ 107 + .tlen = CONST_MSS_V4, 108 + .gso_len = CONST_MSS_V4 + 1, 109 + .r_num_mss = 0, 110 + .r_len_last = CONST_MSS_V4, 111 + }, 112 + { 113 + /* MSS < datalen < gso_len: fail */ 114 + .tlen = CONST_MSS_V4 + 1, 115 + .gso_len = CONST_MSS_V4 + 2, 116 + .tfail = true, 117 + }, 118 + { 106 119 /* send a single MSS + 1B */ 107 120 .tlen = CONST_MSS_V4 + 1, 108 121 .gso_len = CONST_MSS_V4, ··· 217 204 .tlen = CONST_MSS_V6, 218 205 .gso_len = CONST_MSS_V6, 219 206 .r_num_mss = 1, 207 + }, 208 + { 209 + /* datalen <= MSS < gso_len: will fall back to no GSO */ 210 + .tlen = CONST_MSS_V6, 211 + .gso_len = CONST_MSS_V6 + 1, 212 + .r_num_mss = 0, 213 + .r_len_last = CONST_MSS_V6, 214 + }, 215 + { 216 + /* MSS < datalen < gso_len: fail */ 217 + .tlen = CONST_MSS_V6 + 1, 218 + .gso_len = CONST_MSS_V6 + 2, 219 + .tfail = true 220 220 }, 221 221 { 222 222 /* send a single MSS + 1B */