[PATCH 6.1 521/609] tcp: fix TFO max_qlen accounting across reuseport migration
Greg Kroah-Hartman <[email protected]>
| Newsgroups | dev.linux.lists.patches,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
6.1-stable review patch. If anyone has any objections, please let me know. ------------------ From: Jiayuan Chen <[email protected]> [ Upstream commit a0ab2ba83e35159d81cec830a92e885ecf8139be ] 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: 54b92e841937 ("tcp: Migrate TCP_ESTABLISHED/TCP_SYN_RECV sockets in accept queues.") Signed-off-by: Jiayuan Chen <[email protected]> Reviewed-by: Kuniyuki Iwashima <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]> Signed-off-by: Sasha Levin <[email protected]> --- net/ipv4/inet_connection_sock.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index a275ab5321a96..a0014311c520c 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -927,11 +927,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; } -- 2.53.0