]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
udp: gso: do not drop small packets when PMTU reduces
authorYan Zhai <yan@cloudflare.com>
Fri, 31 Jan 2025 08:31:39 +0000 (00:31 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Mar 2025 11:47:04 +0000 (12:47 +0100)
[ Upstream commit 235174b2bed88501fda689c113c55737f99332d8 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/ipv4/udp.c
net/ipv6/udp.c
tools/testing/selftests/net/udpgso.c

index 6ad25dc9710c1f6a0aa745d2b328f6780e69b9ed..d9583a5b8f35401794457d3be583354e1b3af7f3 100644 (file)
@@ -923,9 +923,9 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
                const int hlen = skb_network_header_len(skb) +
                                 sizeof(struct udphdr);
 
-               if (hlen + cork->gso_size > cork->fragsize) {
+               if (hlen + min_t(int, datalen, cork->gso_size) > cork->fragsize) {
                        kfree_skb(skb);
-                       return -EINVAL;
+                       return -EMSGSIZE;
                }
                if (datalen > cork->gso_size * UDP_MAX_SEGMENTS) {
                        kfree_skb(skb);
index 203a6d64d7e99d7968b7f55f58f5da2cebb0cede..a23780434edd3994b9702922e1125f6f645f86b1 100644 (file)
@@ -1210,9 +1210,9 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6,
                const int hlen = skb_network_header_len(skb) +
                                 sizeof(struct udphdr);
 
-               if (hlen + cork->gso_size > cork->fragsize) {
+               if (hlen + min_t(int, datalen, cork->gso_size) > cork->fragsize) {
                        kfree_skb(skb);
-                       return -EINVAL;
+                       return -EMSGSIZE;
                }
                if (datalen > cork->gso_size * UDP_MAX_SEGMENTS) {
                        kfree_skb(skb);
index 7badaf215de288fca3a1ad935b829c7425b32552..0e137182a4f40c22f9943a9ec0b9ab4734e45f1c 100644 (file)
@@ -94,6 +94,19 @@ struct testcase testcases_v4[] = {
                .gso_len = CONST_MSS_V4,
                .r_num_mss = 1,
        },
+       {
+               /* datalen <= MSS < gso_len: will fall back to no GSO */
+               .tlen = CONST_MSS_V4,
+               .gso_len = CONST_MSS_V4 + 1,
+               .r_num_mss = 0,
+               .r_len_last = CONST_MSS_V4,
+       },
+       {
+               /* MSS < datalen < gso_len: fail */
+               .tlen = CONST_MSS_V4 + 1,
+               .gso_len = CONST_MSS_V4 + 2,
+               .tfail = true,
+       },
        {
                /* send a single MSS + 1B */
                .tlen = CONST_MSS_V4 + 1,
@@ -197,6 +210,19 @@ struct testcase testcases_v6[] = {
                .gso_len = CONST_MSS_V6,
                .r_num_mss = 1,
        },
+       {
+               /* datalen <= MSS < gso_len: will fall back to no GSO */
+               .tlen = CONST_MSS_V6,
+               .gso_len = CONST_MSS_V6 + 1,
+               .r_num_mss = 0,
+               .r_len_last = CONST_MSS_V6,
+       },
+       {
+               /* MSS < datalen < gso_len: fail */
+               .tlen = CONST_MSS_V6 + 1,
+               .gso_len = CONST_MSS_V6 + 2,
+               .tfail = true
+       },
        {
                /* send a single MSS + 1B */
                .tlen = CONST_MSS_V6 + 1,