vsock: support SO_INQ on stream sockets

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, and accepted sockets inherit the setting from
the listener. 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 <devnexen@gmail.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Link: https://patch.msgid.link/20261016132716.272018-3-devnexen@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
David Carlier authored and Paolo Abeni committed 2026-10-21 11:58:21 +02:00
1 parent a006b921ad
commit d7bb20b911
2 files changed
+33 -3

No files matched your search

+1
View File
@@ -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;
+32 -3
View File
@@ -984,6 +984,7 @@ static struct sock *__vsock_create(struct net *net,
vsk->buffer_size = psk->buffer_size;
vsk->buffer_min_size = psk->buffer_min_size;
vsk->buffer_max_size = psk->buffer_max_size;
vsk->recvmsg_inq = psk->recvmsg_inq;
security_sk_clone(parent, sk);
} else {
vsk->trusted = ns_capable_noaudit(&init_user_ns, CAP_NET_ADMIN);
@@ -2017,6 +2018,7 @@ static bool vsock_sol_socket_custom(int optname)
{
switch (optname) {
case SO_ZEROCOPY:
case SO_INQ:
return true;
default:
return false;
@@ -2064,6 +2066,19 @@ static int vsock_sol_socket_setsockopt(struct sock *sk, int optname,
sock_valbool_flag(sk, SOCK_ZEROCOPY, val);
break;
case SO_INQ:
if (sk->sk_type != SOCK_STREAM) {
err = -ENOPROTOOPT;
break;
}
err = vsock_sol_socket_get_bool(optval, optlen, &val);
if (err)
break;
vsk->recvmsg_inq = val;
break;
default:
err = -ENOPROTOOPT;
break;
@@ -2620,6 +2635,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;
@@ -2676,13 +2692,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;
}