From nobody Fri Oct 23 22:12:02 2026 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CDF073EAC75 for ; Thu, 15 Oct 2026 15:43:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792079035; cv=none; b=YeK0ReHg/YfyRpQ2rThi/wpJOVpPph61tNS0AO9dWc345p3SAyvXSTivKTzF9UtSaoa9rc0ceSb9AoT7kxKHp6jbBvXvwZLHIFNOMUC+ZsEMzCLzFYfdOpFNn2ny2gQMo4aS3oAsefk9zLxsztoeKZAvvZOoWwpNkUWyHBKDgVY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792079035; c=relaxed/simple; bh=QPUr45PzMu/8RpW9lNwZfx8Bpv3qdCpILxrSxT0z04s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QOBo7QkzzvuyP8HOiHzQ+q6c8t+qttrPDuyAqGe3eGOhs37twB0l+Okqsqp0JfeF3MCl+2B+qEbJLGm10KynIKvOHTNZkotE1w3mCtd+c9F8Uy0kYldVpn4dfDtj7YJpqWCpk6XSVtGIy6yTbSFRfM+H682jGUwvt9fw2v+WXbY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=AZIPP3xT; arc=none smtp.client-ip=209.85.128.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="AZIPP3xT" Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-49fbb2acc90so46411035e9.2 for ; Thu, 15 Oct 2026 08:43:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792079021; x=1792683821; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=HffmXfJKy6j0voVhf7+euRmSr6xXXF4Ar0m+br65Sq0=; b=AZIPP3xTvimIHo5ikMV3LtLGis15wbOMDwi9hi3jdsqFEdkn4bYRiR9JnoTHRF1+g7 KgYuqRtbVUgLJ2yofQbPs+HWpABK0QxB33UmFKM95jIdNKB/GaxHKzcdfLKTq3EGsa+N nqlAbheTKj/7FfWToWOZCMNP6vkFDkvI3lHWTrw5gi0QR8Li1lSC7nqSlOm0CxWE9UTQ 3KbId33D1Ad8416o2DVzXXMIMENjB8mhh5wC2NivhO39DEpJZUyW1070urwD51AhQlNa FZwWEesTSfFdXdgTHVNM2Gvtnz4bwBZ/UmLCC33owrYk3PaaijUW64DpfrkWgpG2vXUB HMMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792079021; x=1792683821; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=HffmXfJKy6j0voVhf7+euRmSr6xXXF4Ar0m+br65Sq0=; b=w9RXqHx9xhYLXVPGy6IfD+vU3Vo/ZEFOpPICeqyVFe2S4zH/mw3Q5GBZJ1ESsIklkU WfPN98b7X6+6+Veu+bwuF3JCxwaVgk3UOxIq4QycTLRwvjHuAxRyvGQQpR4BueCPzonN ZAQBK7cWXSJupbx+39sD+Je4n+9ovef4XBsw4c0roT+jhJKaK2YPfyh5+5iZZ98YFVLg BJGJagsb6gru4Ooci7IPRYXYl2LXo4vdQFf+fj3KZDewAN/mxyFHGafI/gcljo16/Nrt FmvuhUgQH/oYOoY/hnLfdrq7qhAvq9fZx2r5epQ1VICVoyrHuK66DeSpaoj9XPRZTUPn 2yqQ== X-Forwarded-Encrypted: i=1; AKwUvByYPr/Y1Ik+eliXwA+SnfcxbmrnSA1DZsHbM9tAWRUMem/ig9PyN4zFjWRouB2Id/UZYFBEfr/XTSSLiSw=@vger.kernel.org X-Gm-Message-State: AFq9FYJ0ssT/CdVxZvBwZZGml/CKt5yt88qlCSxniu3/ey4plE4hj5L7 fqcPF5AN1CE50z9whLkSN58iWvfXJzbp7W4Ekv/5hTKQKeBVQSg7HJud X-Gm-Gg: AYBFou2+GfTCA2nGfrIap4GS8pjZL33Be0eTSZhl+k2kD0npY0oko+vmBa+Ce4lhXEy Ci+6xDhksRr+jgzImhFUVgwRFONHiE6j2NDOdO2XN8BV96Emfh7f0P+l1OoeVhWzF0i9ideWZXy WxFhvqYqrdiZWQyJsy/eph2t/0agqRgrouCRv9HhQHb1dd79ITb35C0bB5vCJnANelQmYO2uH2i aOU5dZ/UVawIFgghL8mESzAPm3l+eMYT543F9LsQPWKCRr0twrrWmDXLsmT+NnceiLz2M9wobLw rYOmL5k14vcAZBP9ReQp9OclQbwAyNLKI14WsMKgzPJg4o/8kt/XnIRsKgEfuRXRpcX14JTAulY YzmOiDUCVlVsI8FEb1/s68lFQws6+EOnYjLtO89XayqX/jgHEALlDqm8Wo1okljisRltJaLA0zf bQkItUMXrynOvG5O6y3mSJK1FE778YupcS6dMuoyVEfn/HNVNfgZQlNip56y4Ot/QC+IbUkXO2C 0+rjyj4d0hbyw5U+QBA7htFmUpNnwtUhe/ESGYMjwg+6bMHUWSnGiGBYI1x X-Received: by 2002:a05:600c:350b:b0:4a1:ad01:f8dc with SMTP id 5b1f17b1804b1-4a1ad01f924mr184426785e9.21.1792079020615; Thu, 15 Oct 2026 08:43:40 -0700 (PDT) Received: from dohko.chello.ie (188-141-5-72.dynamic.upc.ie. [188.141.5.72]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4a1bd640be0sm61940825e9.1.2026.10.15.08.43.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Oct 2026 08:43:40 -0700 (PDT) From: David Carlier To: netdev@vger.kernel.org Cc: sgarzare@redhat.com, bobbyeshleman@gmail.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, David Carlier Subject: [PATCH net-next v2 1/2] vsock: support SO_INQ on stream sockets Date: Thu, 15 Oct 2026 16:43:33 +0100 Message-ID: <20261015154334.3217358-2-devnexen@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261015154334.3217358-1-devnexen@gmail.com> References: <20261015154334.3217358-1-devnexen@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Acked-by: Bobby Eshleman --- include/net/af_vsock.h | 1 + net/vmw_vsock/af_vsock.c | 111 +++++++++++++++++++++++++++++---------- 2 files changed, 84 insertions(+), 28 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; =20 /* Private to transport. */ void *trans; diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 1c2d1cea67f4..186ed57fd797 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -984,6 +984,7 @@ static struct sock *__vsock_create(struct net *net, vsk->buffer_size =3D psk->buffer_size; vsk->buffer_min_size =3D psk->buffer_min_size; vsk->buffer_max_size =3D psk->buffer_max_size; + vsk->recvmsg_inq =3D psk->recvmsg_inq; security_sk_clone(parent, sk); } else { vsk->trusted =3D ns_capable_noaudit(&init_user_ns, CAP_NET_ADMIN); @@ -2014,6 +2015,66 @@ static void vsock_update_buffer_size(struct vsock_so= ck *vsk, vsk->buffer_size =3D val; } =20 +static bool vsock_custom_sockopt(int optname) +{ + switch (optname) { + case SO_ZEROCOPY: + case SO_INQ: + return true; + default: + return false; + } +} + +static int vsock_sol_socket_setsockopt(struct sock *sk, int optname, + sockptr_t optval, unsigned int optlen) +{ + struct vsock_sock *vsk =3D vsock_sk(sk); + int err =3D 0; + int val; + + /* Use 'int' type here, because variable to + * set these options usually has this type. + */ + if (optlen < sizeof(val)) + return -EINVAL; + + if (copy_from_sockptr(&val, optval, sizeof(val))) + return -EFAULT; + + if (val < 0 || val > 1) + return -EINVAL; + + lock_sock(sk); + + switch (optname) { + case SO_ZEROCOPY: + if (vsk->transport && !vsock_msgzerocopy_allow(vsk->transport)) { + err =3D -EOPNOTSUPP; + break; + } + + sock_valbool_flag(sk, SOCK_ZEROCOPY, val); + break; + + case SO_INQ: + if (sk->sk_type !=3D SOCK_STREAM) { + err =3D -ENOPROTOOPT; + break; + } + + vsk->recvmsg_inq =3D val; + break; + + default: + err =3D -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + static int vsock_connectible_setsockopt(struct socket *sock, int level, int optname, @@ -2045,36 +2106,16 @@ static int vsock_connectible_setsockopt(struct sock= et *sock, sk =3D sock->sk; vsk =3D vsock_sk(sk); =20 - lock_sock(sk); - - transport =3D vsk->transport; - if (level =3D=3D SOL_SOCKET) { - int zerocopy; - - if (optname !=3D SO_ZEROCOPY) { - release_sock(sk); + if (!vsock_custom_sockopt(optname)) return sock_setsockopt(sock, level, optname, optval, optlen); - } - - /* Use 'int' type here, because variable to - * set this option usually has this type. - */ - COPY_IN(zerocopy); =20 - if (zerocopy < 0 || zerocopy > 1) { - err =3D -EINVAL; - goto exit; - } + return vsock_sol_socket_setsockopt(sk, optname, optval, optlen); + } =20 - if (transport && !vsock_msgzerocopy_allow(transport)) { - err =3D -EOPNOTSUPP; - goto exit; - } + lock_sock(sk); =20 - sock_valbool_flag(sk, SOCK_ZEROCOPY, zerocopy); - goto exit; - } + transport =3D vsk->transport; =20 switch (optname) { case SO_VM_SOCKETS_BUFFER_SIZE: @@ -2581,6 +2622,7 @@ __vsock_connectible_recvmsg(struct socket *sock, stru= ct msghdr *msg, size_t len, struct sock *sk; struct vsock_sock *vsk; const struct vsock_transport *transport; + bool do_cmsg =3D false; int err; =20 sk =3D sock->sk; @@ -2637,13 +2679,26 @@ __vsock_connectible_recvmsg(struct socket *sock, st= ruct msghdr *msg, size_t len, err =3D __vsock_seqpacket_recvmsg(sk, msg, len, flags); =20 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 >=3D 0 && sk->sk_type =3D=3D SOCK_STREAM) + if ((vsk->recvmsg_inq || msg->msg_get_inq) && err >=3D 0 && + sk->sk_type =3D=3D SOCK_STREAM) { msg->msg_inq =3D vsock_stream_inq_hint(sk); =20 + /* A negative hint means the transport could not tell. SCM_INQ + * has no encoding for that, so report nothing. + */ + do_cmsg =3D vsk->recvmsg_inq && msg->msg_inq >=3D 0; + } + release_sock(sk); + + if (do_cmsg) + put_cmsg(msg, SOL_SOCKET, SCM_INQ, sizeof(msg->msg_inq), + &msg->msg_inq); + return err; } =20 --=20 2.55.0 From nobody Fri Oct 23 22:12:02 2026 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3B4C94EC66E for ; Thu, 15 Oct 2026 15:43:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792079040; cv=none; b=rqb6l4X0eYgPOLkDaYT/xM7tJj/thcOn7ANDECx7IZfNElSWeD9eurKvQOp1zkVuLas0Rw+QBAgwVqEp6MgaBdKinnF2bIkLsZ1LiF3GTynzJiG3E645WWuNz2Ue+dIlnIV/I3X7m7zlIOQ/2LeE3m9URyT6dNzkFt1f0cceIhM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792079040; c=relaxed/simple; bh=cjliimpdVuKniQW64DNaS43E2VHJ8S1ZygL4W+E5f/o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rvBcGgU9KZJ6ixehzh/3nJp0AShm0O2zkllRoom3y9tMCiJVnCPALodiRlrXSBNpfKEqmqZZGV68T9uSlsOBQWOpU0zsdlT4q6NqVgkG0HyqrjfThPML/q2wh83/qSSEzSqGANvTtVPtX6Xw2zqE6Ix+f+ZnZILcw8esKslsME8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=CkWxiP7M; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CkWxiP7M" Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-4a061a13884so45731505e9.3 for ; Thu, 15 Oct 2026 08:43:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792079023; x=1792683823; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=5J5a55Pg+YpfFRUGFYyLBsKQw7Ye+GLO4/t3xMgXcbc=; b=CkWxiP7Mpk9fhu/Mu3Niws5Z5yVCJoICnABrUNgzwnKjnInJahWcjhX8uNn24Q8NIh DweEFXUK5pBhsVuiNazJvteBf/rmxo3jHCG5fNKSHDbF98WRIV90wPlN+w9U9mo9nMkl 2R9QZhjDU2jQx0st2wnmUDlvl2o7+CteJk42UTQwyf8KuArbVHYuoxTOwlAXTs9NDwcb NCDKoSROGI7IQhMH8KYvBiM/v/wo9tbmaP6tOne1Arj+o1/bz4sQWiHlbkqd2o5nurGQ HM6nbPR6POx+FQKO73K5xML/OGKBpATEpS+JlbxBmbhWhvzS/wKC+mfxfMCd7LEpTSUq W2kg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792079023; x=1792683823; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=5J5a55Pg+YpfFRUGFYyLBsKQw7Ye+GLO4/t3xMgXcbc=; b=C0hPcjWNop1UGi4HmLmB6hmPfebVmpispN8FLYWGYL0Bf2Lta6umfjOBv+p3zm6Iku s65wBA5e+BOtv+gJWKRAu5xIuyOWtB3hhEgTHSq4xCq8EbQCGk2jFHBMwMSodDfpn6yE p/alY7pJijwiQ7eAIglohoAU9rVHNgeMinEM5bN17lmYi5rw1QhTFi8J+A0Zp+HwBXGg Aw4Rg6V0uqggcV+YPdsev4YjBRJ5m3LnRySZ085WHKzX3BqAxIznVsRxPIds1yAKPcBX i/iju4hTwV0s1DD72TUwwCQGlL/Z1NwQ6qYUzXZV1NO+mseighM8moGmjIFg08VralbN JfJQ== X-Forwarded-Encrypted: i=1; AKwUvBxzCqxdxGMwYM6A/nsStKepwAGkZp1b2WYeb26ClZtcqwgu8HMqfwHBSFrLHFimMtEBp8t9RhBpr6SX6q4=@vger.kernel.org X-Gm-Message-State: AFq9FYLWLd7XaDtHBqFyNsRCIUL91R1mMJF6xSrGMQVdcFSDPeWLFtmX RICFG+N9sOzEwnKgt1BCH0iEkFK+MVQMuFzQBJmOl/fB1KFyZr3nPosM X-Gm-Gg: AYBFou2rZ91psE8X7/7VQa0X1X7wQzEXtMUaEdXC9e15qAj76lt6LUVMB0MPM+WajfS dvJarejgHCKl8QFuUsyCc2bY2WSyYohA0NnwvzyewHson8NDyCmK0R1aIzEYxXtPD/Z7QUYWtfJ O4zsr5Edpc0xuablITDim3eY3JnYqPmlof8BK7jhfme+tvBA0b4C4lmBvrotEKcEiavCAV2dxFM iU2p5v9Au1RBeGowWkgJttWvp5rc/SV6IAATzsA/hDwLBi3xwleLqneWl6F+JcLLhaI1Vn4p2iW kfjFwvbfnhLf817gZZDI7yUWDn4RQbaEDLj+/gEQLKhQJbKLZNLzHDTqNK8o2VLuH4N3RVzMQaU z+OSHa3GKGp8gOtP1luKDL+UFGhyhkoqxzJaEHAFa6t5rDrs7eDhXyQsMb4+NDe2Z+g8YwULE3b a+VyQ5eqI1KTnUxFlHLvzEh1CNOjl58VY3bewDS7/jAxSbJYsw6NAh3UgROC0DSRTHWHMpUikSF R1ncxdGkmN7TMK0hzFxrcwbsOB00GPNKOlFlF9FqrLBmUyeA0nYuirbq7E= X-Received: by 2002:a05:600c:6384:b0:4a1:7741:ec43 with SMTP id 5b1f17b1804b1-4a18e4b8966mr365881315e9.26.1792079023060; Thu, 15 Oct 2026 08:43:43 -0700 (PDT) Received: from dohko.chello.ie (188-141-5-72.dynamic.upc.ie. [188.141.5.72]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4a1bd640be0sm61940825e9.1.2026.10.15.08.43.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Oct 2026 08:43:41 -0700 (PDT) From: David Carlier To: netdev@vger.kernel.org Cc: sgarzare@redhat.com, bobbyeshleman@gmail.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, David Carlier Subject: [PATCH net-next v2 2/2] vsock/test: cover SO_INQ Date: Thu, 15 Oct 2026 16:43:34 +0100 Message-ID: <20261015154334.3217358-3-devnexen@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261015154334.3217358-1-devnexen@gmail.com> References: <20261015154334.3217358-1-devnexen@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Acked-by: Bobby Eshleman Reviewed-by: Stefano Garzarella --- 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_t= est.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 struc= t test_opts *opts) close(fd); } =20 +#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))] =3D { 0 }; + unsigned char buf[SO_INQ_BUF_LEN]; + struct iovec iov =3D { + .iov_base =3D buf, + .iov_len =3D len, + }; + struct msghdr msg =3D { + .msg_iov =3D &iov, + .msg_iovlen =3D 1, + .msg_control =3D control, + .msg_controllen =3D sizeof(control), + }; + struct cmsghdr *cmsg; + ssize_t ret; + int inq; + + ret =3D 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 =3D CMSG_FIRSTHDR(&msg); cmsg; cmsg =3D CMSG_NXTHDR(&msg, cmsg)= ) { + if (cmsg->cmsg_level !=3D SOL_SOCKET || + cmsg->cmsg_type !=3D SCM_INQ) + continue; + + if (cmsg->cmsg_len !=3D 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 =3D 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 !=3D 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 =3D 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 =3D 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 =3D true; + int listen_fd, fd; + + listen_fd =3D 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 !=3D ENOPROTOOPT) { + perror("setsockopt(SO_INQ)"); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "Test skipped, SO_INQ not supported.\n"); + supported =3D false; + } + + control_writeln("LISTENING"); + + timeout_begin(TIMEOUT); + do { + fd =3D accept(listen_fd, NULL, NULL); + timeout_check("accept"); + } while (fd < 0 && errno =3D=3D 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 =3D socket(AF_VSOCK, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + if (so_inq_set(fd, 1)) { + if (errno !=3D 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 !=3D 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 =3D socket(AF_VSOCK, SOCK_SEQPACKET, 0); + if (fd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + if (!so_inq_set(fd, 1) || errno !=3D ENOPROTOOPT) { + fprintf(stderr, "SO_INQ not rejected on SOCK_SEQPACKET\n"); + exit(EXIT_FAILURE); + } + + close(fd); +} + static struct test_case test_cases[] =3D { { .name =3D "SOCK_STREAM connection reset", @@ -2633,6 +2898,20 @@ static struct test_case test_cases[] =3D { .run_client =3D test_stream_collapse_client, .run_server =3D test_stream_collapse_server, }, + { + .name =3D "SOCK_STREAM SO_INQ functionality", + .run_client =3D test_stream_so_inq_client, + .run_server =3D test_stream_so_inq_server, + }, + { + .name =3D "SOCK_STREAM SO_INQ inherited by accepted socket", + .run_client =3D test_stream_so_inq_inherit_client, + .run_server =3D test_stream_so_inq_inherit_server, + }, + { + .name =3D "SO_INQ option validation", + .run_client =3D test_so_inq_validation_client, + }, {}, }; =20 --=20 2.55.0