]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
af_unix: Introduce SO_INQ.
authorKuniyuki Iwashima <kuniyu@google.com>
Wed, 2 Jul 2025 22:35:18 +0000 (22:35 +0000)
committerJakub Kicinski <kuba@kernel.org>
Wed, 9 Jul 2025 01:05:25 +0000 (18:05 -0700)
We have an application that uses almost the same code for TCP and
AF_UNIX (SOCK_STREAM).

TCP can use TCP_INQ, but AF_UNIX doesn't have it and requires an
extra syscall, ioctl(SIOCINQ) or getsockopt(SO_MEMINFO) as an
alternative.

Let's introduce the generic version of TCP_INQ.

If SO_INQ is enabled, recvmsg() will put a cmsg of SCM_INQ that
contains the exact value of ioctl(SIOCINQ).  The cmsg is also
included when msg->msg_get_inq is non-zero to make sockets
io_uring-friendly.

Note that SOCK_CUSTOM_SOCKOPT is flagged only for SOCK_STREAM to
override setsockopt() for SOL_SOCKET.

By having the flag in struct unix_sock, instead of struct sock, we
can later add SO_INQ support for TCP and reuse tcp_sk(sk)->recvmsg_inq.

Note also that supporting custom getsockopt() for SOL_SOCKET will need
preparation for other SOCK_CUSTOM_SOCKOPT users (UDP, vsock, MPTCP).

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20250702223606.1054680-7-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
arch/alpha/include/uapi/asm/socket.h
arch/mips/include/uapi/asm/socket.h
arch/parisc/include/uapi/asm/socket.h
arch/sparc/include/uapi/asm/socket.h
include/net/af_unix.h
include/uapi/asm-generic/socket.h
net/unix/af_unix.c

index 8f1f18adcdb593953284d8a6e92549d19a471b24..5ef57f88df6b35f950befed5a2923f0ffc7a43f0 100644 (file)
 
 #define SO_PASSRIGHTS          83
 
+#define SO_INQ                 84
+#define SCM_INQ                        SO_INQ
+
 #if !defined(__KERNEL__)
 
 #if __BITS_PER_LONG == 64
index 31ac655b78371b214b243207f02df1918e00a2f8..72fb1b006da93519304559d5e359c42cea5176f5 100644 (file)
 
 #define SO_PASSRIGHTS          83
 
+#define SO_INQ                 84
+#define SCM_INQ                        SO_INQ
+
 #if !defined(__KERNEL__)
 
 #if __BITS_PER_LONG == 64
index 1f2d5b7a7f5db3a79504ea57ea918092fb78039a..c16ec36dfee6b7dfc428105b4d8295e6cfaad970 100644 (file)
 
 #define SO_PASSRIGHTS          0x4051
 
+#define SO_INQ                 0x4052
+#define SCM_INQ                        SO_INQ
+
 #if !defined(__KERNEL__)
 
 #if __BITS_PER_LONG == 64
index adcba73293868f16ab84932a0dd38657b23d036f..71befa109e1cf33a354feff9b814d76f7feb588f 100644 (file)
 
 #define SO_PASSRIGHTS            0x005c
 
+#define SO_INQ                   0x005d
+#define SCM_INQ                  SO_INQ
+
 #if !defined(__KERNEL__)
 
 
index 603f8cd026e5a8466bbc6f3e7e798e5419762f6b..34f53dde65cea29426ec3ad73aac85003453ffac 100644 (file)
@@ -48,6 +48,7 @@ struct unix_sock {
        wait_queue_entry_t      peer_wake;
        struct scm_stat         scm_stat;
        int                     inq_len;
+       bool                    recvmsg_inq;
 #if IS_ENABLED(CONFIG_AF_UNIX_OOB)
        struct sk_buff          *oob_skb;
 #endif
index f333a0ac4ee4f0da6db544ea1f8ffcac791d1591..53b5a8c002b1eeeaa0485ca09a13c6f5666fb8fc 100644 (file)
 
 #define SO_PASSRIGHTS          83
 
+#define SO_INQ                 84
+#define SCM_INQ                        SO_INQ
+
 #if !defined(__KERNEL__)
 
 #if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
index c3dd41596d89790ffbc46d2b5ee45b7221a4cdfb..7a92733706fe25c01187ded179a608edb21c6024 100644 (file)
@@ -934,6 +934,52 @@ static void unix_show_fdinfo(struct seq_file *m, struct socket *sock)
 #define unix_show_fdinfo NULL
 #endif
 
+static bool unix_custom_sockopt(int optname)
+{
+       switch (optname) {
+       case SO_INQ:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static int unix_setsockopt(struct socket *sock, int level, int optname,
+                          sockptr_t optval, unsigned int optlen)
+{
+       struct unix_sock *u = unix_sk(sock->sk);
+       struct sock *sk = sock->sk;
+       int val;
+
+       if (level != SOL_SOCKET)
+               return -EOPNOTSUPP;
+
+       if (!unix_custom_sockopt(optname))
+               return sock_setsockopt(sock, level, optname, optval, optlen);
+
+       if (optlen != sizeof(int))
+               return -EINVAL;
+
+       if (copy_from_sockptr(&val, optval, sizeof(val)))
+               return -EFAULT;
+
+       switch (optname) {
+       case SO_INQ:
+               if (sk->sk_type != SOCK_STREAM)
+                       return -EINVAL;
+
+               if (val > 1 || val < 0)
+                       return -EINVAL;
+
+               WRITE_ONCE(u->recvmsg_inq, val);
+               break;
+       default:
+               return -ENOPROTOOPT;
+       }
+
+       return 0;
+}
+
 static const struct proto_ops unix_stream_ops = {
        .family =       PF_UNIX,
        .owner =        THIS_MODULE,
@@ -950,6 +996,7 @@ static const struct proto_ops unix_stream_ops = {
 #endif
        .listen =       unix_listen,
        .shutdown =     unix_shutdown,
+       .setsockopt =   unix_setsockopt,
        .sendmsg =      unix_stream_sendmsg,
        .recvmsg =      unix_stream_recvmsg,
        .read_skb =     unix_stream_read_skb,
@@ -1116,6 +1163,7 @@ static int unix_create(struct net *net, struct socket *sock, int protocol,
 
        switch (sock->type) {
        case SOCK_STREAM:
+               set_bit(SOCK_CUSTOM_SOCKOPT, &sock->flags);
                sock->ops = &unix_stream_ops;
                break;
                /*
@@ -1847,6 +1895,9 @@ static int unix_accept(struct socket *sock, struct socket *newsock,
        skb_free_datagram(sk, skb);
        wake_up_interruptible(&unix_sk(sk)->peer_wait);
 
+       if (tsk->sk_type == SOCK_STREAM)
+               set_bit(SOCK_CUSTOM_SOCKOPT, &newsock->flags);
+
        /* attach accepted sock to socket */
        unix_state_lock(tsk);
        unix_update_edges(unix_sk(tsk));
@@ -3034,10 +3085,17 @@ unlock:
        } while (size);
 
        mutex_unlock(&u->iolock);
-       if (msg)
+       if (msg) {
                scm_recv_unix(sock, msg, &scm, flags);
-       else
+
+               if (READ_ONCE(u->recvmsg_inq) || msg->msg_get_inq) {
+                       msg->msg_inq = READ_ONCE(u->inq_len);
+                       put_cmsg(msg, SOL_SOCKET, SCM_INQ,
+                                sizeof(msg->msg_inq), &msg->msg_inq);
+               }
+       } else {
                scm_destroy(&scm);
+       }
 out:
        return copied ? : err;
 }