* [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; 7+ 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] 7+ 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-14 14:54 ` Stefano Garzarella 2026-10-12 23:55 ` [PATCH net-next 2/2] vsock/test: cover SO_INQ David Carlier 1 sibling, 2 replies; 7+ 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] 7+ 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 2026-10-14 14:54 ` Stefano Garzarella 1 sibling, 0 replies; 7+ 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] 7+ 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 @ 2026-10-14 14:54 ` Stefano Garzarella 1 sibling, 0 replies; 7+ messages in thread From: Stefano Garzarella @ 2026-10-14 14:54 UTC (permalink / raw) To: David Carlier Cc: netdev, bobbyeshleman, davem, edumazet, kuba, pabeni, horms, virtualization, linux-kernel On Tue, Oct 13, 2026 at 12:55:23AM +0100, David Carlier wrote: >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; Please use a variable with a better name. > >- if (optname != SO_ZEROCOPY) { >+ if (optname != SO_ZEROCOPY && optname != SO_INQ) { Can we add something like unix_custom_sockopt()? > 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; Should child sockets inherit this from a listener socket? >+ goto exit; >+ } Please add a switch to handle SO_INQ and SO_ZEROCOPY clearly. >+ > 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; > } Maybe we can add a new function to handle SOL_SOCKET level, but I don't have a strong opinion on that. > >@@ -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 && Is the bitwise or what we want here? Thanks, Stefano >+ 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 [flat|nested] 7+ 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 2026-10-14 15:05 ` Stefano Garzarella 1 sibling, 2 replies; 7+ 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] 7+ 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 2026-10-14 15:05 ` Stefano Garzarella 1 sibling, 0 replies; 7+ 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] 7+ 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 @ 2026-10-14 15:05 ` Stefano Garzarella 1 sibling, 0 replies; 7+ messages in thread From: Stefano Garzarella @ 2026-10-14 15:05 UTC (permalink / raw) To: David Carlier Cc: netdev, bobbyeshleman, davem, edumazet, kuba, pabeni, horms, virtualization, linux-kernel On Tue, Oct 13, 2026 at 12:55:24AM +0100, David Carlier wrote: >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; Should we check CMSG_LEN and/or MSG_CTRUNC ? The rest LGTM. Thanks, Stefano >+ >+ 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 [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-14 15:06 UTC | newest] Thread overview: 7+ 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-14 14:54 ` Stefano Garzarella 2026-10-12 23:55 ` [PATCH net-next 2/2] vsock/test: cover SO_INQ David Carlier 2026-10-13 0:06 ` sashiko-bot 2026-10-14 15:05 ` Stefano Garzarella
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox