mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 21:39:35 -04:00
tcp: fix TFO max_qlen accounting across reuseport migration
A listener's TCP_FASTOPEN max_qlen stops being accurate and lets through
far more pending Fast Open requests than it was configured for.
This only shows up with SO_REUSEPORT listener migration, where closing a
listener hands its still-pending TFO children over to a surviving one.
fastopenq.qlen is charged in tcp_fastopen_create_child() when the child
is created and uncharged in reqsk_fastopen_remove() when the handshake
completes. The uncharge follows rsk_listener of the request the child
points at, and inet_reqsk_clone() has repointed the child at a new
request owned by the new listener, so the ++ and the -- land on two
different sockets. The new listener's qlen drifts negative and its
limit no longer binds.
Charge the new listener during migration, like reqsk_queue_migrated()
already does for queue->young and queue->qlen.
Fixes: 54b92e8419 ("tcp: Migrate TCP_ESTABLISHED/TCP_SYN_RECV sockets in accept queues.")
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260803061739.134737-1-jiayuan.chen@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
committed by
Jakub Kicinski
parent
9113e98eb7
commit
a0ab2ba83e
@@ -943,11 +943,23 @@ static struct request_sock *inet_reqsk_clone(struct request_sock *req,
|
||||
|
||||
nreq->rsk_listener = sk;
|
||||
|
||||
/* We need not acquire fastopenq->lock
|
||||
* because the child socket is locked in inet_csk_listen_stop().
|
||||
*/
|
||||
if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener)
|
||||
if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener) {
|
||||
struct fastopen_queue *fastopenq;
|
||||
|
||||
/* reqsk_fastopen_remove() will uncharge nreq->rsk_listener,
|
||||
* that is @sk, so charge it here. Unlike the listener
|
||||
* being closed, @sk is live and needs its lock.
|
||||
*/
|
||||
fastopenq = &inet_csk(sk)->icsk_accept_queue.fastopenq;
|
||||
spin_lock_bh(&fastopenq->lock);
|
||||
fastopenq->qlen++;
|
||||
spin_unlock_bh(&fastopenq->lock);
|
||||
|
||||
/* We need not acquire fastopenq->lock
|
||||
* because the child socket is locked in inet_csk_listen_stop().
|
||||
*/
|
||||
rcu_assign_pointer(tcp_sk(nreq->sk)->fastopen_rsk, nreq);
|
||||
}
|
||||
|
||||
return nreq;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user