Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tcp: fix tcp_grow_skb() vs tstamps

I forgot to call tcp_skb_collapse_tstamp() in the
case we consume the second skb in write queue.

Neal suggested to create a common helper used by tcp_mtu_probe()
and tcp_grow_skb().

Fixes: 8ee602c63520 ("tcp: try to send bigger TSO packets")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Neal Cardwell <ncardwell@google.com>
Link: https://lore.kernel.org/r/20240425193450.411640-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eric Dumazet and committed by
Jakub Kicinski
1bede0a1 8880e266

+19 -14
+19 -14
net/ipv4/tcp_output.c
··· 2403 2403 return 0; 2404 2404 } 2405 2405 2406 + /* tcp_mtu_probe() and tcp_grow_skb() can both eat an skb (src) if 2407 + * all its payload was moved to another one (dst). 2408 + * Make sure to transfer tcp_flags, eor, and tstamp. 2409 + */ 2410 + static void tcp_eat_one_skb(struct sock *sk, 2411 + struct sk_buff *dst, 2412 + struct sk_buff *src) 2413 + { 2414 + TCP_SKB_CB(dst)->tcp_flags |= TCP_SKB_CB(src)->tcp_flags; 2415 + TCP_SKB_CB(dst)->eor = TCP_SKB_CB(src)->eor; 2416 + tcp_skb_collapse_tstamp(dst, src); 2417 + tcp_unlink_write_queue(src, sk); 2418 + tcp_wmem_free_skb(sk, src); 2419 + } 2420 + 2406 2421 /* Create a new MTU probe if we are ready. 2407 2422 * MTU probe is regularly attempting to increase the path MTU by 2408 2423 * deliberately sending larger packets. This discovers routing ··· 2523 2508 copy = min_t(int, skb->len, probe_size - len); 2524 2509 2525 2510 if (skb->len <= copy) { 2526 - /* We've eaten all the data from this skb. 2527 - * Throw it away. */ 2528 - TCP_SKB_CB(nskb)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags; 2529 - /* If this is the last SKB we copy and eor is set 2530 - * we need to propagate it to the new skb. 2531 - */ 2532 - TCP_SKB_CB(nskb)->eor = TCP_SKB_CB(skb)->eor; 2533 - tcp_skb_collapse_tstamp(nskb, skb); 2534 - tcp_unlink_write_queue(skb, sk); 2535 - tcp_wmem_free_skb(sk, skb); 2511 + tcp_eat_one_skb(sk, nskb, skb); 2536 2512 } else { 2537 2513 TCP_SKB_CB(nskb)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags & 2538 2514 ~(TCPHDR_FIN|TCPHDR_PSH); ··· 2711 2705 TCP_SKB_CB(next_skb)->seq += nlen; 2712 2706 2713 2707 if (!next_skb->len) { 2708 + /* In case FIN is set, we need to update end_seq */ 2714 2709 TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(next_skb)->end_seq; 2715 - TCP_SKB_CB(skb)->eor = TCP_SKB_CB(next_skb)->eor; 2716 - TCP_SKB_CB(skb)->tcp_flags |= TCP_SKB_CB(next_skb)->tcp_flags; 2717 - tcp_unlink_write_queue(next_skb, sk); 2718 - tcp_wmem_free_skb(sk, next_skb); 2710 + 2711 + tcp_eat_one_skb(sk, skb, next_skb); 2719 2712 } 2720 2713 } 2721 2714