Commit 235174b2 authored by Yan Zhai's avatar Yan Zhai Committed by David S. Miller
Browse files

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



Commit 4094871d ("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: 4094871d ("udp: only do GSO if # of segs > 1")
Signed-off-by: default avatarYan Zhai <yan@cloudflare.com>
Suggested-by: default avatarWillem de Bruijn <willemdebruijn.kernel@gmail.com>
Reviewed-by: default avatarWillem de Bruijn <willemb@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e0efe83e
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1141,9 +1141,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(datalen, cork->gso_size) > cork->fragsize) {
			kfree_skb(skb);
			return -EINVAL;
			return -EMSGSIZE;
		}
		if (datalen > cork->gso_size * UDP_MAX_SEGMENTS) {
			kfree_skb(skb);
+2 −2
Original line number Diff line number Diff line
@@ -1389,9 +1389,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(datalen, cork->gso_size) > cork->fragsize) {
			kfree_skb(skb);
			return -EINVAL;
			return -EMSGSIZE;
		}
		if (datalen > cork->gso_size * UDP_MAX_SEGMENTS) {
			kfree_skb(skb);
+26 −0
Original line number Diff line number Diff line
@@ -102,6 +102,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,
@@ -205,6 +218,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,