* [PATCH net-next 0/2] vsock: SO_INQ for stream sockets
@ 2026-10-12 23:55 David Carlier
2026-10-12 23:55 ` [PATCH net-next 1/2] vsock: support SO_INQ on " David Carlier
2026-10-12 23:55 ` [PATCH net-next 2/2] vsock/test: cover SO_INQ David Carlier
0 siblings, 2 replies; 5+ messages in thread
From: David Carlier @ 2026-10-12 23:55 UTC (permalink / raw)
To: netdev
Cc: sgarzare, bobbyeshleman, davem, edumazet, kuba, pabeni, horms,
virtualization, linux-kernel, David Carlier
This is the follow-up announced in the receive queue hint series [1]:
SO_INQ and SCM_INQ for AF_VSOCK stream sockets, as AF_UNIX has.
The value is the one SIOCINQ returns, so on Hyper-V it covers the
current packet only. The flag is per socket, is not inherited by
accepted sockets, and the sockmap receive path does not report it.
Tested over loopback with vsock_test and vsock_uring_test.
[1] https://lore.kernel.org/netdev/20260923184008.153541-1-devnexen@gmail.com/
David Carlier (2):
vsock: support SO_INQ on stream sockets
vsock/test: cover SO_INQ
include/net/af_vsock.h | 1 +
net/vmw_vsock/af_vsock.c | 41 +++++--
tools/testing/vsock/vsock_test.c | 191 +++++++++++++++++++++++++++++++
3 files changed, 224 insertions(+), 9 deletions(-)
base-commit: a4057f51efc2b74ddf46721e83f45e9efb13649d
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net-next 1/2] vsock: support SO_INQ on stream sockets
2026-10-12 23:55 [PATCH net-next 0/2] vsock: SO_INQ for stream sockets David Carlier
@ 2026-10-12 23:55 ` David Carlier
2026-10-13 0:06 ` sashiko-bot
2026-10-12 23:55 ` [PATCH net-next 2/2] vsock/test: cover SO_INQ David Carlier
1 sibling, 1 reply; 5+ messages in thread
From: David Carlier @ 2026-10-12 23:55 UTC (permalink / raw)
To: netdev
Cc: sgarzare, bobbyeshleman, davem, edumazet, kuba, pabeni, horms,
virtualization, linux-kernel, David Carlier
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 <devnexen@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH net-next 2/2] vsock/test: cover SO_INQ
2026-10-12 23:55 [PATCH net-next 0/2] vsock: SO_INQ for stream sockets David Carlier
2026-10-12 23:55 ` [PATCH net-next 1/2] vsock: support SO_INQ on " David Carlier
@ 2026-10-12 23:55 ` David Carlier
2026-10-13 0:06 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: David Carlier @ 2026-10-12 23:55 UTC (permalink / raw)
To: netdev
Cc: sgarzare, bobbyeshleman, davem, edumazet, kuba, pabeni, horms,
virtualization, linux-kernel, David Carlier
Check the SCM_INQ value after a partial receive, after draining the
queue and at EOF, that no cmsg is sent while the option is off, and
that out of range values and SOCK_SEQPACKET are rejected.
Signed-off-by: David Carlier <devnexen@gmail.com>
---
tools/testing/vsock/vsock_test.c | 191 +++++++++++++++++++++++++++++++
1 file changed, 191 insertions(+)
diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c
index b4ff9f946565..00bb68c00c07 100644
--- a/tools/testing/vsock/vsock_test.c
+++ b/tools/testing/vsock/vsock_test.c
@@ -2429,6 +2429,188 @@ static void test_stream_collapse_server(const struct test_opts *opts)
close(fd);
}
+#define SO_INQ_BUF_LEN 64
+#define SO_INQ_CHUNK_LEN 16
+
+static int so_inq_set(int fd, int val)
+{
+ return setsockopt(fd, SOL_SOCKET, SO_INQ, &val, sizeof(val));
+}
+
+/* Receive @len bytes and return the SCM_INQ value, or -1 if the kernel did
+ * not attach one.
+ */
+static int so_inq_recv(int fd, size_t len, int expected_ret)
+{
+ char control[CMSG_SPACE(sizeof(int))] = { 0 };
+ unsigned char buf[SO_INQ_BUF_LEN];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = len,
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ ssize_t ret;
+ int inq;
+
+ ret = recvmsg(fd, &msg, 0);
+ if (ret < 0) {
+ perror("recvmsg");
+ exit(EXIT_FAILURE);
+ }
+
+ expect_res(ret, expected_ret, "recvmsg");
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_INQ)
+ continue;
+
+ memcpy(&inq, CMSG_DATA(cmsg), sizeof(inq));
+ return inq;
+ }
+
+ return -1;
+}
+
+static void test_stream_so_inq_client(const struct test_opts *opts)
+{
+ int fd;
+
+ fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
+ if (fd < 0) {
+ perror("connect");
+ exit(EXIT_FAILURE);
+ }
+
+ control_expectln("SENT");
+
+ /* Wait for the whole payload to be queued, so the values below do not
+ * depend on how the transport delivers it.
+ */
+ if (!vsock_ioctl_int(fd, SIOCINQ, SO_INQ_BUF_LEN)) {
+ fprintf(stderr, "Test skipped, SIOCINQ not supported.\n");
+ goto out;
+ }
+
+ /* No ancillary data unless the option is enabled. */
+ expect_res(so_inq_recv(fd, SO_INQ_CHUNK_LEN, SO_INQ_CHUNK_LEN), -1,
+ "SCM_INQ with SO_INQ disabled");
+
+ if (so_inq_set(fd, 1)) {
+ if (errno != ENOPROTOOPT) {
+ perror("setsockopt(SO_INQ)");
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(stderr, "Test skipped, SO_INQ not supported.\n");
+ goto out;
+ }
+
+ /* Partial read: the rest of the payload is still queued. */
+ expect_res(so_inq_recv(fd, SO_INQ_CHUNK_LEN, SO_INQ_CHUNK_LEN),
+ SO_INQ_BUF_LEN - 2 * SO_INQ_CHUNK_LEN,
+ "SCM_INQ after partial receive");
+
+ /* Draining read while the peer stays connected. */
+ expect_res(so_inq_recv(fd, SO_INQ_BUF_LEN - 2 * SO_INQ_CHUNK_LEN,
+ SO_INQ_BUF_LEN - 2 * SO_INQ_CHUNK_LEN), 0,
+ "SCM_INQ after draining receive");
+
+ control_writeln("DRAINED");
+ vsock_wait_remote_close(fd);
+
+ /* Nothing is queued once the peer is gone, but 1 is reported so the
+ * caller keeps reading until it observes EOF, as TCP_INQ does.
+ */
+ expect_res(so_inq_recv(fd, SO_INQ_CHUNK_LEN, 0), 1, "SCM_INQ at EOF");
+
+ if (so_inq_set(fd, 0)) {
+ perror("setsockopt(SO_INQ)");
+ exit(EXIT_FAILURE);
+ }
+
+ expect_res(so_inq_recv(fd, SO_INQ_CHUNK_LEN, 0), -1,
+ "SCM_INQ after disabling SO_INQ");
+
+ close(fd);
+ return;
+
+out:
+ control_writeln("DRAINED");
+ close(fd);
+}
+
+static void test_stream_so_inq_server(const struct test_opts *opts)
+{
+ unsigned char buf[SO_INQ_BUF_LEN];
+ int fd;
+
+ fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
+ if (fd < 0) {
+ perror("accept");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(buf, 'A', sizeof(buf));
+ send_buf(fd, buf, sizeof(buf), 0, sizeof(buf));
+ control_writeln("SENT");
+
+ /* Keep the connection open until the peer has drained the queue: an
+ * early close would turn its last SCM_INQ value into the EOF one.
+ */
+ control_expectln("DRAINED");
+ close(fd);
+}
+
+static void test_so_inq_validation_client(const struct test_opts *opts)
+{
+ int fd;
+
+ fd = socket(AF_VSOCK, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ if (so_inq_set(fd, 1)) {
+ if (errno != ENOPROTOOPT) {
+ perror("setsockopt(SO_INQ)");
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(stderr, "Test skipped, SO_INQ not supported.\n");
+ close(fd);
+ return;
+ }
+
+ if (!so_inq_set(fd, 2) || errno != EINVAL) {
+ fprintf(stderr, "SO_INQ accepted an out of range value\n");
+ exit(EXIT_FAILURE);
+ }
+
+ close(fd);
+
+ /* SCM_INQ counts bytes, which a message based socket cannot report. */
+ fd = socket(AF_VSOCK, SOCK_SEQPACKET, 0);
+ if (fd < 0) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!so_inq_set(fd, 1) || errno != ENOPROTOOPT) {
+ fprintf(stderr, "SO_INQ not rejected on SOCK_SEQPACKET\n");
+ exit(EXIT_FAILURE);
+ }
+
+ close(fd);
+}
+
static struct test_case test_cases[] = {
{
.name = "SOCK_STREAM connection reset",
@@ -2633,6 +2815,15 @@ static struct test_case test_cases[] = {
.run_client = test_stream_collapse_client,
.run_server = test_stream_collapse_server,
},
+ {
+ .name = "SOCK_STREAM SO_INQ functionality",
+ .run_client = test_stream_so_inq_client,
+ .run_server = test_stream_so_inq_server,
+ },
+ {
+ .name = "SO_INQ option validation",
+ .run_client = test_so_inq_validation_client,
+ },
{},
};
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH net-next 2/2] vsock/test: cover SO_INQ
2026-10-12 23:55 ` [PATCH net-next 2/2] vsock/test: cover SO_INQ David Carlier
@ 2026-10-13 0:06 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-13 0:06 UTC (permalink / raw)
To: David Carlier; +Cc: Michael S. Tsirkin, Eugenio Perez, virtualization
> Check the SCM_INQ value after a partial receive, after draining the
> queue and at EOF, that no cmsg is sent while the option is off, and
> that out of range values and SOCK_SEQPACKET are rejected.
>
> Signed-off-by: David Carlier <devnexen@gmail.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261012235524.41468-1-devnexen@gmail.com?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net-next 1/2] vsock: support SO_INQ on stream sockets
2026-10-12 23:55 ` [PATCH net-next 1/2] vsock: support SO_INQ on " David Carlier
@ 2026-10-13 0:06 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-13 0:06 UTC (permalink / raw)
To: David Carlier; +Cc: Michael S. Tsirkin, Eugenio Perez, virtualization
> 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 <devnexen@gmail.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261012235524.41468-1-devnexen@gmail.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-13 0:06 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-12 23:55 [PATCH net-next 0/2] vsock: SO_INQ for stream sockets David Carlier
2026-10-12 23:55 ` [PATCH net-next 1/2] vsock: support SO_INQ on " David Carlier
2026-10-13 0:06 ` sashiko-bot
2026-10-12 23:55 ` [PATCH net-next 2/2] vsock/test: cover SO_INQ David Carlier
2026-10-13 0:06 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.