inet: set/get simple options locklessly
authorEric Dumazet <edumazet@google.com>
Wed, 16 Aug 2023 08:15:34 +0000 (08:15 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 16 Aug 2023 10:09:17 +0000 (11:09 +0100)
Now we have inet->inet_flags, we can set following options
without having to hold the socket lock:

IP_PKTINFO, IP_RECVTTL, IP_RECVTOS, IP_RECVOPTS, IP_RETOPTS,
IP_PASSSEC, IP_RECVORIGDSTADDR, IP_RECVFRAGSIZE.

ip_sock_set_pktinfo() no longer hold the socket lock.

Similarly we can get the following options whithout holding
the socket lock:

IP_PKTINFO, IP_RECVTTL, IP_RECVTOS, IP_RECVOPTS, IP_RETOPTS,
IP_PASSSEC, IP_RECVORIGDSTADDR, IP_CHECKSUM, IP_RECVFRAGSIZE.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Soheil Hassas Yeganeh <soheil@google.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/ip_sockglue.c

index 66f55f3..69b8751 100644 (file)
@@ -636,9 +636,7 @@ EXPORT_SYMBOL(ip_sock_set_mtu_discover);
 
 void ip_sock_set_pktinfo(struct sock *sk)
 {
-       lock_sock(sk);
        inet_set_bit(PKTINFO, sk);
-       release_sock(sk);
 }
 EXPORT_SYMBOL(ip_sock_set_pktinfo);
 
@@ -952,6 +950,36 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname,
        if (ip_mroute_opt(optname))
                return ip_mroute_setsockopt(sk, optname, optval, optlen);
 
+       /* Handle options that can be set without locking the socket. */
+       switch (optname) {
+       case IP_PKTINFO:
+               inet_assign_bit(PKTINFO, sk, val);
+               return 0;
+       case IP_RECVTTL:
+               inet_assign_bit(TTL, sk, val);
+               return 0;
+       case IP_RECVTOS:
+               inet_assign_bit(TOS, sk, val);
+               return 0;
+       case IP_RECVOPTS:
+               inet_assign_bit(RECVOPTS, sk, val);
+               return 0;
+       case IP_RETOPTS:
+               inet_assign_bit(RETOPTS, sk, val);
+               return 0;
+       case IP_PASSSEC:
+               inet_assign_bit(PASSSEC, sk, val);
+               return 0;
+       case IP_RECVORIGDSTADDR:
+               inet_assign_bit(ORIGDSTADDR, sk, val);
+               return 0;
+       case IP_RECVFRAGSIZE:
+               if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM)
+                       return -EINVAL;
+               inet_assign_bit(RECVFRAGSIZE, sk, val);
+               return 0;
+       }
+
        err = 0;
        if (needs_rtnl)
                rtnl_lock();
@@ -991,27 +1019,6 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname,
                        kfree_rcu(old, rcu);
                break;
        }
-       case IP_PKTINFO:
-               inet_assign_bit(PKTINFO, sk, val);
-               break;
-       case IP_RECVTTL:
-               inet_assign_bit(TTL, sk, val);
-               break;
-       case IP_RECVTOS:
-               inet_assign_bit(TOS, sk, val);
-               break;
-       case IP_RECVOPTS:
-               inet_assign_bit(RECVOPTS, sk, val);
-               break;
-       case IP_RETOPTS:
-               inet_assign_bit(RETOPTS, sk, val);
-               break;
-       case IP_PASSSEC:
-               inet_assign_bit(PASSSEC, sk, val);
-               break;
-       case IP_RECVORIGDSTADDR:
-               inet_assign_bit(ORIGDSTADDR, sk, val);
-               break;
        case IP_CHECKSUM:
                if (val) {
                        if (!(inet_test_bit(CHECKSUM, sk))) {
@@ -1025,11 +1032,6 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname,
                        }
                }
                break;
-       case IP_RECVFRAGSIZE:
-               if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM)
-                       goto e_inval;
-               inet_assign_bit(RECVFRAGSIZE, sk, val);
-               break;
        case IP_TOS:    /* This sets both TOS and Precedence */
                __ip_sock_set_tos(sk, val);
                break;
@@ -1544,6 +1546,37 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
        if (len < 0)
                return -EINVAL;
 
+       /* Handle options that can be read without locking the socket. */
+       switch (optname) {
+       case IP_PKTINFO:
+               val = inet_test_bit(PKTINFO, sk);
+               goto copyval;
+       case IP_RECVTTL:
+               val = inet_test_bit(TTL, sk);
+               goto copyval;
+       case IP_RECVTOS:
+               val = inet_test_bit(TOS, sk);
+               goto copyval;
+       case IP_RECVOPTS:
+               val = inet_test_bit(RECVOPTS, sk);
+               goto copyval;
+       case IP_RETOPTS:
+               val = inet_test_bit(RETOPTS, sk);
+               goto copyval;
+       case IP_PASSSEC:
+               val = inet_test_bit(PASSSEC, sk);
+               goto copyval;
+       case IP_RECVORIGDSTADDR:
+               val = inet_test_bit(ORIGDSTADDR, sk);
+               goto copyval;
+       case IP_CHECKSUM:
+               val = inet_test_bit(CHECKSUM, sk);
+               goto copyval;
+       case IP_RECVFRAGSIZE:
+               val = inet_test_bit(RECVFRAGSIZE, sk);
+               goto copyval;
+       }
+
        if (needs_rtnl)
                rtnl_lock();
        sockopt_lock_sock(sk);
@@ -1578,33 +1611,6 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
                        return -EFAULT;
                return 0;
        }
-       case IP_PKTINFO:
-               val = inet_test_bit(PKTINFO, sk);
-               break;
-       case IP_RECVTTL:
-               val = inet_test_bit(TTL, sk);
-               break;
-       case IP_RECVTOS:
-               val = inet_test_bit(TOS, sk);
-               break;
-       case IP_RECVOPTS:
-               val = inet_test_bit(RECVOPTS, sk);
-               break;
-       case IP_RETOPTS:
-               val = inet_test_bit(RETOPTS, sk);
-               break;
-       case IP_PASSSEC:
-               val = inet_test_bit(PASSSEC, sk);
-               break;
-       case IP_RECVORIGDSTADDR:
-               val = inet_test_bit(ORIGDSTADDR, sk);
-               break;
-       case IP_CHECKSUM:
-               val = inet_test_bit(CHECKSUM, sk);
-               break;
-       case IP_RECVFRAGSIZE:
-               val = inet_test_bit(RECVFRAGSIZE, sk);
-               break;
        case IP_TOS:
                val = inet->tos;
                break;
@@ -1754,7 +1760,7 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
                return -ENOPROTOOPT;
        }
        sockopt_release_sock(sk);
-
+copyval:
        if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
                unsigned char ucval = (unsigned char)val;
                len = 1;