SO_INQ makes recvmsg() return the bytes left in the receive queue in an
SCM_INQ cmsg, saving a SIOCINQ call per receive. AF_UNIX stream sockets
support it, AF_VSOCK fails with ENOPROTOOPT.

Add it for SOCK_STREAM, reusing the hint already computed for io_uring.
As with TCP_INQ, 1 is reported once the stream is finished so the caller
reads again and sees EOF. No cmsg is sent when the transport cannot
report the queue length.

SOCK_SEQPACKET is refused, as on AF_UNIX: its transport callback counts
messages, not bytes.

Signed-off-by: David Carlier <[email protected]>
---
 include/net/af_vsock.h   |  1 +
 net/vmw_vsock/af_vsock.c | 41 +++++++++++++++++++++++++++++++---------
 2 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h
index 5549298c1ec6..e8481f12312e 100644
--- a/include/net/af_vsock.h
+++ b/include/net/af_vsock.h
@@ -68,6 +68,7 @@ struct vsock_sock {
        u64 buffer_size;
        u64 buffer_min_size;
        u64 buffer_max_size;
+       bool recvmsg_inq;
 
        /* Private to transport. */
        void *trans;
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 1c2d1cea67f4..874dc1545a35 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -2050,29 +2050,38 @@ static int vsock_connectible_setsockopt(struct socket 
*sock,
        transport = vsk->transport;
 
        if (level == SOL_SOCKET) {
-               int zerocopy;
+               int v;
 
-               if (optname != SO_ZEROCOPY) {
+               if (optname != SO_ZEROCOPY && optname != SO_INQ) {
                        release_sock(sk);
                        return sock_setsockopt(sock, level, optname, optval, 
optlen);
                }
 
                /* Use 'int' type here, because variable to
-                * set this option usually has this type.
+                * set these options usually has this type.
                 */
-               COPY_IN(zerocopy);
+               COPY_IN(v);
 
-               if (zerocopy < 0 || zerocopy > 1) {
+               if (v < 0 || v > 1) {
                        err = -EINVAL;
                        goto exit;
                }
 
+               if (optname == SO_INQ) {
+                       if (sk->sk_type != SOCK_STREAM) {
+                               err = -ENOPROTOOPT;
+                               goto exit;
+                       }
+                       vsk->recvmsg_inq = v;
+                       goto exit;
+               }
+
                if (transport && !vsock_msgzerocopy_allow(transport)) {
                        err = -EOPNOTSUPP;
                        goto exit;
                }
 
-               sock_valbool_flag(sk, SOCK_ZEROCOPY, zerocopy);
+               sock_valbool_flag(sk, SOCK_ZEROCOPY, v);
                goto exit;
        }
 
@@ -2581,6 +2590,7 @@ __vsock_connectible_recvmsg(struct socket *sock, struct 
msghdr *msg, size_t len,
        struct sock *sk;
        struct vsock_sock *vsk;
        const struct vsock_transport *transport;
+       bool do_cmsg = false;
        int err;
 
        sk = sock->sk;
@@ -2637,13 +2647,26 @@ __vsock_connectible_recvmsg(struct socket *sock, struct 
msghdr *msg, size_t len,
                err = __vsock_seqpacket_recvmsg(sk, msg, len, flags);
 
 out:
-       /* Seqpacket has_data counts messages, while io_uring treats msg_inq as
-        * a byte length when sizing retries, so only streams report a hint.
+       /* Seqpacket has_data counts messages, while SCM_INQ reports bytes and
+        * io_uring treats msg_inq as a byte length when sizing retries, so
+        * only streams report it.
         */
-       if (msg->msg_get_inq && err >= 0 && sk->sk_type == SOCK_STREAM)
+       if ((vsk->recvmsg_inq | msg->msg_get_inq) && err >= 0 &&
+           sk->sk_type == SOCK_STREAM) {
                msg->msg_inq = vsock_stream_inq_hint(sk);
 
+               /* A negative hint means the transport could not tell. SCM_INQ
+                * has no encoding for that, so report nothing.
+                */
+               do_cmsg = vsk->recvmsg_inq && msg->msg_inq >= 0;
+       }
+
        release_sock(sk);
+
+       if (do_cmsg)
+               put_cmsg(msg, SOL_SOCKET, SCM_INQ, sizeof(msg->msg_inq),
+                        &msg->msg_inq);
+
        return err;
 }
 
-- 
2.55.0


Reply via email to