Thread (5 messages) flat view 5 messages, 3 authors, 6d ago

Re: [PATCH net] tcp: initialize skb tx timestamp key before cloning

From: Eric Dumazet <edumazet@google.com>
Date: 2026-09-16 17:32:11

On Wed, Sep 16, 2026 at 10:23 AM David Wei [off-list ref] wrote:
On 2026-09-15 15:21, Eric Dumazet wrote:
quoted
On Tue, Sep 15, 2026 at 2:44 PM David Wei [off-list ref] wrote:
quoted
tcp_tx_timestamp() can select skbs from the rtx queue when all the
copied data has been sent in tcp_sendmsg_locked(). These skbs may be
already cloned, sharing the same shinfo, and handed off into the lower
tx layers.

tcp_tx_timestamp() sets tx_flags before setting skb tskey, racing with
any reader of both. It is possible to observe a valid tx_flag, but an
uninitialized tskey, which produces a large underflow after subtracting
the socket tskey.

Initialize skb tskey just before cloning while its shinfo is still
private. In tcp_tx_timestamp(), if it selects a cloned skb from the rtx
queue from the current tcp_sendmsg_locked() call (i.e. tx_flags and
txstamp_ack are 0) and the pre-init skb tskey matches, then tx_flags can
be set safely.

If the skb is not shared, coming from the write queue, then both tskey
and tx_flags are set, as before.

Fixes: 838eb9687691 ("tcp: tcp_tx_timestamp() must look at the rtx queue")
Assisted-by: LLM
Signed-off-by: David Wei <redacted>
pw-bot: cr

Sorry, this patch significantly hurts TCP performance and has many bugs.

I am sure your LLM can find a cheaper solution if you ask it to try harder.
This wasn't a yolo patch straight from an LLM, I tried quite hard to
reason with the bug and possible solutions. Maybe that's the problem -
the LLM is actually smarter than me :)
quoted
Some hints if needed:

First idea:

1) In tcp_tx_timestamp():

Keep shinfo / tcb accesses inside if (unlikely(tsflags)) so normal
traffic pays zero cost.
When falling back to skb = skb_rb_last(&sk->tcp_rtx_queue):
Verify TCP_SKB_CB(skb)->end_seq == tp->write_seq and
!tcp_has_tx_tstamp(skb). If either fails, do not touch skb.
Always write shinfo->tskey before sock_tx_timestamp(sk, sockc,
&shinfo->tx_flags) (and do the same in
bpf_sock_ops_enable_tx_tstamp()).
If pre-initializing shinfo->tskey in __tcp_transmit_skb() before
skb_clone(oskb, gfp_mask) (which already dirties shinfo->dataref in
the same cache line), guard it with !tcp_has_tx_tstamp(oskb) and use
TCP_SKB_CB(oskb)->end_seq - 1 (or seq + len - 1), without the bogus if
(shinfo->tskey != tskey) check in tcp_tx_timestamp().

2) In tcp_sendmsg_locked() (to preserve TX_SCHED / TX_SOFTWARE /
TX_HARDWARE on non-blocking partial sends):

Before calling tcp_push_one() / __tcp_push_pending_frames() at the end
of the loop (when skb->len >= size_goal), check if
(!sk_stream_memory_free(sk)) goto wait_for_space;.
At wait_for_space:, only call tcp_push() if timeo != 0 (since when
!timeo, sk_stream_wait_memory() immediately returns -EAGAIN and out:
will run tcp_tx_timestamp() before tcp_push()).

Second idea:

We can solve this cleanly and minimally by:

Reordering the writes in tcp_tx_timestamp() (and
bpf_sock_ops_enable_tx_tstamp()) so shinfo->tskey is written first via
WRITE_ONCE(), followed by publishing shinfo->tx_flags via
smp_store_release() in __sock_tx_timestamp().
I did think about this solution. I still don't fully grasp the memory
model and how READ/WRITE_ONCE and acquire-release works, so I wasn't
entirely sure if it was sufficient.

There could be situations where the skb is pushed early, and the lower
layers copy the shinfo e.g. GSO and COW. I don't have experience with
this code, so I find it hard to evaluate plausible paths that LLMs
suggest.

That's why I opted for the pre-set tskey before clone approach in this
RFC. I didn't think this would affect perf that badly, but yeah it is a
really hot function.

I believe I can make the GSO path work with acquire-release. For COW, do
you think it is actually relevant?
I am traveling today back to France, I will answer to your email in
~24 hours or so.

Thanks.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help