When tcp_collapse() rebuilds skbs under memory pressure, the copy process doesn't include the right tstamp and hwtstamp from the old skb. And memcpy(nskb->cb, skb->cb, ...) copies has_rxtstamp, but nskb->tstamp and hwtstamps are left at zero, so tcp_recv_timestamp() ends up emitting no cmsg at all. In net timestamping case, if such an skb happens to be the last one consumed in a recvmsg() call, the application receives no RX timestamp for that call. Fix this by copying both tstamp and hwtstamp of the last skb to the new skb, matching tcp_try_coalesce()/tcp_add_backlog(). Note that the has_rxtstamp flag can still be inherited through the cb memcpy from an skb that contributes no bytes (fully covered skb left in the ofo tree by the tcp_ooo_try_coalesce() -> coalesce_done path), so set TCP_SKB_CB(nskb)->has_rxtstamp to false which makes the new block the only place setting it. Fixes: 98aaa913b4ed ("tcp: Extend SOF_TIMESTAMPING_RX_SOFTWARE to TCP recvmsg") Signed-off-by: Jason Xing --- V3 Link: https://lore.kernel.org/all/20260917132742.87117-1-kerneljasonxing@gmail.com/ 1. revise the commit message with more accurate description (Eric) 2. fix a corner case (where a skb can contribute no bytes if OOO happens) spotted by AI and Eric V2 Link: https://lore.kernel.org/all/20260916124153.43457-1-kerneljasonxing@gmail.com/ 1. add one sentence in commit log from Eric's reply 2. use the same semantic as tcp_add_backlog which is to sync the last skb (Eric) --- net/ipv4/tcp_input.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 92bc60716f33..f58954ddecee 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5834,6 +5834,7 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root, break; memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); + TCP_SKB_CB(nskb)->has_rxtstamp = false; skb_copy_decrypted(nskb, skb); TCP_SKB_CB(nskb)->seq = TCP_SKB_CB(nskb)->end_seq = start; if (list) @@ -5854,6 +5855,12 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root, if (skb_copy_bits(skb, offset, skb_put(nskb, size), size)) BUG(); TCP_SKB_CB(nskb)->end_seq += size; + if (TCP_SKB_CB(skb)->has_rxtstamp) { + TCP_SKB_CB(nskb)->has_rxtstamp = true; + nskb->tstamp = skb->tstamp; + skb_hwtstamps(nskb)->hwtstamp = + skb_hwtstamps(skb)->hwtstamp; + } copy -= size; start += size; } -- 2.43.7