On Fri, Sep 25, 2026 at 12:45 AM Jeff Jo <[email protected]> wrote: > > A TCP packet can carry new data while acknowledging traffic in the > opposite direction. With overlapping traffic in both directions, a > delayed packet's acknowledgment can be older than one Linux has already > accepted, even when that packet fills a gap in the received data. > > Linux accepts the data, but tcp_ack() takes the old_ack path and skips > updating TS.Recent, the timestamp saved for outgoing acknowledgments. > The reply therefore echoes an older timestamp. If the sender uses this > echo to measure round-trip time after a long idle period, its estimate > includes the idle time and can reduce its sending rate. > > Update TS.Recent in old_ack using tcp_replace_ts_recent(), before SACK > processing can trigger a transmission. This reuses the existing timestamp > and sequence checks, including PAWS protection against old duplicate > packets. ACK validation already rejects old ACKs in SYN_RECV before this > path, so no additional state check is needed. > > Echoing the timestamp of the packet that fills the receive gap follows > RFC 7323 section 4.3. In a socket reproduction with 300 seconds idle, > controlled reordering and retransmission to exercise timestamp-based RTT > sampling, the sender's smoothed round-trip time was 37.5 seconds without > the fix and 15.5 ms with it. > > Fixes: 12fb3dd9dc3c ("tcp: call tcp_replace_ts_recent() from tcp_ack()") > Assisted-by: LLM sparse > Signed-off-by: Jeff Jo <[email protected]> > --- > net/ipv4/tcp_input.c | 6 ++++++ > 1 file changed, 6 insertions(+) > > diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c > index 92bc60716f33..99baf14afdfd 100644 > --- a/net/ipv4/tcp_input.c > +++ b/net/ipv4/tcp_input.c > @@ -4465,6 +4465,12 @@ static int tcp_ack(struct sock *sk, const struct > sk_buff *skb, int flag) > return 1; > > old_ack: > + /* An old ACK can carry new data. Update TS.Recent before SACK > + * processing can trigger a retransmission. > + */ > + if (flag & FLAG_UPDATE_TS_RECENT) > + tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq); > +
Reviewed-by: Eric Dumazet <[email protected]>

