From: Bobby Eshleman <bobbyeshleman@gmail.com>
To: David Carlier <devnexen@gmail.com>
Cc: netdev@vger.kernel.org, sgarzare@redhat.com, davem@davemloft.net,
edumazet@kernel.org, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, virtualization@lists.linux.dev,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH net-next v2 2/2] vsock/test: cover SO_INQ
Date: Thu, 15 Oct 2026 10:12:37 -0700 [thread overview]
Message-ID: <atEJhetXtwmGngqT@devvm29614.prn0.facebook.com> (raw)
In-Reply-To: <20261015154334.3217358-3-devnexen@gmail.com>
On Thu, Oct 15, 2026 at 04:43:34PM +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, that
> accepted sockets inherit it from the listener, 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 | 279 +++++++++++++++++++++++++++++++
> 1 file changed, 279 insertions(+)
>
> diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c
> index b4ff9f946565..10b9074fb87f 100644
> --- a/tools/testing/vsock/vsock_test.c
> +++ b/tools/testing/vsock/vsock_test.c
> @@ -2429,6 +2429,271 @@ 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");
> +
> + if (msg.msg_flags & MSG_CTRUNC) {
> + fprintf(stderr, "SCM_INQ control message truncated\n");
> + exit(EXIT_FAILURE);
> + }
> +
> + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
> + if (cmsg->cmsg_level != SOL_SOCKET ||
> + cmsg->cmsg_type != SCM_INQ)
> + continue;
> +
> + if (cmsg->cmsg_len != CMSG_LEN(sizeof(inq))) {
> + fprintf(stderr, "unexpected SCM_INQ length %zu\n",
> + (size_t)cmsg->cmsg_len);
> + exit(EXIT_FAILURE);
> + }
> +
> + 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_stream_so_inq_inherit_client(const struct test_opts *opts)
> +{
> + unsigned char buf[SO_INQ_BUF_LEN];
> + int fd;
> +
> + fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
> + if (fd < 0) {
> + perror("connect");
> + exit(EXIT_FAILURE);
> + }
> +
> + memset(buf, 'A', sizeof(buf));
> + send_buf(fd, buf, sizeof(buf), 0, sizeof(buf));
> + control_writeln("SENT");
> +
> + control_expectln("DONE");
> + close(fd);
> +}
> +
> +static void test_stream_so_inq_inherit_server(const struct test_opts *opts)
> +{
> + bool supported = true;
> + int listen_fd, fd;
> +
> + listen_fd = vsock_stream_listen(VMADDR_CID_ANY, opts->peer_port);
> +
> + /* Enable the option on the listener only. */
> + if (so_inq_set(listen_fd, 1)) {
> + if (errno != ENOPROTOOPT) {
> + perror("setsockopt(SO_INQ)");
> + exit(EXIT_FAILURE);
> + }
> +
> + fprintf(stderr, "Test skipped, SO_INQ not supported.\n");
> + supported = false;
> + }
> +
> + control_writeln("LISTENING");
> +
> + timeout_begin(TIMEOUT);
> + do {
> + fd = accept(listen_fd, NULL, NULL);
> + timeout_check("accept");
> + } while (fd < 0 && errno == EINTR);
> + timeout_end();
> +
> + if (fd < 0) {
> + perror("accept");
> + exit(EXIT_FAILURE);
> + }
> +
> + close(listen_fd);
> + control_expectln("SENT");
> +
> + if (!supported)
> + goto out;
> +
> + if (!vsock_ioctl_int(fd, SIOCINQ, SO_INQ_BUF_LEN)) {
> + fprintf(stderr, "Test skipped, SIOCINQ not supported.\n");
> + goto out;
> + }
> +
> + /* The accepted socket reports SCM_INQ without its own setsockopt(). */
> + expect_res(so_inq_recv(fd, SO_INQ_CHUNK_LEN, SO_INQ_CHUNK_LEN),
> + SO_INQ_BUF_LEN - SO_INQ_CHUNK_LEN,
> + "SCM_INQ inherited from the listener");
> +
> +out:
> + control_writeln("DONE");
> + 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 +2898,20 @@ 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 = "SOCK_STREAM SO_INQ inherited by accepted socket",
> + .run_client = test_stream_so_inq_inherit_client,
> + .run_server = test_stream_so_inq_inherit_server,
> + },
> + {
> + .name = "SO_INQ option validation",
> + .run_client = test_so_inq_validation_client,
> + },
> {},
> };
>
> --
> 2.55.0
>
Acked-by: Bobby Eshleman <bobbyeshleman@meta.com>
prev parent reply other threads:[~2026-10-15 17:12 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 15:43 [PATCH net-next v2 0/2] vsock: SO_INQ for stream sockets David Carlier
2026-10-15 15:43 ` [PATCH net-next v2 1/2] vsock: support SO_INQ on " David Carlier
2026-10-15 17:07 ` Bobby Eshleman
2026-10-15 15:43 ` [PATCH net-next v2 2/2] vsock/test: cover SO_INQ David Carlier
2026-10-15 17:12 ` Bobby Eshleman [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=atEJhetXtwmGngqT@devvm29614.prn0.facebook.com \
--to=bobbyeshleman@gmail.com \
--cc=davem@davemloft.net \
--cc=devnexen@gmail.com \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sgarzare@redhat.com \
--cc=virtualization@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox