tcp_measure_rcv_mss() recomputes tp->scaling_ratio from skb->len/skb->truesize and updates tp->window_clamp accordingly. Commit f5da7c45188e ("tcp: adjust rcvq_space after updating scaling ratio") made that update go through tcp_set_window_clamp(), which also shrinks tp->rcv_ssthresh, so every scaling_ratio dip cut the advertised window as well. Commit 0e125ecfe20c ("tcp: do not change rcv_ssthresh in tcp_measure_rcv_mss()") restored the plain window_clamp update. Add a packetdrill test for it, as suggested during the review of that fix. A fixed SO_RCVBUF pins sk_rcvbuf, so window_clamp can only move when scaling_ratio does, and the peer announces TCP_MIN_MSS so that icsk_ack.rcv_mss sits at its 88 byte floor. A single 89 byte segment is then measured, and 89 bytes of payload in a ~900 byte skb is far below the 50% that TCP_DEFAULT_SCALING_RATIO assumes, so scaling_ratio and window_clamp drop sharply. Nothing puts the socket under memory pressure, so rcv_ssthresh must not move at all. The test checks the state it depends on instead of assuming it. tcp_measure_rcv_mss() only measures segments of at least rcv_mss bytes, so rcv_mss is checked to be 88 before the segment is sent. It is checked to be 89 afterwards, because rcv_mss is only updated by the same code that recomputes scaling_ratio, so this proves the segment was measured. rcv_ssthresh is compared against the value it had right after the handshake, not against a fixed number, so the test does not depend on the receive buffer size or on how the architecture accounts skb memory. Verified on net-next, where it passes for ipv4, ipv6 and ipv4-mapped-ipv6, and on the same tree with the fix reverted, where it fails in all three modes with rcv_ssthresh cut from 260684 to 51200. Signed-off-by: Nathan Gao --- .../tcp_rcv_ssthresh_scaling_ratio.pkt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tools/testing/selftests/net/packetdrill/tcp_rcv_ssthresh_scaling_ratio.pkt diff --git a/tools/testing/selftests/net/packetdrill/tcp_rcv_ssthresh_scaling_ratio.pkt b/tools/testing/selftests/net/packetdrill/tcp_rcv_ssthresh_scaling_ratio.pkt new file mode 100644 index 0000000000000..f99ecb8a224f7 --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_rcv_ssthresh_scaling_ratio.pkt @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +// tcp_measure_rcv_mss() lowers tp->window_clamp when a segment carries less +// payload per byte of memory than the ratio it had assumed. Test that it does +// not lower tp->rcv_ssthresh as well: rcv_ssthresh is cut back under +// memory pressure, and it only grows back slowly, through tcp_grow_window(). + +--mss=1000 + +`./defaults.sh` + +// A fixed SO_RCVBUF keeps sk_rcvbuf from moving, so window_clamp can only +// change when the ratio does. The MSS of 88 (TCP_MIN_MSS) is the smallest +// segment size TCP will measure, and window scaling lets rcv_ssthresh start +// well above 64 KB. The window the peer announces does not matter here, this +// side never sends data. + +0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 setsockopt(3, SOL_SOCKET, SO_RCVBUF, [262144], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + + +0 < S 0:0(0) win 65535 + +0 > S. 0:0(0) ack 1 <...> + +.1 < . 1:1(0) ack 1 win 65535 + + +0 accept(3, ..., ...) = 4 + +// Only segments of at least rcv_mss bytes are measured. Note it down, along +// with the rcv_ssthresh the connection starts with. + +0 %{ +assert tcpi_rcv_mss == 88, tcpi_rcv_mss +ssthresh_0 = tcpi_rcv_ssthresh +}% + +// One segment, one byte above that threshold. 89 bytes of payload sit in an +// skb of about 900 bytes, far below the 50% ratio TCP assumes by default, so +// window_clamp drops sharply. + +0 < P. 1:90(89) ack 1 win 65535 + +0 > . 1:1(0) ack 90 + +// rcv_mss is only updated by the code that recomputes the ratio, so 89 here +// means the segment was measured. rcv_ssthresh must have been left alone. + +0 %{ +assert tcpi_rcv_mss == 89, tcpi_rcv_mss +assert tcpi_rcv_ssthresh >= ssthresh_0, (tcpi_rcv_ssthresh, ssthresh_0) +}% base-commit: 4982d3552a3bf94de503acf93433277d08421de6 -- 2.50.1