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

