tcp: set TCP_SYNCNT locklessly
authorEric Dumazet <edumazet@google.com>
Fri, 4 Aug 2023 14:46:11 +0000 (14:46 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 6 Aug 2023 07:24:55 +0000 (08:24 +0100)
icsk->icsk_syn_retries can safely be set without locking the socket.

We have to add READ_ONCE() annotations in tcp_fastopen_synack_timer()
and tcp_write_timeout().

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/tcp.c
net/ipv4/tcp_timer.c

index aca5620..bcbb33a 100644 (file)
@@ -3291,9 +3291,7 @@ int tcp_sock_set_syncnt(struct sock *sk, int val)
        if (val < 1 || val > MAX_TCP_SYNCNT)
                return -EINVAL;
 
-       lock_sock(sk);
        WRITE_ONCE(inet_csk(sk)->icsk_syn_retries, val);
-       release_sock(sk);
        return 0;
 }
 EXPORT_SYMBOL(tcp_sock_set_syncnt);
@@ -3462,6 +3460,12 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname,
        if (copy_from_sockptr(&val, optval, sizeof(val)))
                return -EFAULT;
 
+       /* Handle options that can be set without locking the socket. */
+       switch (optname) {
+       case TCP_SYNCNT:
+               return tcp_sock_set_syncnt(sk, val);
+       }
+
        sockopt_lock_sock(sk);
 
        switch (optname) {
@@ -3569,13 +3573,6 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname,
                else
                        WRITE_ONCE(tp->keepalive_probes, val);
                break;
-       case TCP_SYNCNT:
-               if (val < 1 || val > MAX_TCP_SYNCNT)
-                       err = -EINVAL;
-               else
-                       WRITE_ONCE(icsk->icsk_syn_retries, val);
-               break;
-
        case TCP_SAVE_SYN:
                /* 0: disable, 1: enable, 2: start from ether_header */
                if (val < 0 || val > 2)
index 470f581..66040ab 100644 (file)
@@ -239,7 +239,8 @@ static int tcp_write_timeout(struct sock *sk)
        if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
                if (icsk->icsk_retransmits)
                        __dst_negative_advice(sk);
-               retry_until = icsk->icsk_syn_retries ? :
+               /* Paired with WRITE_ONCE() in tcp_sock_set_syncnt() */
+               retry_until = READ_ONCE(icsk->icsk_syn_retries) ? :
                        READ_ONCE(net->ipv4.sysctl_tcp_syn_retries);
 
                max_retransmits = retry_until;
@@ -421,8 +422,10 @@ static void tcp_fastopen_synack_timer(struct sock *sk, struct request_sock *req)
 
        req->rsk_ops->syn_ack_timeout(req);
 
-       /* add one more retry for fastopen */
-       max_retries = icsk->icsk_syn_retries ? :
+       /* Add one more retry for fastopen.
+        * Paired with WRITE_ONCE() in tcp_sock_set_syncnt()
+        */
+       max_retries = READ_ONCE(icsk->icsk_syn_retries) ? :
                READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_synack_retries) + 1;
 
        if (req->num_timeout >= max_retries) {