From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4BC6E392C5A; Tue, 1 Sep 2026 13:40:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270006; cv=none; b=cu4J8GLMFaIHmlsUGZ7sDWRxlMwouASLugroSgWC6h3V3EQlACMkwpJWmdrhNU4UMog6G+zJ/wIOVLhXV0+8G2SrpA05Dow10oD6SMqLA5x3oWY7jr1NgnzZcy1qNl4IY904dSgpJyz54Ov1qFRIo6e7hm3JZYveg5ybE+/5qG8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270006; c=relaxed/simple; bh=tRmgxww+rxzTihIU6XxzVA4+qqOxvEdXskaEQAJ+8Pg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fBdPAozZR4iV24wVFxJnBUNyq0hX0hS/giAy03frAnoFfgmWPO54lDeRbtgIiV+9Rm6tHNkxF1IDybCWTQz+7XUykdw6yq76lRAWVwSY8nipB+Vu68I2qquX4GstAPXCi6dlZffewQDFCNaj0cGjc7sQm0wW/EfusQjyQgsJLkQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=JkpAmDyn; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="JkpAmDyn" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 86D251F00A3E; Tue, 1 Sep 2026 13:40:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270005; bh=pL3XeMVN1EzXKRu842hzSRvAUH5WEX0PtRHjn0sU1pg=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=JkpAmDynHq6WoSqdiah24q/01nO0S16uzFoeU19LAOPJrYmNH/F41IZO2ylJBgcVW N5sF+iCI+7vPzdq+oWrJBLYQJGwjd3OuC5ciQfh8VXcma1qR8FV/Hhd1SOwrQn66DX azUowcYFfAAWh2I3ucjn8tDHK+JqzZkvlJzX1GRGRkq6ByaC+hsWleLu0FaYvQ/UaX f5PNIEjRKqx7RO+KIb0rPuVCg0C2Pb51ehIcef6G/niwVkDPEzZMqeLXx948q3fWyK XgvrIAinHBgCjpIZ1tbPl1kcevztZPsFmEOu3DQfejhPOObA8Dt3lwrEUlW+Lo778R 1a+3VwtHU8u0g== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:39 -0400 Subject: [PATCH v5 01/15] NFSD: cap the number of listeners accepted in listener_set Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-1-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2930; i=jlayton@kernel.org; h=from:subject:message-id; bh=tRmgxww+rxzTihIU6XxzVA4+qqOxvEdXskaEQAJ+8Pg=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWuwZ4cFcqw0XUvOq2am0xKHPMtyIyPRIUJi pAMLWUIxTOJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVrgAKCRAADmhBGVaC FYC0EADBpVMMtxaUlAGjzrJwXHZMesdpcKwSKt2f/0xaBhm7ftFypPczeHWAp/iTgHyf6owbylT oh2n/a1Oz+cUDhS+Xg9H0qq1nemFZCleFG3rKKCEjuFHJrEyF54PsxHSY4GNpIqZ9LurbWqfxep AItZSifA+5j55+VIFtcvrCP0pv2hbgpHijF5Zg9B3cN7u0FXPJvM5hU9KihnmqHquJIEJGbsL9E SzJzV45UM05UccQ1ycKFToEPbUb8a3C5WBy1PRDTciVdwpJOpdFf86oedihNPYyYILcorrZKhHi glQsgEVkyGmdilU+qgMF3/DoVFEUlivaW7i49aavs5VwOuWUrSKfJj6s4WYb82VyUYWOlkuUx3S XjlFpbLPMaeqZ0RvXyAc4lJJObxWXGRaItJIDds6Sa0HSneYvxVrllaCtWQk1t/d5IeRCUKQTCe GyxMU5JsSnV7KZ2GLO5B/fKhc4SB0wdd8watXfi/E65f4MmA/4QExMbJhFaXrIoJmO848JdXnii nQcIKTiMnXiPgIy+iOZCsVM5SWS/WSaepIHkNk34lsgQ5E3CdW2Y6w2/gHTryJS3H+t07oyaYug 08xr7j8r7YhRKEttorXE2v5XJo/hkKsjDHznZOsoglTyFmUY8qkZEtkyAVs2h0LLZP5zYxSIiLo BNvbUqAHHLvvCpA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 nfsd_nl_listener_set_doit() matches each requested listener against the existing set. The nested loop that does this is O(N * M), where N is the requested count and M is the existing count. The loop runs under sv_lock with bottom halves disabled. A userland request with a very large listener list can therefore spin in atomic context for a long time. Reject a request that carries more than NFSD_NL_LISTENER_MAX (1024) entries. The check goes in nfsd_nl_validate_listeners(), before the code takes any lock. The limit is far above any realistic configuration. This patch does not cap M. Only the message size bounded N; real sockets bound M. A listener_set result set is the requested set, so that path also holds M at the cap, but __write_ports_addxprt() adds two listeners per call and removes none, so repeated calls can push M past it. The worst case under sv_lock is therefore 1024 * M, plus 1024 nla_parse_nested() calls. Both interfaces require CAP_NET_ADMIN. Fixes: 16a471177496 ("NFSD: add listener-{set,get} netlink command") Assisted-by: LLM Signed-off-by: Jeff Layton --- fs/nfsd/nfsctl.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 5331b89c4281..b6f4d66f612a 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -1995,21 +1995,22 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, s= truct genl_info *info) return err; } =20 +/* Upper bound on the number of listeners a single request may carry. */ +#define NFSD_NL_LISTENER_MAX 1024 + /** * nfsd_nl_validate_listeners - sanity-check the listener list from userla= nd * @info: netlink metadata and command arguments * - * Walk every NFSD_A_SERVER_SOCK_ADDR attribute and confirm that each entry - * is well-formed: it parses against the policy, carries both an address a= nd - * a transport name, and the address is long enough for its family. Doing - * this up front lets the callers below assume every entry is valid and - * guarantees we make no changes when the request is malformed. + * Walk every NFSD_A_SERVER_SOCK_ADDR attribute and confirm that the list = is + * not oversized and that each entry is well-formed. * * Return: 0 if every entry is valid, or a negative errno otherwise. */ static int nfsd_nl_validate_listeners(struct genl_info *info) { const struct nlattr *attr; + unsigned int count =3D 0; int rem; =20 nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr, @@ -2018,6 +2019,11 @@ static int nfsd_nl_validate_listeners(struct genl_in= fo *info) struct sockaddr *sa; int err; =20 + if (++count > NFSD_NL_LISTENER_MAX) { + NL_SET_ERR_MSG(info->extack, "too many listeners"); + return -E2BIG; + } + err =3D nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr, nfsd_sock_nl_policy, info->extack); if (err < 0) --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DC9662F3C18; Tue, 1 Sep 2026 13:40:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270008; cv=none; b=g2B89950Pp/uGE7V+37C4m9bjoD6RAr6YESHG203VjnChZXcqKRZTdBI5dSEWTfMJ/D922KwlOUVRicNZZrT70y0SYHPwAvYYIyOAcKQFHcnHfFXyyoOgfpraNkSkA6a3GmkT54evth26FLdRPrOpV60yU+QUvAS2RXjeZP1pu8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270008; c=relaxed/simple; bh=aJetiop332TzGMjtNxCwp2osCzOlN1SMdSvmKASa+aI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NXOC8IiC6+cbiIoxRgrYIQoxPaxBhHRex0tdNgNAiHQRtfb69ArL4NSaUeatOZ+zFs0XlGz+JFe2zs7mno7BnHPqV+ltf3ZKZ1Pa4R2DkaVgVt4wIYlW3yIVyPRT70hphg5dsa+HF9k4nmNouSr1JslI0QlkqDSJ6DfaG4IOLsQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BnKW3vZx; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BnKW3vZx" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 33E451F00A3D; Tue, 1 Sep 2026 13:40:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270006; bh=f7DIO5UFdyzmop+0FickQyJR+upbMPxvd7WYzoH5UvM=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=BnKW3vZxrxuHwWNycigqeLIWzN/bOxUePAv20ekKVg76KgBoSO/CBbZap98SVdTAP TOOT3FJ4yUOpqU+VHTt4a0apDE7wd3cOt8Cx2Pmb0eNUPhhkpuLNv2aSFrZnLMYrs3 5ViPncRorCJG8+qMrpSfEMLz8JhPWjXe6pDVtxG1/IEhTrxKJkY9FuHxksW0phQH5w nfaDoiyG+80XdfrK8KawpejLGFFYfc1j6a9SKI1M9pZKlKKwqxwR6dZVT8t05hncEa zYHrIukzZNdvDVF07inmaZhEXuZM/BN+JQ+a451444JdPFrbWHeNO9wjVC/Y5xNs+4 nenTP95lMWdDg== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:40 -0400 Subject: [PATCH v5 02/15] NFSD: validate transport name in listener_set before serv creation Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-2-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2761; i=jlayton@kernel.org; h=from:subject:message-id; bh=aJetiop332TzGMjtNxCwp2osCzOlN1SMdSvmKASa+aI=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWuaHsNrR6zYYfSMArd5NsyaG532BAv1IbcP lDaHurfePSJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVrgAKCRAADmhBGVaC FSz3D/9mEWvGywz0hP/R1Ypu96onbBI6AKRDb48NcEZ6LdvoAVUbRrNw3GEr3fydzFtiQF50XLu zJBrs1AxMLlUvQYklx0Ihe3Zr0xFuYWdU0YKefxx6fkyClVTkDuh+E7MEm3/7FtPYiT9KWK5N5L 7iPFYSH8ax0v880YAmEWSew/+VqIJZRlF7PbxTwq4VsqevLPl8bcXRf67A5k/4s6MvNmEXraweG s7Hojn1Q3j8+bAzRKH3qcKVbHFRI1h8ocdCCZq3e3XMqy5oUyd1ONJRpYPE50nDcTSgpn4g9ips HsDWHCqLPOP1O5HtUqNK4bvlu0vJo1W+Etv5vewWHzJhIXkIc8RrcyUJClZtEgi02zaAfBTYrS4 7ttN9eQdQsClk8bKcKPU3ToKh04KtXJFBkTal6W2NKKOOe8odfoSIXU4s+BoHIIG9b37HczowCE bmVJz+yqB3K/DKIvap/CJOM0c1aCXDsV3da15QJ3+xto8Ray+vQLsmWx5yUhasIIwQ5jdmgUJLk mtibP9A1SRG1Ch1rxxhkZDXdcYVOIUILyTpzHt0VG8QVnfrtyaUHKyWpfRUpW5D1FxMhqRzYFJ2 uGEy2mauYPQrFOtZV/3xmVu1LPG3T2mNAcgl1mPfvd9ahRHVylf4jYCV89wmu6vJigAJQXi/s+G JOibkyb9UzgQIUQ== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 nfsd_nl_listener_set_doit() holds nfsd_mutex for the whole listener teardown and rebuild. The code checks NFSD_A_SOCK_TRANSPORT_NAME for presence only, and not for content. An arbitrary name therefore reaches svc_xprt_create_from_sa(). There, a name that matches no registered class calls request_module("svc%s", name). That call is a TASK_KILLABLE usermode helper upcall, and it runs under nfsd_mutex. Check the name against the classes that NFSD can create: tcp, udp and rdma. The check goes in nfsd_nl_validate_listeners(), which runs before the code takes nfsd_mutex. The rejection names the offending attribute through extack, since -EPROTONOSUPPORT on its own does not say which entry carried the bad name. This narrows the upcall. It does not remove it. NFSD accepts "rdma" without a condition, so on a kernel that does not build svcrdma the name still reaches request_module("svcrdma") under nfsd_mutex. That is necessary for the modular case, where the autoload is legitimate. Fixes: 16a471177496 ("NFSD: add listener-{set,get} netlink command") Assisted-by: LLM Link: https://syzkaller.appspot.com/bug?extid=3Dc7eae0eb80858a2dba0f Signed-off-by: Jeff Layton --- fs/nfsd/nfsctl.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index b6f4d66f612a..d8135f38e69f 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -1995,6 +1995,23 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, st= ruct genl_info *info) return err; } =20 +/* + * Transport classes NFSD knows how to instantiate. Vetting the name here + * keeps a bogus string from reaching svc_xprt_create_from_sa(), where an + * unknown name triggers a request_module("svc%s", name) upcall under + * nfsd_mutex. + */ +static bool nfsd_nl_transport_supported(const char *name) +{ + static const char * const supported[] =3D { "tcp", "udp", "rdma" }; + int i; + + for (i =3D 0; i < ARRAY_SIZE(supported); i++) + if (!strcmp(name, supported[i])) + return true; + return false; +} + /* Upper bound on the number of listeners a single request may carry. */ #define NFSD_NL_LISTENER_MAX 1024 =20 @@ -2032,6 +2049,13 @@ static int nfsd_nl_validate_listeners(struct genl_in= fo *info) if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME]) return -EINVAL; =20 + if (!nfsd_nl_transport_supported(nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]= ))) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NFSD_A_SOCK_TRANSPORT_NAME], + "unsupported transport name"); + return -EPROTONOSUPPORT; + } + sa =3D nla_data(tb[NFSD_A_SOCK_ADDR]); if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(sa->sa_family)) return -EINVAL; --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AA8A9476CCF; Tue, 1 Sep 2026 13:40:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270009; cv=none; b=NzNIaGiG7vgQJZdETOGFmy/TYdzmKJqYKBnJOlB9o8CjfDLBKQI/g9pVnBEbpse7TFI0ROpLbuBGNdx3wLk5xf8n7Y4MLjhUuULQSvKzhT+6vackO5nKK4J+xYTXkTsy8b/MS1NSn45Z5/S4aqr7BfC0s6CxCGOUrAUuEziJQfU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270009; c=relaxed/simple; bh=0/b63pHsH99q59pHXxgWpdCiis0fT0wFTjXendozs0s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=suOHlBePPFdbZho5+2SW1Z16LQ7NyDQTRd7NuhFeOQpvtZVAkMN+OnbG+mbbBGsi7NYmaxLiQaIHrN/q0aFBDVnGfaM7/qAyNZN7Egiy1/3pXghn9ucknrqAb5CFEEqWHYw3GYNNUexL9hmsrtiJramH9lz9jyVuX2cnoJcCIdI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=htSmE64q; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="htSmE64q" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D55E61F00A3E; Tue, 1 Sep 2026 13:40:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270008; bh=yBYuY0qF5cKQRzExECF4YXQMQy4ClFSSmJFRGPrU+CQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=htSmE64q3fC4iWTwmrjYUzjhlxQP+2ZM3ufi3PUvYqLBvyHNKCukNgVV/nxlEWX+W 5mBYTTFexmUJ7ZU1L0jeZD62h6saaAygYcn4Sy5kVbqBrgwFs0j6PG/CpRuvn7YBSt eD0OD3Fe6a85fIw99LuQilH7zBJhuqK5t5OHVBLmuVBcxM1/5mh+NDdLtQDx6yM/rd ZjPghyE9BNEsPFrwkTFoc1SwcSm0TxnyHgi4zyTyulFJOHPNuv4IlRGRwBtGSxNYyz wFAoQM2ihlvOOaUiPHdadjkNZIF1Zn8ZOB+K+OPs9XihOg+5KZyC2b6ajqoTMWiyaF THrinhIQ72+bg== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:41 -0400 Subject: [PATCH v5 03/15] SUNRPC: keep the first error in svc_register() Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-3-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1206; i=jlayton@kernel.org; h=from:subject:message-id; bh=0/b63pHsH99q59pHXxgWpdCiis0fT0wFTjXendozs0s=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWuGrYPAMgfxAOn/iOdOtkSUH386teawscqI YllYO1KZiyJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVrgAKCRAADmhBGVaC FVu3D/9a6ti55iWJsHI0pwGN8SrUgxqjRPylCcyyd2jNm7tU07QTHQ8M9PueVVGY2Disa5ACXka L/ezVVrKL3q6Mt+o3B6t6f139OEs/nFZEfonPatA5ojo5sM3O3K9qWGgdhwH5FZa5qCjXLwidBg 6BtFVH96UqeoVHQTKp4frd/Q984qwJ8eO3ONuhmhUAGJVbdg10D3W3S3GQSb6fzWwaG8tg6kauT MjhH3nnrjINwRp5letgeBPK/ckXpNsQZStnz8BC4aiDVPLCdyDgZDmgtw6O7wrjNlKWqrhyTBC4 ngCth155C3R3ziviO2c9Ump+d1cKKfPxWZ2LNkxHJLrixDSVu7dXWaNGgXhh1N/woNG4lIPQtBw D9gJgkgJgnlXY8gs+kwWR1Tytm2AtU/Vm1ImCQpnYDK90oUL/6WV0DPg74yzYbp6SSLN0c8lCSW /UZRAJIHj2s0zJyR5xVL/TGnfYaZ1zXnaZ1Twr7B6GIoLimwlCxDrYq0xim9bSjg4nRMNeSV6FY AUk5HJFHofAL6blQH+lIEcsX/er0UssDHNOlDJVnHduZc5GNQXOjWvqzTfnNWsSYzq5L45pcr1D qgBrNXx8g/8mzPk9tgEolLfKZbD83uHRRtj5N+FGL8azk7PSGWZC6CmcWh7xVZgHD7pZyn2sJZ0 ivOft0iLXuL8e6Q== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 svc_register() assigns every pg_rpcbind_set() result to the same "error" variable and returns the last one, so a later result erases an earlier failure. Keep the first error instead of the last. Fixes: 642ee6b209c2 ("SUNRPC: Allow further customisation of RPC program re= gistration") Assisted-by: LLM Signed-off-by: Jeff Layton --- net/sunrpc/svc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 8297bad2b177..4f402bbf97ba 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1208,13 +1208,16 @@ int svc_register(const struct svc_serv *serv, struc= t net *net, struct svc_program *progp =3D &serv->sv_programs[p]; =20 for (i =3D 0; i < progp->pg_nvers; i++) { + int ret; =20 - error =3D progp->pg_rpcbind_set(net, progp, i, + ret =3D progp->pg_rpcbind_set(net, progp, i, family, proto, port); - if (error < 0) { + if (ret < 0) { printk(KERN_WARNING "svc: failed to register " "%sv%u RPC service (errno %d).\n", - progp->pg_name, i, -error); + progp->pg_name, i, -ret); + if (!error) + error =3D ret; break; } } --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 69DFD38D40A; Tue, 1 Sep 2026 13:40:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270011; cv=none; b=HrvEjKDPSoX1aXDJ41e/2xRcy/OLdB2hHt3S9hjYALlk/sKKDkIkd+DSV8KI/9XghBAvE64kgWNwtvfkxGFDgqi3tc2XGn1zUpBI3mD1MT2dvi1PW0MPe2fL90t56BFkKbmMKyL4vb2XTjoZUE1ObYUNRVAj4xb4hbISuuliBvU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270011; c=relaxed/simple; bh=hTif5dKJuoIIsVz4dAaSFnhZxibEnCpGcJ3SqDcakyA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=SgjllWiIVeta1JhiPwAh0mBWcBA3bRV5LDdQ5It/Yw08pt5nVUNn3O7IwvbZNRnoO/GXgDYJIleugqN+R1xPeW5AKuUKde70nLl5OtdCzIXtrJGJdW4FpCVJM/2hNzs4JraGdm54fzsxnXrw6AE2HxQc4fNxJeY5xhhNM6amIuM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bwBICul9; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bwBICul9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 81C161F000E9; Tue, 1 Sep 2026 13:40:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270010; bh=NJ+06JLg+zozGY40HFLsiWKPGrZUTJWkSToQO3R8seU=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=bwBICul932Zsbtsv/XyGl/2yyebMpk4MMUl96yL6uKj90NLefrrHzXsabaQMhuY+1 +9aRNXCE4Niij6+WS8JxyTJcOL6COcPP+e+JczZMC87APck59cM0ooP27m8ZqXSj0i PPGhAjl4nC1HPtbTCq3pK9uCy0JdtOHQZbSuAsKsIO9N4qrv51TUdGV1KDTBl9QIX+ eteZ4t1LYYdcaPbBVqb6rHZRy+NnsV7uqepiTSnuiY3rv+ixtEUld7Y9ceLCyQu/HR e7edcS5SN0Iy3yTHcyrgugqV0+oXC0TwXbSMvjtO1VvL8lLGCS5zWSER/uIVzB+Gzl NE2zbWkQFJulg== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:42 -0400 Subject: [PATCH v5 04/15] SUNRPC: bound the local rpcbind client timeout to 1s Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-4-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2890; i=jlayton@kernel.org; h=from:subject:message-id; bh=hTif5dKJuoIIsVz4dAaSFnhZxibEnCpGcJ3SqDcakyA=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWuHjMlPum1euYq58oGh+kqm+UNZq6kTTY5i /tiaRztjLaJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVrgAKCRAADmhBGVaC FbiAD/9pGkVqPgfw/F+jckcU/uWf4blnUMAqYYxhOMJKHvGD0bYECvBQdgPXqJdYBXYReNYbD4O 9E45J1oao1NrUQUFgfxIJWomb9b/TwT+o8mo73zuSwMzRCOsh9zDnDBTAZOmMYDWuO+oxqNvq5M RPGWhFAgtkRpusCPVqQfEGxWVtOXyZm6DZFlmNHBzf/P66rZ/AWDCCGaj6K1G9KgMD/whXqqpAG ZfCOPtj+sYAT99+JxcpHhplkp25eB07ASEYyjV/7EG3VJhPAD/KYvVfSWeyRHSAYNbJYYdlHpJ8 N7Y8gXM7Co4CRvRnt1Wu74w1y4CHY2eBLNECgdTgoh+OhO3KLG6gGkDdYlxwzf8JGrcdS3h3ew/ MsWB/NSCBw6lHqqdCkbPObWRc/72/JfsJj5VnqI8hZvELF2cU9FXj4qT8beupzIBv/hqrsCzHCR +Z9yC4Rko17tkAO+LeBjjRPbOnHvhCqqsuv9XGlDPjjdHpcXDxk3RCh7Hi8zNykb0r93QqbA9Hs lAze2fdGOOgVfV+8/sdNxq+j2JspRVVgVZYmw+rDj48EaOdytnV+77UgU3zQi9TeiW4qmQITxhO qN2Eczsa0D8v6jWD71coF609zSs/q7adFE6YQrts7FVOZyanx1h8vmGN47JP18rOXOeNP6/mlYV B1szT/sWhFlJp5Q== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 The kernel's local rpcbind client uses the transport defaults: a 10s major timeout for AF_LOCAL, and 60s for the loopback TCP fallback. xprt_calc_majortimeo() returns to_initval when to_increment is 0. Those calls are synchronous, and they run under nfsd_mutex. One operation makes several of them. rpcb_create_local() tries up to three client creations, and svc_register() sends one call for each program and version. A local rpcbind that accepts the connection but never replies stalls every one of these calls. The accumulated hold is long enough to trip the hung-task watchdog on other NFSD netlink operations. The holder itself waits killably and escapes the watchdog: INFO: task hung in nfsd_nl_cache_flush_doit The local rpcbind is on loopback or on an AF_LOCAL socket, and it answers in microseconds. Bound its client to one attempt of 1s. This shortens the stall. It does not remove the stall, and it is not free. Registration stays synchronous and stays fatal. An rpcb_create_local() failure aborts nfsd_create_serv() through svc_bind(), and an svc_register() failure makes svc_setup_socket() fail. An rpcbind that is merely slow to be scheduled can therefore now fail server startup, where it succeeded before. The real fix is to make the registration asynchronous. Assisted-by: LLM Link: https://syzkaller.appspot.com/bug?extid=3Dc7eae0eb80858a2dba0f Signed-off-by: Jeff Layton --- net/sunrpc/rpcb_clnt.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 6aa372188c86..0aa376b82a52 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -221,6 +221,16 @@ static void rpcb_set_local(struct net *net, struct rpc= _clnt *clnt, # define SUN_LEN(ptr) (offsetof(struct sockaddr_un, sun_path) \ + 1 + strlen((ptr)->sun_path + 1)) =20 +/* + * The kernel's rpcbind client talks only to the local rpcbind, over loopb= ack + * or a local AF_LOCAL socket, where a healthy rpcbind answers in microsec= onds. + */ +static const struct rpc_timeout rpcb_local_timeout =3D { + .to_initval =3D 1 * HZ, + .to_maxval =3D 1 * HZ, + .to_retries =3D 0, +}; + /* * Returns zero on success, otherwise a negative errno value * is returned. @@ -238,6 +248,7 @@ static int rpcb_create_af_local(struct net *net, .version =3D RPCBVERS_2, .authflavor =3D RPC_AUTH_NULL, .cred =3D current_cred(), + .timeout =3D &rpcb_local_timeout, /* * We turn off the idle timeout to prevent the kernel * from automatically disconnecting the socket. @@ -312,6 +323,7 @@ static int rpcb_create_local_net(struct net *net) .version =3D RPCBVERS_2, .authflavor =3D RPC_AUTH_UNIX, .cred =3D current_cred(), + .timeout =3D &rpcb_local_timeout, .flags =3D RPC_CLNT_CREATE_NOPING, }; struct rpc_clnt *clnt, *clnt4; --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B1DE147CA96; Tue, 1 Sep 2026 13:40:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270012; cv=none; b=RmOxCRhIgn1Rgec9p42zM83BNSpA4zXnW+bNulXI36+vQ+rhotKkw0Qy9Kc2go1DPgBRZyKjI2E0RXW+tLesmUClL+qMJzGgEO32bnxyvepI6becR49Yztj4xxRhVWVRYJ9v4mBeKPDCRo9SvauxWKMtZNpb74CdNpP39YIN5QI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270012; c=relaxed/simple; bh=V+wvIGsJlzSDNv4WKj3Vpj9Bw7A+dysnBkXbj7sTYeg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cLf+46qQcPUaciCCQNgLCPe8gMUJhFvLEkycl1iKgLMEhiRXl9+f+yRtYAlauK3BOcw3HbcPAx72GfdH/EfM6C49iwyfoQ5xO8Vp7O6yMnXSEzMpdB7J8uffaIqQoksEKr815LujMixvaMfnyHAo42fspggMLtX/S2AKIWItw3E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lz8xnOj4; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="lz8xnOj4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2ECD61F00A3D; Tue, 1 Sep 2026 13:40:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270011; bh=dtpz+uXivVuZd9+O4vrtoCG96NBwe0+gdme3nSBwjT8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=lz8xnOj4miZzzoqc7YEOJdn9WNRB3shucdtTQRmMia+1Z3kHvdDpdaweLAvkqCdGC zq41kvj5UX0E//vSc2fNq/2alENiAR0iDkvxAj9tGjoY8lgW+Qx4yHYRt7hbuaSc6G oNc81/XqsIL7aX8ArLNvuOz+9ogslsewpMEbgTOYp6W+Q+fg3tgGGlYLyhQhJZr5NP ZUYlMmF0q4JPcFG5iskc0nWYqVtitGddYppisyqoe81iZ9hSabpvYIdJywD+yeRfks J/fOaK3miXsnEpztiPDn8InHpQuLZQz6GIv3gnVqcnpA6J0xtjFeMjBRmlG7LarCe7 3S5Iw7LReuAmA== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:43 -0400 Subject: [PATCH v5 05/15] NFSD: report listener creation failures through extack Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-5-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2186; i=jlayton@kernel.org; h=from:subject:message-id; bh=V+wvIGsJlzSDNv4WKj3Vpj9Bw7A+dysnBkXbj7sTYeg=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWvSbJyZt5RAZwzkKtgaFUzryFf0hXXEAkMc Bxs6fRmonaJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVrwAKCRAADmhBGVaC FXuhEAC0Cwkuk/JuKyIw798uHhPMOj5S4NPtGsQB4Q5nPLGsY69h4Q+qjp4IRTBMZtNA5TgQko0 rdsU2gV81ReP2W7XSwmiYZEfcFXHB8S2gi0dTal3lGFCAUc6sQ5d3Ve/3yWjmQf+UdPR4rGUFVO 8JiAzK//YFSDjsmEwtmYX22rJx60aSYfSjnrw49FdQEJHz2Mr3tJuHenlUQ7YPEsaI8CV2SND46 ZEm8r0MTDp00EKH0iGJhyvve33WR7wnfMqTDAYUQcDDR+FyESFzW2XH76dYMQOk0cfsEND6HYyU vFQPu3W7COx8NWLdVw39PNAIoMIH4mW14dinkHJ+hn7leXq30bsE8ALzC8GooOyR+R883vygUDm FvrnPU9cfS8/XGGoTUl8A1HjvtiAUc9UF8QN4bJ9NniT5ilz+PW80LvjKwsiXYEViHGUuTtdRL4 yFYOHJogOXFmrPUJAfAJP9QwyfmDexUS5LA6LXifqHpQoy8r2wh6ub+R+Ke0pSbIVnzGXEsZ0n4 5FUknNLlL3ldurR49SHvx/g1bpy8T9//EPhr5SAPKBK1Kd5IaD39OIcQdx467N4a78IZ2n1lbyg QqKdO6DXOdT6c5ysdJUjy5V9lo6qToO8nSEa3WeTm/vSjhjJJfROuWfqlFxia119r3FKX3JqA1v 5lixF5FYIRrHZEg== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 nfsd_nl_listener_set_doit() returns the raw errno from svc_xprt_create_from_sa() and sets no extack. A failed LISTENER_SET therefore tells userland only "Address already in use", or whatever else the transport returned. It never tells userland which entry failed. Record the attribute and the transport name of the entry whose errno the call returns, and report both after the loop. NL_SET_BAD_ATTR() names the entry, which the message alone cannot do: a request can carry several entries with the same transport name. The rejections in nfsd_nl_validate_listeners() other than -E2BIG and the unsupported transport name still carry no extack. This patch does not change them. Assisted-by: LLM Signed-off-by: Jeff Layton --- fs/nfsd/nfsctl.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index d8135f38e69f..6cbdcee4b733 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2089,7 +2089,9 @@ static int nfsd_nl_validate_listeners(struct genl_inf= o *info) int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info) { struct net *net =3D genl_info_net(info); + const struct nlattr *bad_attr =3D NULL; struct svc_xprt *xprt, *tmp; + const char *bad_xprt =3D NULL; const struct nlattr *attr; struct svc_serv *serv; LIST_HEAD(permsocks); @@ -2208,8 +2210,22 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, s= truct genl_info *info) ret =3D svc_xprt_create_from_sa(serv, xcl_name, net, sa, 0, current_cred()); /* always save the latest error */ - if (ret < 0) + if (ret < 0) { + bad_attr =3D attr; + bad_xprt =3D xcl_name; err =3D ret; + } + } + + /* + * The ack carries the errno of the last entry that failed. Point at + * that entry as well, since several entries can share a transport + * name and the errno alone cannot tell them apart. + */ + if (err) { + NL_SET_BAD_ATTR(info->extack, bad_attr); + NL_SET_ERR_MSG_FMT(info->extack, "cannot create %s listener", + bad_xprt); } =20 if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks)) --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 62AB047DF88; Tue, 1 Sep 2026 13:40:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270014; cv=none; b=af0h6QBlh17Anu2yCM6R8jL/mSusCwnq1Fnzggyif7Uz9lyFLeglBNaDrN+i+NzwYhRgnd+rV3OqwKR+WJsBevN9MwUNcr2hTVGsN3pjAFtZAt2p/xX0u3bUM4mGWyI/L5ENRrfjWxzHglRuMfylLg6i8B8mkR4U/U36vYHD0iA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270014; c=relaxed/simple; bh=JLnk8OD5dxPakv5eo5RzRVgOCab5/lnV243h8lP55v4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=khHLWDnt0CjR/iPtu2PDVpWWs6LsSxnspRXqbcWvBANPaPRw37nVLpD/k7BebS2CFHQrvbNBHuJvbCIZa5PmVef5YwnEc9c8lgpxDsbRxT5CYv8FZZqzylapdr0OY7Qzg7D7b+/Y699mUnax7htg4LpUbrvK8CHGH+2Mn462MN0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lTFRokQB; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="lTFRokQB" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CFB061F000E9; Tue, 1 Sep 2026 13:40:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270013; bh=6w8mtWwdFDtZSMDLedR8zw2agxxuoU0s/M9odz3vUz4=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=lTFRokQBzhk3gGTjaY/MlLye+ej4oymwdk+Z5aaSSI2jkQN9xYA72TvA3madpwhak RNokYcKW6SiU7q70OCt3WpXIiFwhIjbnIjRLUv36h11fDh3kGvaBWxN+gQMagVwZAB e5bOTOAq0T1lPcaq/4mqw/QLVYV9S1xEyMhIYu3FxCMESnIICP3cv8UIdZJiZaLjxf ujjzlQxKcOHSSY/ftvgJMbru+F8pzeWTVl/oBhG/kRmuOTUjfFwpvPe1LNb093wR8D WbvE7RdxrtjncAMdRbQWvFSWjieHf6TLKJi15MoAFLt6MNQLmJzoksTocUNngLKvy6 3S2xRXywNgT3w== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:44 -0400 Subject: [PATCH v5 06/15] SUNRPC: report local rpcbind calls that get no answer Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-6-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=8135; i=jlayton@kernel.org; h=from:subject:message-id; bh=JLnk8OD5dxPakv5eo5RzRVgOCab5/lnV243h8lP55v4=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWvcdFKR42SiqaTGrbVRcRcMFoxLZvTVuoUk 2GFaAP9Y4aJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVrwAKCRAADmhBGVaC FTZTEADSjb/3hxPbar4sIa+2KHJjEhnOWKMHMhRe6VpQX7BgY4lvxTW7RF7WZx9cKYxqgnopSRy KvyIZWIjbIMT+ZRSEKglD9PnnpifZJjUxq0leXRlWR+pcC1q4lyHCYBZ07t0fS+Y8v6V8Hxg2lx TijUPrOPstUgy4/ZC/Pi1q59jgJnMu6D56K9Xy6uKkzA9K4WfyWgAa7YiwbPEcBIjmrK7KPjupo aTMFx6xmkTw+WCnS60Ezxk7xI6x6KXRnv3drZr5FC4qGI3GkXIJdk0HVYdm/z5A3QdE6kwezCC4 KIeiy3qRUEZR6PeJZ+OLOp4wEqw+P3vPOwGfbihYMD/PPA1nyF1zyv9giF8ta2/uD5Ucil1a9RG vVrOLtvkhigqa7sD5/kiYk3Ei2QZip4aCUIvdDWK8mkrRdJNCD4eqN7ukgHgdRv/CA8ItRML10d CAkHgY9pu0QDxf79z5WZ21zMd6oKnYFLe58ChhhLFjElFSEQ68tHAWyyQRvU8wmr3cXQv5TAaUV xIq/IN9bL05J6K/84HVraL5651oiSEvthRP8fpoRiXsqDHL4LcwNpmrDtsuE8hHCneOQyCKP5/x 3/YPN+h6ku8juNb/ZqGA1lz7kg2OmB8vf8f87qwevvhE3TubD/Y6858yOl4bubpPugzgH4EU3mE ZQGbluvRgAQaNDQ== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 A caller that creates many listeners in one operation calls svc_register() once for each of them. Every call waits for the local rpcbind on its own, so a rpcbind that never answers costs the caller one timeout per listener. The caller has no way to learn that the first call already failed. Split rpcb_register_call() failures by whether rpcbind answered: - answered: a FALSE reply, or an RPC-level rejection that rpc_decode_header() derives from the reply. -EACCES (FALSE reply or AUTH_ERROR), -EPROTONOSUPPORT, -EPFNOSUPPORT, -EOPNOTSUPP. - never sent: -ENOMEM, -EMSGSIZE, -ERESTARTSYS. - no answer: everything else, i.e. transport errors. Any no-answer error gets represented by -EIO, which is already what the RPC layer reports for most of it: rpc_check_timeout() returns -EIO for a soft timeout without RPC_TASK_TIMEOUT, and call_status() documents -EIO as "shutdown or soft timeout". Keep a count of the number of rpcbind failures in the serv. Later patches will use that to watch for hard rpcbind failures, and alter their behavior accordingly. Assisted-by: LLM Signed-off-by: Jeff Layton --- include/linux/sunrpc/clnt.h | 3 ++- include/linux/sunrpc/svc.h | 7 +++++-- net/sunrpc/rpcb_clnt.c | 22 +++++++++++++++++++--- net/sunrpc/svc.c | 42 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 3c2b8c355ab3..30344c0d6a9d 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -199,7 +199,8 @@ struct rpc_xprt *rpc_task_get_xprt(struct rpc_clnt *cln= t, =20 int rpcb_create_local(struct net *); void rpcb_put_local(struct net *); -int rpcb_register(struct net *, u32, u32, int, unsigned short); +int rpcb_register(struct net *net, u32 prog, u32 vers, int prot, + unsigned short port); int rpcb_v4_register(struct net *net, const u32 program, const u32 version, const struct sockaddr *address, diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 2db1b9ec5658..5fa9417e034d 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -78,6 +78,7 @@ struct svc_serv { unsigned int sv_max_payload; /* datagram payload size */ unsigned int sv_max_mesg; /* max_payload + 1 page for overheads */ unsigned int sv_xdrsize; /* XDR buffer size */ + atomic_t sv_rpcb_failures; /* unanswered rpcbind calls */ struct list_head sv_permsocks; /* all permanent sockets */ struct list_head sv_tempsocks; /* all temporary sockets */ int sv_tmpcnt; /* count of temporary "valid" sockets */ @@ -451,6 +452,7 @@ int sunrpc_set_pool_mode(const char *val); int sunrpc_get_pool_mode(char *val, size_t size); void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net); int svc_bind(struct svc_serv *serv, struct net *net); +unsigned int svc_rpcb_failure_count(struct svc_serv *serv); struct svc_serv *svc_create(struct svc_program *, unsigned int, int (*threadfn)(void *data)); bool svc_rqst_replace_page(struct svc_rqst *rqstp, @@ -471,8 +473,9 @@ unsigned int svc_serv_maxthreads(const struct svc_se= rv *serv); int svc_pool_stats_open(struct svc_info *si, struct file *file); void svc_process(struct svc_rqst *rqstp); void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp); -int svc_register(const struct svc_serv *, struct net *, const int, - const unsigned short, const unsigned short); +int svc_register(struct svc_serv *serv, struct net *net, + const int family, const unsigned short proto, + const unsigned short port); =20 void svc_wake_up(struct svc_serv *); void svc_reserve(struct svc_rqst *rqstp, int space); diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 0aa376b82a52..7255c1e07eec 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -412,7 +412,8 @@ static struct rpc_clnt *rpcb_create(struct net *net, co= nst char *nodename, return rpc_create(&args); } =20 -static int rpcb_register_call(struct sunrpc_net *sn, struct rpc_clnt *clnt= , struct rpc_message *msg, bool is_set) +static int rpcb_register_call(struct sunrpc_net *sn, struct rpc_clnt *clnt, + struct rpc_message *msg, bool is_set) { int flags =3D RPC_TASK_NOCONNECT; int error, result =3D 0; @@ -422,8 +423,23 @@ static int rpcb_register_call(struct sunrpc_net *sn, s= truct rpc_clnt *clnt, stru msg->rpc_resp =3D &result; =20 error =3D rpc_call_sync(clnt, msg, flags); - if (error < 0) - return error; + if (error < 0) { + switch (error) { + /* rpcbind answered; the reply itself carries the error */ + case -EPROTONOSUPPORT: + case -EPFNOSUPPORT: + case -EOPNOTSUPP: + case -EACCES: + /* the call never made it onto the wire */ + case -ENOMEM: + case -EMSGSIZE: + /* the caller is going away; this says nothing about rpcbind */ + case -ERESTARTSYS: + return error; + } + /* anything else, we assume that rpcbind isn't functional */ + return -EIO; + } =20 if (!result) return -EACCES; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 4f402bbf97ba..ca6f90653327 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1179,10 +1179,40 @@ int svc_generic_rpcbind_set(struct net *net, error =3D svc_rpcbind_set_version(net, progp, version, family, proto, port); =20 + /* -EIO means no answer, not a refusal, so vs_rpcb_optnl must keep it. */ + if (error =3D=3D -EIO) + return error; + return (vers->vs_rpcb_optnl) ? 0 : error; } EXPORT_SYMBOL_GPL(svc_generic_rpcbind_set); =20 +/** + * svc_rpcb_failure_count - local rpcbind calls for @serv that got no answ= er + * @serv: RPC service to query + * + * svc_register() adds one for each of its calls that got no answer. A rep= ly + * that refuses one entry does not count, because rpcbind answered and the + * next entry may still succeed. + * + * The count is kept per serv rather than per net. The local rpcbind client + * is per-net and lockd shares it, but a count that another service can mo= ve + * says nothing about this serv's own calls. + * + * This is for callers that cannot see the svc_register() return, because a + * transport class sits in between. Such a caller reads the count before it + * starts and compares as it goes, so there is no state to reset between + * operations. The count never resets, and callers must not attach meaning + * to the value itself. + * + * Return: the number of unanswered calls since this serv was created. + */ +unsigned int svc_rpcb_failure_count(struct svc_serv *serv) +{ + return atomic_read(&serv->sv_rpcb_failures); +} +EXPORT_SYMBOL_GPL(svc_rpcb_failure_count); + /** * svc_register - register an RPC service with the local portmapper * @serv: svc_serv struct for the service to register @@ -1193,10 +1223,11 @@ EXPORT_SYMBOL_GPL(svc_generic_rpcbind_set); * * Service is registered for any address in the passed-in protocol family */ -int svc_register(const struct svc_serv *serv, struct net *net, +int svc_register(struct svc_serv *serv, struct net *net, const int family, const unsigned short proto, const unsigned short port) { + bool noanswer =3D false; unsigned int p, i; int error =3D 0; =20 @@ -1208,10 +1239,16 @@ int svc_register(const struct svc_serv *serv, struc= t net *net, struct svc_program *progp =3D &serv->sv_programs[p]; =20 for (i =3D 0; i < progp->pg_nvers; i++) { + const struct svc_version *vers =3D progp->pg_vers[i]; int ret; =20 ret =3D progp->pg_rpcbind_set(net, progp, i, family, proto, port); + if (ret =3D=3D -EIO) { + noanswer =3D true; + if (vers && vers->vs_rpcb_optnl) + ret =3D 0; + } if (ret < 0) { printk(KERN_WARNING "svc: failed to register " "%sv%u RPC service (errno %d).\n", @@ -1223,6 +1260,9 @@ int svc_register(const struct svc_serv *serv, struct = net *net, } } =20 + if (noanswer) + atomic_inc(&serv->sv_rpcb_failures); + return error; } =20 --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D6D447F2D1; Tue, 1 Sep 2026 13:40:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270016; cv=none; b=cQIzZ96rNYDhtnugS94bKe0lB0KOhyxvh7KBhYO/UsQOTOpu02GsipkiGq/Qr+boCVaMFhtBmfIvWqHg98lhbxi8mbtjXMVFVFojfxCpOc+GVXuhjWvK8MFqdIXx0ZmkRz9BxAg0772QzjtzXax7HME2OvxKXa07v/OAJAHEnQY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270016; c=relaxed/simple; bh=C33LkpUKt/07H9evJ07qv3CM9i/amV7NHBG7uNyOI24=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ML9gIKYRrRi+qoDSfIawx8mtBH9ngmZNcXC5tjsmirEqHNA4JBEUgsMnaqjQP5yHsEgGNYFMfvuzvLTF+XjTc315Hl5suPGTqouJyvFnGPNGp4dSxyYq6xu4Qcw7i6aLQ8XMbO7pAx8rzhmvHBLHIWb1Y2g9kDRQybMKhd2Ez4w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Xrq14D6F; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Xrq14D6F" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7F8921F00A3D; Tue, 1 Sep 2026 13:40:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270015; bh=uu/5w6dnsC16IumNu9SsTj4UaX4P/j02MChTCzfrPns=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Xrq14D6F+DpkYhwRnmq2fPIN4gC108LdFWRW6c6FK90reQSIiXCzliVGMAUCBVyWs 5X9rXuz8pwibiQa6YSOQiROMtqPNbmK3FRmutENgN5wvu7PswyA6zCCMcxfrb5zTc5 lf4M0ryHpsE/5AZ8YBiUr8hveQKjTQC/nqAaKTUjvJZb4BrJPm3rNHWXEFNhYE9ld+ tHzf3NXjfbnwaDgf03JhKLr91EcIK4KcbQ/rmAUAsGRyxaEMqXgteVjZkfjhVfTyvX iiTe0qi4uDck3WhU4VVOxuywdNDFhHbwP9C5g9Lkdc+Ldr5N93aFqYR3O2P3lZ6aBZ TZCdGccYaGc6w== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:45 -0400 Subject: [PATCH v5 07/15] SUNRPC: stop svc_register() once rpcbind stops answering Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-7-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=985; i=jlayton@kernel.org; h=from:subject:message-id; bh=C33LkpUKt/07H9evJ07qv3CM9i/amV7NHBG7uNyOI24=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWvicBGa60Lij2LJ9FxksICclXD28o5vjpwZ UwbwxL8Ku6JAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVrwAKCRAADmhBGVaC FdxCD/9VH4WsjdVWlI+g/i9mAB1SEuUyn4hXVuwaxh0qygq5j1wAj3OT4su98YE55DuvWXAGdXT ehPpmJdqsZ5IaoVtpOfJjqzp+JCbGC7qabsUa2XrVLTxXYDTh09i4KLGGOtTnDybOYiZfhcuu8o s+1SyL1lFNV1ykuVaoywjjM9FyvNaCNn4BAnyWV3OgwY5nO24dnKK2dcWjYJYR27HZTLFnYs6qX ut5UgoOQddrdGQqC368BL1MYA1iFIfM2fL9pO/DzWkMl78qe6XVHEEk8Skp1VtGIJZlcXsm9isj 9/eeBupG4AWgBd4odK82XawbpaLG+9EemzFEKfXVExwzTchrWD8VFXA7ZWLkK+QG/wb0AvYh6iy bGdBxFN9L4LLKODeubtlnhb9/VgFY7dNEvZpS2Flwe1506TnrMJb8p5v4FNXMqWldeiUnM2XFEL kpPJ6XpgmQLTl9dAzasBkCY+ESgCO949qzEO0fsrScKb2nEs+1jemPrC51XeA/cgPEtGMjUywsV XUCAxZb3NeK5BOrk9UVunPMf5VWnmt1i+YvtjpCQpGrUru5i6UYBUe4FmRZMwj7HYBulCEdXVGR 8J5dIpO2nJmGtyN9mhp2nJ9WewyuvZX6oaTTP5SFesaUmZNiLqWJKUMspT0xg1cXx1nLlu0oirW WaZWf3gIWeL25Iw== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 svc_register() walks every program and every version. The break on error leaves the version loop only, so a program whose call to the local rpcbind timed out is followed by the next program timing out in the same way. With the 1s bound on the local rpcbind client, an nfsd serv pays 2s for that, once for nfsd and once for nfsacl. Stop the program loop too, but only when the call got no answer. Assisted-by: LLM Signed-off-by: Jeff Layton --- net/sunrpc/svc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index ca6f90653327..592f2745b028 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1257,7 +1257,13 @@ int svc_register(struct svc_serv *serv, struct net *= net, error =3D ret; break; } + if (noanswer) + break; } + + /* Give up on trying to register anything if it didn't respond */ + if (noanswer) + break; } =20 if (noanswer) --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CC65747F763; Tue, 1 Sep 2026 13:40:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270018; cv=none; b=TwgP5cM470gYn0FO+TkShvNmdbRz0LZ+XjmXPJ6ldX3uFoAGomGkGEX2F08Z9AzHUpvIcvUqGoZi0DvDr157bFuMDlT1w6YvfMmSICgDBvpCOnVgNQ4cvCw8j2iKuqV/70XRX6YK2asD3us0cWSplO3x8LjDyIbMad23uMLpgJc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270018; c=relaxed/simple; bh=OxooBR6zP1EObRUZaoecaq02mYeD5P6mm/aldTuwXlY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fp8+5MrdYnjIobBGPjF13plz+tD4ugqoSIgZ0qIodbrKTemVO31lWJ8l8Mpd1uH9ujtecIaPk245bc84/FBCta7Y+VRmP+KF5RUhK5b8Mnf5a7kY5dw6eQG6gXdXEMtOabJCGhxeBtVFK2zU94s6Itm48M8wa85FZqffFCxVz5o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=f0gWPQCh; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="f0gWPQCh" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2F94C1F00ACF; Tue, 1 Sep 2026 13:40:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270016; bh=i4VG5NqTo3OXGrkAYOASfBWI3LZV+WyKA2uNOsOM3bU=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=f0gWPQCheGTgkZNeOyzxgBX7mT0gjWEyE36a+AdZqsX6TBzq9VKSuc1semShC2Rz1 pAPKseRnhNxENNMVj6F+hfkVYD3e9Y9tzVlOPdvdLKb2Oaeo4Mx3tL8NZ6tx27z+Cc 1zC2Z3gMjrI/5mVb2K6L1AwVU5DnyyG5ckBdQQWEYyK2AV7T2iFZt4Wr/DcGfZsK+7 qBkDlTJn1MWozAvOqEIFpCTc+M+KnFNF8yN6IXaardS5zyjS2lKWvcYx5lQl2xSLBY U0lgawwzzKZfUGvzgFzHK01i/ZwDw864UatjXbourl6gJqvNm0jD34Sj9Ce9Ackner 0dpw/OcKHm2LA== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:46 -0400 Subject: [PATCH v5 08/15] SUNRPC: stop the svc_unregister() sweep once rpcbind stops answering Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-8-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1958; i=jlayton@kernel.org; h=from:subject:message-id; bh=OxooBR6zP1EObRUZaoecaq02mYeD5P6mm/aldTuwXlY=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWvSR+9poLFWt2GoDrViVJeabbe33matKOfR tRHctysdB2JAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVrwAKCRAADmhBGVaC Fd1GD/9AWtvXqIl3cYusIXfUcnawTaTsvPU9qBGSLuCApyljzguaMCNU5/lgllTJqrk2waF8Vya /X0J8IjZbxT/kFIXhyTTV/XTQSQThGekPVqfXnzfsva1ycrlP81pZyTlquGHG+1QpyaJ04HxjVK wd6rgEhvhWmNoz0+XatXxWVaUstAYNE7LbWaYWfK4OwGojVXGzeaSPUPydDFv4lf5c3VST6K/rT 3wrzQNF+DGkdShToHbGHilTY/snPnNtUW87LImdPY4axesYqfO2RVb89R0UIlwwiMwvDmUnIvpI 6YfkIH5sSZiy3qw4ShnhmBOtAQpvCtvVyPjW28tls/IRKQUmmHyHsHlDz8J5xBi0sn9Ii/KWWQV O6+G/BOHsuY26nX+Qnju9whjUxnc8y2QWBN1vpMN0a6gN8VJKpI0idtp16SqIJaDJ+XPKhkUXFI krDxDoliFObucGrhflRjrLEtkOND4cYAj7mGWF99BLulaJt1Lk9CsdVDoJubY+DTSotVr6jsXiK +y7aPZ+2sPQDC52k/KJjWAPrRMceV680aB0dTy9Us8jtC00FPY4d81MyIGA3NXFrfXXGCkVW7N1 ppJDF4ClUK+DutRthAlJqirB+rXdU7VIgECeHM5lgcjKDW1+Y6nsgqVutmN7RIzlGWA3M0CFNt+ A+bg7L1z/jzJqhA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 svc_unregister() clears the rpcbind entry for every non-hidden program and version. svc_rpcb_setup() runs it to drop stale entries when a serv binds, and svc_rpcb_cleanup() runs it when one goes away. An nfsd serv with v3 and v4 enabled sweeps four or five entries, so a local rpcbind that never replies costs that many timeouts, twice per NFSD_CMD_LISTENER_SET, all under nfsd_mutex. Give up after the first call that gets no answer. Assisted-by: LLM Signed-off-by: Jeff Layton --- net/sunrpc/svc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 592f2745b028..f73412e123a1 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1279,8 +1279,8 @@ int svc_register(struct svc_serv *serv, struct net *n= et, * any "inet6" entries anyway. So a PMAP_UNSET should be sufficient * in this case to clear all existing entries for [program, version]. */ -static void __svc_unregister(struct net *net, const u32 program, const u32= version, - const char *progname) +static int __svc_unregister(struct net *net, const u32 program, const u32 = version, + const char *progname) { int error; =20 @@ -1294,6 +1294,7 @@ static void __svc_unregister(struct net *net, const u= 32 program, const u32 versi error =3D rpcb_register(net, program, version, 0, 0); =20 trace_svc_unregister(progname, version, error); + return error; } =20 /* @@ -1320,10 +1321,13 @@ static void svc_unregister(const struct svc_serv *s= erv, struct net *net) continue; if (progp->pg_vers[i]->vs_hidden) continue; - __svc_unregister(net, progp->pg_prog, i, progp->pg_name); + if (__svc_unregister(net, progp->pg_prog, i, + progp->pg_name) =3D=3D -EIO) + goto out; } } =20 +out: rcu_read_lock(); sighand =3D rcu_dereference(current->sighand); spin_lock_irqsave(&sighand->siglock, flags); --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8E13B47FAF8; Tue, 1 Sep 2026 13:40:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270020; cv=none; b=AG0joMtIbycICvuCIkOCh/umpODleYs/xlv1dyEfyC1N0GBCvWM12BNu/9FaLq8riYtilDUBnnp19ZmCjWD/wwsbCsULPzhl3pmvv5xe/PIv8nK4LsfqLv9QmAwjyGHeKuSpiINwjGDWlbJjTot+RYBgbQrjkH7eRcPZw1E3FvM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270020; c=relaxed/simple; bh=1r/MeTdtTiqjG/QVDbFc0lfCvcqVv7N3LlY8u4zmC44=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Q+ciqZMqO/0MbR+/uN157N5YJvp5V/+InDLm8LA4o58+ci5u3bHm3XNJp03Dc1WtdO4r5FywCQ82QylmGfq7OiK2PB9Afxr5KkCNkbMto8LMocY8Dv9pGGVmAnAr+kahFlBj3VXpq6LWuPg8EXmHesmjsYNgpJrAfyG+yGBBC7s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FCh/UC7/; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FCh/UC7/" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D0B6A1F000E9; Tue, 1 Sep 2026 13:40:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270018; bh=YExOASgIJ2/l4OeTeYY2Iv2oz+KB5P8laUEbMe5fIZ8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=FCh/UC7/2h1H+CtPmF1Mv0qiK/frtgAXu80yYVOuc7UVpXk5GEeJJFd2qfm62L1Yc j1vUVv+acaV31vH3e1Sq78JFMlntUphWrgwdGFf0/pozUgeSq8Vt9vROFIw+AuAGYZ 8ff39acWFgCal3f7rc7yDPnzkII9y7MS7/B/L/B00V4pKVFEnsKRbXJXp1ZGu5X4La UwkdAyvBAjhyqWjVviXvtGmY+LqKkYBQfhbOfBQxtnV/xhujuhQxlUM6veM5ny6UOM VpJNSbquAukG+xNm53Vfnqv7OJlb50h+LXIWeFkdD+ijFE3H1lLomEGRLQbmjSfssH C2hroMJwtZwoA== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:47 -0400 Subject: [PATCH v5 09/15] SUNRPC: stop unregistering listeners once rpcbind stops answering Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-9-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2139; i=jlayton@kernel.org; h=from:subject:message-id; bh=1r/MeTdtTiqjG/QVDbFc0lfCvcqVv7N3LlY8u4zmC44=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWwBa2RAcJW2//7w0sglDAH2WRSAt6iNc94Z wcZ0yOjIbyJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVsAAKCRAADmhBGVaC FW5RD/9dznm9oX5bdKYnqQe4Vh7DQ2sVrf9MrQesZ9HWu45xG3iqXtThMd4zfaSKyDR+cR6ySnt TLzMJ3kr5A9CpGIcxR1Rhz3gSL96WU/OJg4P7PyExS6E6UCQPoIT1bW7NxZGPzaBs8nFnmzcOr5 q8cmYGeYLRYEE6SJi1GqJ7QG/UUgK6fjtgPWPBWCYb56LqAmd88v1saZc6ZPpr0qgWJKj2gqmIx nw2WRam0tvauBwPMCJllBIkNNYS7+3dB7nGf5211wB0pZF6qffixVReIsLpW3Fb0MXLFfRKOV2U +kTREUH0tjFF4hGWg4n5fSQNZs4+awdFTzKdsnRvA4w+VE5UWzW16CqRApfpfFNeGwysBt9Td+7 ERYuFXm6+9l7vDeRPRQHWwEfzyDWYgcBhdlgjPgeviIenRbFj+/USxDVXQ2UDCI5n9uopMvH+9G 3dyUSbBKIa15gqxRVOiO6ClXqwcsKxYsFBFQhpd97FPvsMQMPSzinwSmqcXvRyc2yzudPtxMA4N okKvl1DqAbrib1JVPkvwwhvOKSsBbFhLgjocQLHZzWIt4atw2oOsur/HMuhGLDAn6QYuC/sBP4y Ip30srxyo7bFcaS8wfrNUM13/rlMWlO927q+i0+bL4hwHR8h3PmlbXpCyGdE/t49WpTyliR9ioE 52ejg7KIoLdXQ4A== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 svc_delete_xprt() unregisters each listener it destroys. One NFSD_CMD_LISTENER_SET that removes listeners therefore pays one local rpcbind timeout for each of them, under nfsd_mutex, on top of the one the create loop already bounds. One failure is enough to know that the rest of the teardown will not fare better. When the call gets no answer, clear XPT_RPCB_UNREG on every remaining transport in the same net. Assisted-by: LLM Signed-off-by: Jeff Layton --- net/sunrpc/svc_xprt.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 40040af588fb..ef35a09b74d7 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -1101,6 +1101,23 @@ static void call_xpt_users(struct svc_xprt *xprt) spin_unlock(&xprt->xpt_lock); } =20 +/* + * If rpcbind stops answering, every listener still to be destroyed would + * only wait out the same timeout again. Drop the flag on the rest of this + * teardown, which is every listener already marked for close. + */ +static void svc_xprt_clear_rpcb_unreg(struct svc_serv *serv, struct net *n= et) +{ + struct svc_xprt *xprt; + + spin_lock_bh(&serv->sv_lock); + list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) + if (xprt->xpt_net =3D=3D net && + test_bit(XPT_CLOSE, &xprt->xpt_flags)) + clear_bit(XPT_RPCB_UNREG, &xprt->xpt_flags); + spin_unlock_bh(&serv->sv_lock); +} + /* * Remove a dead transport */ @@ -1115,11 +1132,15 @@ static void svc_delete_xprt(struct svc_xprt *xprt) struct svc_sock *svsk =3D container_of(xprt, struct svc_sock, sk_xprt); struct socket *sock =3D svsk->sk_sock; + unsigned int failures =3D svc_rpcb_failure_count(serv); =20 if (svc_register(serv, xprt->xpt_net, sock->sk->sk_family, sock->sk->sk_protocol, 0) < 0) pr_warn("failed to unregister %s with rpcbind\n", xprt->xpt_class->xcl_name); + + if (svc_rpcb_failure_count(serv) !=3D failures) + svc_xprt_clear_rpcb_unreg(serv, xprt->xpt_net); } =20 if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags)) --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5457547F78E; Tue, 1 Sep 2026 13:40:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270021; cv=none; b=agXnUhFzVERLo1Osb/dfB5lgOPtmg5V1BI8DUl2/mOFPJS1ftJL2fAzeH0vBfeAGwVjYoi/zqwVi4NhNkRejIppaL6mt4QDRcy/krR+y65+Zayhjp1viFhtK0e9QPI/tOTXN4/P1OlzBDJ4EGJCbUSJGruryiPXRFcJSWDxNsRM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270021; c=relaxed/simple; bh=f2Z8k6GcgoIUj3Olxr0Qxt0ELIlZxuGOVQEcHdPGh1g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ky2CkEu5e4vbhWfjw8rFI9fpuM/J6jCehul6gmkOWWKO30Mo57+DsKeXkKM2a/sDBj+vMd5Dboj42YVweR6VmoDdkgzeKBh0t2UsQZWEkbyndVcY4y3eQCtlBafPuTMmRuipHRne+saqX9aSyeyPD0zfgHhMiNurmiaJ26Lu53c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kaNuO9JM; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kaNuO9JM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7D7761F00A3D; Tue, 1 Sep 2026 13:40:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270019; bh=bfaSv13nbLfzYE4zUFuADA/CvpnLYYvbl3M88GgwW30=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=kaNuO9JM4xr1DJ20MFc9TmDYX4AyS6qhN142JY2zZu3fDki19302w+etFQWdlZDCC RCjWHEx6M/mp1kWOXWgem0RfGExbA4EN11k9X+AyYCgLdanBtklPbYmqx+zM93VY61 VpQYo2Xsl1kMuR8WsnkqP8LflSwBGDksppmiPK74MrByvbQPP2eM4UKV2zl04NxqS7 N7p31exiXQPyRp2+yP3mkXXmQH9l5qpg6IIXmfUXNdQVKsTyRTeYk9htf0B2jsDg65 GRjuhXT4OSkPqtaM8pvV39qyJ4tGlTez86U7EEzOx4el8JzIP3ZSUn5g/5z4xfTC9/ 0UXx/1DBY81/g== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:48 -0400 Subject: [PATCH v5 10/15] NFSD: stop registering with rpcbind after a failure in listener_set Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-10-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=3924; i=jlayton@kernel.org; h=from:subject:message-id; bh=f2Z8k6GcgoIUj3Olxr0Qxt0ELIlZxuGOVQEcHdPGh1g=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWwJHTytUPCbGK+b5wNAESiX3eTe8WJoABlh 6dmPT93eiGJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVsAAKCRAADmhBGVaC FYUYEACIDrKkWyCBPkdTQTM7iGwqpHqALvbicbkvJnW/C6QmtK91mlVB7tnPDDDLITAL6Kd42Pq 0fp5mtrh3KFE3jlws90p5j5CGofjBCR0+Alz2gr4qyDCYCWk+ug0kLFG5bBuqNK2rz0f8EXRnWQ pdcmKCY2cv46OE9dz6kT7IBPw0g9ssBGeV7zxSA40gT3lPa5yZVk/f6w97DOI/6u1J1mV48FNfZ 5jzvJcyhlfUDs7B2FbTwJMvY7WJm1+C3Ivup9PUWhlUCCwSOSopTq2GVpKemwjf/U8Fc+9cpARY Wvp3ckUnCj6JIOA3ZBdyuKYctSLRnhwq7jV3Vjh6tfT73gdI6ScIFxX7dODK13MOwqwS+5jn3bT lIS4vf1I0cNtbI1pFFL5U/njgQhELVzs+lgN2EPzaVN5BDsgqSIRxtnh4Exi2qSPvXD53ID2ZF+ UA7+3besq6gD+LzedvXprQsNfS822Y/BLSMskFvlpG6EfqGH+ewS2cs5biV/jHbQCnZRo9fRgGu YUib/v/EIDjDII5cTHaYh882AT0Lk5CRqv4qN3eaob6aQu8Yi3lwdw+r5sJG8WAj64q/AmJpSvQ dlPjqtt07Y3zgKoQQuVpxoHpD46qBfj7fKng1vQVMDxfAA8SW42Md80cxWhAhDuIHqtS1IrbiK7 uRc0oxCPY8YA2bA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 nfsd_nl_listener_set_doit() calls svc_xprt_create_from_sa() once for each requested listener and passes flags of 0, so every listener registers with rpcbind on its own. A rpcbind that accepts the connection and never replies therefore costs one timeout for each entry. With the cap of 1024 entries the request can hold nfsd_mutex for about 34 minutes, which is roughly 17 times the hung-task threshold. One failure is enough to know that the next call will not fare better. Read svc_rpcb_failure_count() before the create loop, and pass SVC_SOCK_ANONYMOUS for the rest of the request once the count moves. Fixes: 16a471177496 ("NFSD: add listener-{set,get} netlink command") Assisted-by: LLM Link: https://syzkaller.appspot.com/bug?extid=3Dc7eae0eb80858a2dba0f Suggested-by: Olga Kornievskaia Signed-off-by: Jeff Layton --- fs/nfsd/nfsctl.c | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 6cbdcee4b733..c6f6bc3b1281 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2092,7 +2092,10 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, s= truct genl_info *info) const struct nlattr *bad_attr =3D NULL; struct svc_xprt *xprt, *tmp; const char *bad_xprt =3D NULL; + unsigned int rpcb_failures; const struct nlattr *attr; + bool skipped_rpcb =3D false; + bool bad_rpcb =3D false; struct svc_serv *serv; LIST_HEAD(permsocks); struct nfsd_net *nn; @@ -2182,13 +2185,16 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, = struct genl_info *info) if (delete) svc_xprt_destroy_all(serv, net, false); =20 + rpcb_failures =3D svc_rpcb_failure_count(serv); + /* walk list of addrs again, open any that still don't exist */ nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr, GENL_HDRLEN, rem) { struct nlattr *tb[NFSD_A_SOCK_MAX + 1]; const char *xcl_name; struct sockaddr *sa; - int ret; + bool hit_rpcb; + int flags, ret; =20 /* validated up front in nfsd_nl_validate_listeners() */ if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr, @@ -2207,12 +2213,27 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, = struct genl_info *info) continue; } =20 - ret =3D svc_xprt_create_from_sa(serv, xcl_name, net, sa, 0, + flags =3D skipped_rpcb ? SVC_SOCK_ANONYMOUS : 0; + ret =3D svc_xprt_create_from_sa(serv, xcl_name, net, sa, flags, current_cred()); + + hit_rpcb =3D false; + if (!skipped_rpcb && + svc_rpcb_failure_count(serv) !=3D rpcb_failures) { + skipped_rpcb =3D true; + hit_rpcb =3D true; + if (ret < 0) + ret =3D svc_xprt_create_from_sa(serv, xcl_name, + net, sa, + SVC_SOCK_ANONYMOUS, + current_cred()); + } + /* always save the latest error */ if (ret < 0) { bad_attr =3D attr; bad_xprt =3D xcl_name; + bad_rpcb =3D hit_rpcb; err =3D ret; } } @@ -2224,8 +2245,21 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, s= truct genl_info *info) */ if (err) { NL_SET_BAD_ATTR(info->extack, bad_attr); - NL_SET_ERR_MSG_FMT(info->extack, "cannot create %s listener", - bad_xprt); + if (bad_rpcb) + NL_SET_ERR_MSG_FMT(info->extack, + "cannot create %s listener; rpcbind did not answer", + bad_xprt); + else if (skipped_rpcb) + NL_SET_ERR_MSG_FMT(info->extack, + "cannot create %s listener; rpcbind did not answer earlier, so som= e listeners are not registered", + bad_xprt); + else + NL_SET_ERR_MSG_FMT(info->extack, + "cannot create %s listener", + bad_xprt); + } else if (skipped_rpcb) { + NL_SET_ERR_MSG(info->extack, + "rpcbind did not answer, some listeners are not registered"); } =20 if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks)) --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E931848122C; Tue, 1 Sep 2026 13:40:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270023; cv=none; b=GcsyJUaTFt3jX3HZ7IFUfJ34ybo7DkbARocQL3kfv8Gp0Y/A2v79BIx9NzB2rBjrNxT9gdrSIyfqDuSqKjdS9b8UrQOEf3KvBu3ex5jXU7Dkq05BEfVm4eedpVMSG8vKLsXpz65DKoeiaGuOG3F9bqAJdZC3kM/Mu11wtbNP3AY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270023; c=relaxed/simple; bh=0ZW6OKmjiCCSRO6Fjj9N+K8OBoj3fMRkesMtY3+fw6Q=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=unmwM9a8pPeM/qDGMcZEA7H+eo7LNwrMbhVvDzZGSjXVnjton7QXEYOF58KBLq29orNoJWoFHlQ+OT1wLmrnX1jyhXNAJSP11GU3oRZKlj85MnhapMbo7gNP66dx5k31NSClabAU1E9gtriWqZx7MUtfYwUXfdqUWyLVGaKTaUc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BXpltTdF; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BXpltTdF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2A6721F00A3F; Tue, 1 Sep 2026 13:40:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270021; bh=XSLyqYMtkJhmknYEk7fox4brPcvHsyc4eE1EzL2dIX0=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=BXpltTdFfbCL10QZDOq2YsVdG8oJ0TDdHuFfl39VSZNEZX9dkrCcgWm3VZo+co8Nz KQyUYWz1Bt4W97W+h6t2QWUIJqXGP6161BzRtHESXMv0XzNPOou/P9XlXtO2pn8XKv pD0JvqZtxOLQEQdwZocikSYCE4ZuNl4gjREGEIxQUV1A05msZDMoKDtd3uFHPBS3BR wU6ICMgayirQ87MkynoxjQTrAs1Tp2bA18j0yxzqqp9zdxwOu0d8LPD0fIrhcmXVQR TB5IiwQTQygiAIGBu3zHDBNeFQABWJSB2BKD0t7hqmp9MQep7hjlSirCVQTi5f2ndT w7+bX1Mo91Exg== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:49 -0400 Subject: [PATCH v5 11/15] SUNRPC: check rpc_sockaddr2uaddr() for failure when registering Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-11-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1171; i=jlayton@kernel.org; h=from:subject:message-id; bh=0ZW6OKmjiCCSRO6Fjj9N+K8OBoj3fMRkesMtY3+fw6Q=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWwPXSjtVcQ0LEyV5Mj3Kv/bLSpNGQp1wRjU pYzs1HP8TyJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVsAAKCRAADmhBGVaC FTygEADVcH3u0KK4da5JRenYyMehx+L3tdUOnBhzK1hR+PAqZ+jWNyAKc8oogJTkY+Q+W/m8UKk tVAP46TC/UxFOYoEShfH4tGAvV7pPaQZc0x2bVm7dDhH1sxQVKUqej31FXKSxwDDks+MkWepm19 cOjf5ClbqNzPeuLnCdZ6Urgm3tlYQ8067Gas0Om6DF0DToYNevhv50DxCCTNosRmsX/l5jdaYaj s/2L/y9eFkW9GD2Fwto2LbTpfOUaOIfJSsGF1dOlicS8Z1T2qpJgJia8nQWcu4PAwNQjsvnFRad 8z1bNNyYXb1WT8p7Jbecfr0zMkqGx0LwoGs86LEwYjUmxWIEBC2CK/5a9zCQUmII6KmvltsQJr1 dRLuilx8fhYKIg6eL5BQAwY7cQbcrOtoZ1SYQ40CZ1IlT3k4U2UL5xNR7/iFWm4nKLC5guzj6bF 4moFooc9RFGOxRj3vP5zBLgl8vPDezlD7trnrBx7Unj7e5mNmGQ3uHhNEn/oe2nAdBpz2qwZ+pG 0bhabFpIGZKloTgfN9wkG7rUWtKcZGQbf2xa8XW60z2lszY2mQazUNn0EglKAY4uJDK8xVjCwfF 6Wqt6VXTbTwJYBFGg9/XOzufJuh5ZdxBgkkc2I5JoNHcnn8rakssuq3OyLgmks00rr/JHqNa65q l/lPkrFqWXNGL2A== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 rpcb_register_inet4() and rpcb_register_inet6() store the universal address unchecked. rpc_sockaddr2uaddr() returns NULL when its GFP_KERNEL kstrdup() fails, and encode_rpcb_string() then calls strlen() on it. Return -ENOMEM instead. Fixes: ba809130bc26 ("SUNRPC: Remove duplicate universal address generation= ") Assisted-by: LLM Signed-off-by: Jeff Layton --- net/sunrpc/rpcb_clnt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 7255c1e07eec..397919f2a38b 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -518,6 +518,8 @@ static int rpcb_register_inet4(struct sunrpc_net *sn, int result; =20 map->r_addr =3D rpc_sockaddr2uaddr(sap, GFP_KERNEL); + if (!map->r_addr) + return -ENOMEM; =20 msg->rpc_proc =3D &rpcb_procedures4[RPCBPROC_UNSET]; if (port !=3D 0) { @@ -544,6 +546,8 @@ static int rpcb_register_inet6(struct sunrpc_net *sn, int result; =20 map->r_addr =3D rpc_sockaddr2uaddr(sap, GFP_KERNEL); + if (!map->r_addr) + return -ENOMEM; =20 msg->rpc_proc =3D &rpcb_procedures4[RPCBPROC_UNSET]; if (port !=3D 0) { --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5A017481AB0; Tue, 1 Sep 2026 13:40:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270025; cv=none; b=WCco8kNiZACj1cw8sx/IqTtO+ZEfQZGO5OeYrMr4MKo3aextAkGnyB2ZgCeENgy1sv1HWMv0/rVCjJsekB4XEeg41IMuKNKM5huZAe+SO6cNRO9/9gr9zDRM7TWO129ZX1oNz9ZcvpvyDrw46t4AN+RdyaWeVlV7TZKQ76d0AXE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270025; c=relaxed/simple; bh=6LMiA8mZvHsz+RymjS4B9eebkdsaJAs7V9akPoHYta0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cwURUxU/ySclHc/jbYN8RJqwjxjdtc2whSUXs/vGuqC/44nyl3PdQYc8BCfdxuRK06XkwBy54rosb360lWPay3arU2HSnDyxMC+Oue/YMDE1j2ANZVSnPlOd6igsa/fXRtbORm0Up4J1ZCvao5RylAzLpX2WSadvY3wGA7HPLLM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GajHdl23; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="GajHdl23" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CB8151F00ADE; Tue, 1 Sep 2026 13:40:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270023; bh=EGHZbsOfha4CZ7pzhSsbuJgOe/cYVATnpueirOLJkv4=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=GajHdl23po/gmcidSXoCSFqwTWgmzoR+duhYlBrffSddp2YiVRUs4+daF4yOoxW0G RAtzIPBVPB7uBD/ZEeuQU1kIsys68kAHQ5XA5Dg+TZYtpoDR40NVr1997TxgAwht+N MMSMAlV/o4faSeQCiBnoiAgT9iBS/R8/HSbNS2PV087myOU5SVxYwcKNZhA8kME6Ls qONLtox9kS1tYkUVOPu3GT86tkMnpAwZcum2PU2uwLD6ERazANTi7oqVUOMTrvuFsy 45G4vwtlY0VYGqpAWdvHFgSCvtoH5fH3mm2ksDDz+g1iszR8DTu3G5Ft4yRdCJdCU+ WB+5Jd0g5RgCw== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:50 -0400 Subject: [PATCH v5 12/15] selftests/nfsd: exercise listener_set request validation Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-12-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=19205; i=jlayton@kernel.org; h=from:subject:message-id; bh=6LMiA8mZvHsz+RymjS4B9eebkdsaJAs7V9akPoHYta0=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWwlAGlgxFViCsQmJQF9zsTUOf9MHw2WFk3w CZhJllj5QeJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVsAAKCRAADmhBGVaC FXh7D/90vF51AOZmtOsMfjI+8x3W7z1xVVEqOZdIloj9lC1AAeZHxustDcDiQV5djxD4AYM2RfQ IFlFdcpZM7f3/fiz8O/M1FE8OrdHvb07pa93vz4m6B/fZsgDFnLz2w/u/WTuODU/varFiPl6t+8 w8NiC4GJkdP5UHnk0DAZ4ESpqIIZ/O1wyn3Ud2fao3eGH8xGBJbBzlbiDvfuX2NHa7UgmGJxqb+ yHCeH36vVCKIEffaOvT5we/+WM1iSijnptKe47CEiFrd7TNQ9ys9z2jKrw86RXFXDh/AOcawRWy rRV+qq2PJtm5HVvOICj3SxR9JRIcnj65DtSPu7zrrvvDSlxo/9pNtffOnW+PxWy6M7YpKli6SV5 w/oJcLR1pq4m+sS5v7XuocrdsUbk6d95KbDV2K7mzVgbisoHcpY48JmZlRuLZ2UWGTN0Afa8cD5 gt7al0Cs3o0Z347SKeIq6fewQTsWyE3d7FyRj4+vl1Zp+ZTPejyDfZu02tdmmhKMHyqaIYN+lC8 yyExwFS4ZNmFb9gGgXrEjpiURuB6WPOIe14V7/qIdTuT8TjEUG0GUUcdtqiXk7bKvnR9raJxOry vWdBR0/qIbb3VXHA59Y+pwmSes7KqrKV/SydujqUfIzfSXw3J6jpMdidKq0Y+DdG+srJGuIozXL 5BGiUFPJsWdBZYA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Add regression tests for the NFSD_CMD_LISTENER_SET checks that nfsd_nl_validate_listeners() runs before the code takes nfsd_mutex. The tests cover a bad transport name, an absent transport name, a missing address, a truncated or unsupported sockaddr, a bad address family, a malformed entry behind a well-formed one, and more than NFSD_NL_LISTENER_MAX entries. One more test sends a LISTENER_GET to an empty netns. None of these requests reach nfsd_create_serv(), so nothing here creates a serv or registers with rpcbind. The tests that do need a serv come next, with a stub. The tests use kselftest_harness.h, so each test runs in its own net and mount namespace. /run is masked there. unix_find_bsd() resolves by inode and takes no struct net, so a connect to "/var/run/rpcbind.sock" from this netns would otherwise reach the rpcbind on the host. svc_rpcb_setup() opens with a call to svc_unregister(), which would then clear the host's nfsd registrations. The config fragment must therefore cover the fixture as well as nfsd: NAMESPACES and NET_NS for the unshare(), SHMEM and TMPFS for the mask, and UNIX for the AF_LOCAL rpcbind client. Without them, every test skips. Add the new directory to the NFSD MAINTAINERS entry, which does not cover tools/testing/selftests/ today. Assisted-by: LLM Signed-off-by: Jeff Layton --- MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + tools/testing/selftests/nfsd/.gitignore | 1 + tools/testing/selftests/nfsd/Makefile | 6 + tools/testing/selftests/nfsd/config | 14 + .../testing/selftests/nfsd/nfsd_netlink_listener.c | 490 +++++++++++++++++= ++++ tools/testing/selftests/nfsd/settings | 1 + 7 files changed, 514 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index be63cb3844db..9d97df56cad6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14163,6 +14163,7 @@ F: include/uapi/linux/nfsd/ F: include/uapi/linux/sunrpc/ F: net/sunrpc/ F: tools/net/sunrpc/ +F: tools/testing/selftests/nfsd/ =20 KERNEL NFSD BLOCK and SCSI LAYOUT DRIVER R: Christoph Hellwig diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Mak= efile index c62642302c84..edaf3c932011 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -89,6 +89,7 @@ TARGETS +=3D net/packetdrill TARGETS +=3D net/ppp TARGETS +=3D net/rds TARGETS +=3D net/tcp_ao +TARGETS +=3D nfsd TARGETS +=3D nolibc TARGETS +=3D pci_endpoint TARGETS +=3D pcie_bwctrl diff --git a/tools/testing/selftests/nfsd/.gitignore b/tools/testing/selfte= sts/nfsd/.gitignore new file mode 100644 index 000000000000..19e6dec04d8e --- /dev/null +++ b/tools/testing/selftests/nfsd/.gitignore @@ -0,0 +1 @@ +nfsd_netlink_listener diff --git a/tools/testing/selftests/nfsd/Makefile b/tools/testing/selftest= s/nfsd/Makefile new file mode 100644 index 000000000000..15ac65549d25 --- /dev/null +++ b/tools/testing/selftests/nfsd/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +CFLAGS +=3D $(KHDR_INCLUDES) -Wall + +TEST_GEN_PROGS :=3D nfsd_netlink_listener + +include ../lib.mk diff --git a/tools/testing/selftests/nfsd/config b/tools/testing/selftests/= nfsd/config new file mode 100644 index 000000000000..0eef03af3503 --- /dev/null +++ b/tools/testing/selftests/nfsd/config @@ -0,0 +1,14 @@ +CONFIG_NAMESPACES=3Dy +CONFIG_NET_NS=3Dy +CONFIG_SHMEM=3Dy +CONFIG_TMPFS=3Dy +CONFIG_UNIX=3Dy +CONFIG_INET=3Dy +CONFIG_IPV6=3Dy +CONFIG_MULTIUSER=3Dy +CONFIG_PROC_FS=3Dy +CONFIG_FILE_LOCKING=3Dy +CONFIG_INOTIFY_USER=3Dy +CONFIG_SUNRPC=3Dy +CONFIG_NFSD=3Dy +CONFIG_NFSD_V4=3Dy diff --git a/tools/testing/selftests/nfsd/nfsd_netlink_listener.c b/tools/t= esting/selftests/nfsd/nfsd_netlink_listener.c new file mode 100644 index 000000000000..3d2f0e3ee8d9 --- /dev/null +++ b/tools/testing/selftests/nfsd/nfsd_netlink_listener.c @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Regression tests for the NFSD generic-netlink listener interface + * (NFSD_CMD_LISTENER_SET / NFSD_CMD_LISTENER_GET). + * + * These cover the request validation that nfsd_nl_validate_listeners() do= es + * before nfsd_mutex is taken: bad or absent transport name, missing addre= ss, + * truncated or unsupported sockaddr, oversized list. None of them reach + * nfsd_create_serv(), so nothing here creates a serv or talks to rpcbind. + * + * Each test runs in its own private net + mount namespace (unshare in + * FIXTURE_SETUP). /run is masked there: a pathname AF_LOCAL connect is not + * scoped by the network namespace, since unix_find_bsd() resolves by inode + * and takes no struct net, so the kernel's rpcbind client would otherwise= be + * able to reach the rpcbind running on the host. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest_harness.h" + +#define NLA_ALIGN4(len) (((len) + 3) & ~3) +#define TEST_PORT 20049 +#define MAX_LISTENERS 8 +#define RECV_TIMEO_SEC 30 + +static int nfsd_family; /* set per-test in FIXTURE_SETUP */ + +static void die(const char *msg) +{ + perror(msg); + exit(1); +} + +/* ------------------- minimal generic-netlink plumbing ------------------= - */ + +static int genl_open(void) +{ + struct sockaddr_nl sa =3D { .nl_family =3D AF_NETLINK }; + struct timeval tv =3D { .tv_sec =3D RECV_TIMEO_SEC }; + int fd =3D socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + + if (fd < 0) + die("socket(NETLINK_GENERIC)"); + if (bind(fd, (void *)&sa, sizeof(sa)) < 0) + die("bind(netlink)"); + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + return fd; +} + +/* Append an attribute at @off; return the new (aligned) offset. */ +static int put_attr(char *buf, int off, uint16_t type, + const void *data, int len) +{ + struct nlattr *na =3D (void *)(buf + off); + + na->nla_type =3D type; + na->nla_len =3D NLA_HDRLEN + len; + if (len) + memcpy(buf + off + NLA_HDRLEN, data, len); + return off + NLA_ALIGN4(NLA_HDRLEN + len); +} + +/* Build a genl message header into @buf; return the offset past it. */ +static int genl_hdr(char *buf, uint16_t type, uint16_t flags, uint8_t cmd) +{ + struct nlmsghdr *nlh =3D (void *)buf; + struct genlmsghdr *gnl =3D (void *)(buf + NLMSG_HDRLEN); + + memset(buf, 0, NLMSG_HDRLEN + GENL_HDRLEN); + nlh->nlmsg_type =3D type; + nlh->nlmsg_flags =3D flags; + nlh->nlmsg_seq =3D 1; + gnl->cmd =3D cmd; + gnl->version =3D 1; + return NLMSG_HDRLEN + GENL_HDRLEN; +} + +/* Send an nfsd command with an ACK; return the ACK errno (<=3D 0). */ +static int genl_request(uint8_t cmd, const char *attrs, int attrs_len) +{ + char buf[1 << 20], rbuf[4096]; + struct nlmsghdr *nlh =3D (void *)buf; + int fd =3D genl_open(); + int off, n, ret; + + off =3D genl_hdr(buf, nfsd_family, NLM_F_REQUEST | NLM_F_ACK, cmd); + if (attrs_len) { + memcpy(buf + off, attrs, attrs_len); + off +=3D attrs_len; + } + nlh->nlmsg_len =3D off; + + if (send(fd, buf, off, 0) < 0) + die("send(genl)"); + + n =3D recv(fd, rbuf, sizeof(rbuf), 0); + if (n < 0) + ret =3D (errno =3D=3D EAGAIN || errno =3D=3D EWOULDBLOCK) ? -ETIMEDOUT := -errno; + else if (((struct nlmsghdr *)rbuf)->nlmsg_type =3D=3D NLMSG_ERROR) + ret =3D ((struct nlmsgerr *)NLMSG_DATA(rbuf))->error; + else + ret =3D 0; + close(fd); + return ret; +} + +/* Send a command and return the full reply message; -errno on failure. */ +static int genl_request_reply(uint8_t cmd, char *rbuf, size_t rlen) +{ + char buf[256]; + struct nlmsghdr *nlh =3D (void *)buf; + int fd =3D genl_open(); + int off, n, ret; + + off =3D genl_hdr(buf, nfsd_family, NLM_F_REQUEST, cmd); + nlh->nlmsg_len =3D off; + + if (send(fd, buf, off, 0) < 0) + die("send(genl reply)"); + + n =3D recv(fd, rbuf, rlen, 0); + if (n < 0) + ret =3D (errno =3D=3D EAGAIN || errno =3D=3D EWOULDBLOCK) ? -ETIMEDOUT := -errno; + else if (((struct nlmsghdr *)rbuf)->nlmsg_type =3D=3D NLMSG_ERROR) + ret =3D ((struct nlmsgerr *)NLMSG_DATA(rbuf))->error; + else + ret =3D n; + close(fd); + return ret; +} + +/* Resolve the "nfsd" genl family id; -1 if not registered. */ +static int genl_resolve_nfsd(void) +{ + char buf[1024], rbuf[4096]; + struct nlmsghdr *nlh =3D (void *)buf; + struct nlmsghdr *rh =3D (void *)rbuf; + struct nlattr *na; + int fd, off, left, id =3D -1; + + fd =3D genl_open(); + off =3D genl_hdr(buf, GENL_ID_CTRL, NLM_F_REQUEST, CTRL_CMD_GETFAMILY); + off =3D put_attr(buf, off, CTRL_ATTR_FAMILY_NAME, + NFSD_FAMILY_NAME, sizeof(NFSD_FAMILY_NAME)); + nlh->nlmsg_len =3D off; + + if (send(fd, buf, off, 0) < 0) + die("send(GETFAMILY)"); + if (recv(fd, rbuf, sizeof(rbuf), 0) < 0) + die("recv(GETFAMILY)"); + close(fd); + + if (rh->nlmsg_type =3D=3D NLMSG_ERROR) + return -1; + + na =3D (void *)((char *)NLMSG_DATA(rh) + GENL_HDRLEN); + left =3D rh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; + while (left >=3D (int)NLA_HDRLEN) { + if (na->nla_type =3D=3D CTRL_ATTR_FAMILY_ID) { + id =3D *(uint16_t *)((char *)na + NLA_HDRLEN); + break; + } + left -=3D NLA_ALIGN4(na->nla_len); + na =3D (void *)((char *)na + NLA_ALIGN4(na->nla_len)); + } + return id; +} + +/* ------------------- listener request builders ------------------- */ + +/* Fine-grained control for negative tests: any field can be omitted/malfo= rmed. */ +struct raw_listener { + const char *xprt; /* NULL -> omit NFSD_A_SOCK_TRANSPORT_NAME */ + int emit_addr; /* 0 -> omit NFSD_A_SOCK_ADDR */ + const void *addr; + int addr_len; /* bytes to emit for NFSD_A_SOCK_ADDR */ +}; + +static int put_raw_listener(char *buf, int off, const struct raw_listener = *r) +{ + struct nlattr *nest =3D (void *)(buf + off); + int inner =3D off + NLA_HDRLEN; + + if (r->emit_addr) + inner =3D put_attr(buf, inner, NFSD_A_SOCK_ADDR, r->addr, r->addr_len); + if (r->xprt) + inner =3D put_attr(buf, inner, NFSD_A_SOCK_TRANSPORT_NAME, + r->xprt, strlen(r->xprt) + 1); + nest->nla_type =3D NFSD_A_SERVER_SOCK_ADDR | NLA_F_NESTED; + nest->nla_len =3D inner - off; + return off + NLA_ALIGN4(nest->nla_len); +} + +/* Well-formed loopback listener for @family (AF_INET or AF_INET6). */ +static int put_listener_af(char *buf, int off, const char *xprt, int famil= y, + uint16_t port) +{ + struct sockaddr_storage ss =3D {0}; + struct raw_listener r =3D { .xprt =3D xprt, .emit_addr =3D 1, .addr =3D &= ss }; + + if (family =3D=3D AF_INET6) { + struct sockaddr_in6 *s6 =3D (void *)&ss; + + s6->sin6_family =3D AF_INET6; + s6->sin6_port =3D htons(port); + s6->sin6_addr =3D in6addr_loopback; + r.addr_len =3D sizeof(*s6); + } else { + struct sockaddr_in *s4 =3D (void *)&ss; + + s4->sin_family =3D AF_INET; + s4->sin_port =3D htons(port); + s4->sin_addr.s_addr =3D htonl(INADDR_LOOPBACK); + r.addr_len =3D sizeof(*s4); + } + return put_raw_listener(buf, off, &r); +} + +static int put_listener(char *buf, int off, const char *xprt, uint16_t por= t) +{ + return put_listener_af(buf, off, xprt, AF_INET, port); +} + +/* ------------------- LISTENER_GET parsing ------------------- */ + +struct listener_ent { + char xprt[16]; + int family; + uint16_t port; + struct in_addr a4; + struct in6_addr a6; +}; + +static int parse_listener_get(const char *rbuf, int len, + struct listener_ent *out, int max) +{ + const struct nlmsghdr *nlh =3D (const void *)rbuf; + const struct nlattr *na; + int left, count =3D 0; + + (void)len; + na =3D (const void *)(rbuf + NLMSG_HDRLEN + GENL_HDRLEN); + left =3D nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; + + while (left >=3D (int)NLA_HDRLEN) { + int alen =3D na->nla_len; + + if ((na->nla_type & NLA_TYPE_MASK) =3D=3D NFSD_A_SERVER_SOCK_ADDR && + count < max) { + const struct nlattr *in =3D (const void *)((char *)na + NLA_HDRLEN); + int ileft =3D alen - NLA_HDRLEN; + struct listener_ent *e =3D &out[count]; + + memset(e, 0, sizeof(*e)); + while (ileft >=3D (int)NLA_HDRLEN) { + const void *d =3D (const char *)in + NLA_HDRLEN; + int t =3D in->nla_type & NLA_TYPE_MASK; + + if (t =3D=3D NFSD_A_SOCK_TRANSPORT_NAME) { + strncpy(e->xprt, d, sizeof(e->xprt) - 1); + } else if (t =3D=3D NFSD_A_SOCK_ADDR) { + const struct sockaddr_storage *ss =3D d; + + e->family =3D ss->ss_family; + if (ss->ss_family =3D=3D AF_INET) { + const struct sockaddr_in *s =3D d; + + e->a4 =3D s->sin_addr; + e->port =3D ntohs(s->sin_port); + } else if (ss->ss_family =3D=3D AF_INET6) { + const struct sockaddr_in6 *s =3D d; + + e->a6 =3D s->sin6_addr; + e->port =3D ntohs(s->sin6_port); + } + } + ileft -=3D NLA_ALIGN4(in->nla_len); + in =3D (const void *)((char *)in + NLA_ALIGN4(in->nla_len)); + } + count++; + } + left -=3D NLA_ALIGN4(alen); + na =3D (const void *)((char *)na + NLA_ALIGN4(alen)); + } + return count; +} + +/* ------------------- convenience wrappers ------------------- */ + +static int listener_set(const char *attrs, int len) +{ + return genl_request(NFSD_CMD_LISTENER_SET, attrs, len); +} + +/* Fetch the current listeners; returns count (>=3D0) or -errno. */ +static int listener_get(struct listener_ent *out, int max) +{ + char rbuf[8192]; + int n =3D genl_request_reply(NFSD_CMD_LISTENER_GET, rbuf, sizeof(rbuf)); + + if (n < 0) + return n; + return parse_listener_get(rbuf, n, out, max); +} + +/* --------------------------- fixture --------------------------- */ + +FIXTURE(nfsd_listener) { + int placeholder; +}; + +FIXTURE_SETUP(nfsd_listener) +{ + struct ifreq ifr =3D {0}; + struct stat st; + int s; + + if (geteuid() !=3D 0) + SKIP(return, "must be run as root"); + if (unshare(CLONE_NEWNET | CLONE_NEWNS) < 0) + SKIP(return, "unshare(NEWNET|NEWNS): %s", strerror(errno)); + if (mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL) < 0) + SKIP(return, "mount(/ private): %s", strerror(errno)); + + /* + * Keep the kernel's rpcbind client inside this namespace. The + * abstract socket it tries first is per-netns, but the + * "/var/run/rpcbind.sock" fallback is not, so hide the path. + */ + if (mount("tmpfs", "/run", "tmpfs", 0, NULL) < 0) + SKIP(return, "mount(tmpfs on /run): %s", strerror(errno)); + if (lstat("/var/run", &st) =3D=3D 0 && S_ISDIR(st.st_mode) && + mount("tmpfs", "/var/run", "tmpfs", 0, NULL) < 0) + SKIP(return, "mount(tmpfs on /var/run): %s", strerror(errno)); + + /* + * Bring loopback up so listener binds (127.0.0.1 / ::1) work. Root + * without CAP_NET_ADMIN in this netns gets -EPERM here, so skip. + */ + s =3D socket(AF_INET, SOCK_DGRAM, 0); + ASSERT_GE(s, 0); + strcpy(ifr.ifr_name, "lo"); + if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { + close(s); + SKIP(return, "SIOCGIFFLAGS(lo): %s", strerror(errno)); + } + ifr.ifr_flags |=3D IFF_UP | IFF_RUNNING; + if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) { + close(s); + SKIP(return, "SIOCSIFFLAGS(lo): %s", strerror(errno)); + } + close(s); + + nfsd_family =3D genl_resolve_nfsd(); + if (nfsd_family < 0) + SKIP(return, "nfsd genl family not found (modprobe nfsd?)"); +} + +FIXTURE_TEARDOWN(nfsd_listener) +{ +} + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D validat= ion / negative =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D */ + +TEST_F(nfsd_listener, val_too_many) +{ + static char attrs[1 << 20]; + int i, off =3D 0; + + for (i =3D 0; i < 1025; i++) /* > NFSD_NL_LISTENER_MAX (1024) */ + off =3D put_listener(attrs, off, "udp", TEST_PORT); + EXPECT_EQ(-E2BIG, listener_set(attrs, off)); +} + +TEST_F(nfsd_listener, val_missing_addr) +{ + char attrs[64]; + struct raw_listener r =3D { .xprt =3D "tcp", .emit_addr =3D 0 }; + int off =3D put_raw_listener(attrs, 0, &r); + + EXPECT_EQ(-EINVAL, listener_set(attrs, off)); +} + +TEST_F(nfsd_listener, val_missing_transport) +{ + struct sockaddr_in s4 =3D { .sin_family =3D AF_INET, .sin_port =3D htons(= TEST_PORT) }; + struct raw_listener r =3D { .xprt =3D NULL, .emit_addr =3D 1, + .addr =3D &s4, .addr_len =3D sizeof(s4) }; + char attrs[64]; + int off =3D put_raw_listener(attrs, 0, &r); + + EXPECT_EQ(-EINVAL, listener_set(attrs, off)); +} + +/* + * A name matching no transport class must be refused before nfsd_mutex is + * taken, so it never reaches svc_xprt_create_from_sa() and its + * request_module("svc%s", name) upcall. + */ +TEST_F(nfsd_listener, val_bad_transport) +{ + char attrs[64]; + int off =3D put_listener(attrs, 0, "bogus_xprt", TEST_PORT); + + EXPECT_EQ(-EPROTONOSUPPORT, listener_set(attrs, off)); +} + +TEST_F(nfsd_listener, val_addr_too_short) +{ + unsigned char tiny =3D 0; + struct raw_listener r =3D { .xprt =3D "tcp", .emit_addr =3D 1, + .addr =3D &tiny, .addr_len =3D 1 }; + char attrs[64]; + int off =3D put_raw_listener(attrs, 0, &r); + + EXPECT_EQ(-EINVAL, listener_set(attrs, off)); +} + +TEST_F(nfsd_listener, val_inet_short) +{ + struct sockaddr_in s4 =3D { .sin_family =3D AF_INET, .sin_port =3D htons(= TEST_PORT) }; + struct raw_listener r =3D { .xprt =3D "tcp", .emit_addr =3D 1, .addr =3D = &s4, + .addr_len =3D sizeof(sa_family_t) + 2 }; + char attrs[64]; + int off =3D put_raw_listener(attrs, 0, &r); + + EXPECT_EQ(-EINVAL, listener_set(attrs, off)); +} + +TEST_F(nfsd_listener, val_inet6_short) +{ + struct sockaddr_in6 s6 =3D { .sin6_family =3D AF_INET6, .sin6_port =3D ht= ons(TEST_PORT) }; + struct raw_listener r =3D { .xprt =3D "tcp", .emit_addr =3D 1, .addr =3D = &s6, + .addr_len =3D sizeof(struct sockaddr_in) }; + char attrs[64]; + int off =3D put_raw_listener(attrs, 0, &r); + + EXPECT_EQ(-EINVAL, listener_set(attrs, off)); +} + +TEST_F(nfsd_listener, val_bad_family) +{ + struct sockaddr_storage ss =3D { .ss_family =3D AF_UNIX }; + struct raw_listener r =3D { .xprt =3D "tcp", .emit_addr =3D 1, .addr =3D = &ss, + .addr_len =3D sizeof(struct sockaddr_in) }; + char attrs[64]; + int off =3D put_raw_listener(attrs, 0, &r); + + EXPECT_EQ(-EAFNOSUPPORT, listener_set(attrs, off)); +} + +TEST_F(nfsd_listener, val_second_entry_bad) +{ + struct sockaddr_storage ss =3D { .ss_family =3D AF_UNIX }; + struct raw_listener bad =3D { .xprt =3D "tcp", .emit_addr =3D 1, .addr = =3D &ss, + .addr_len =3D sizeof(struct sockaddr_in) }; + char attrs[128]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + + off =3D put_raw_listener(attrs, off, &bad); + /* The whole request is rejected during validation; nothing applied. */ + EXPECT_EQ(-EAFNOSUPPORT, listener_set(attrs, off)); +} + +/* LISTENER_GET with no serv in this netns returns an empty list. */ +TEST_F(nfsd_listener, func_get_empty) +{ + struct listener_ent got[MAX_LISTENERS]; + + EXPECT_EQ(0, listener_get(got, MAX_LISTENERS)); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/nfsd/settings b/tools/testing/selftest= s/nfsd/settings new file mode 100644 index 000000000000..6091b45d226b --- /dev/null +++ b/tools/testing/selftests/nfsd/settings @@ -0,0 +1 @@ +timeout=3D120 --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 098A4481FC2; Tue, 1 Sep 2026 13:40:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270027; cv=none; b=UxUejMOyTZuqr+i+dvzV/i/8lG+9r2kHgm/8kaR0bkd8pcZ0LJqGphJIjcavjIy6C4BUYaT1z8gbeAQ1Y+Qsg27R9CHRteWnZbrOdwk77uN4U+ycSqTmw5R4oeuIBrTrmQeedDNTCN6lCQS9LLMejfXqzLNEmWsD903Tm3PwqRI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270027; c=relaxed/simple; bh=mDhAmZ8cXpFxO5erFE879uBMJj9MnA7zxhfGhfSEDes=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=nVkwlSGj8ACTycC9Q5UVZyByVwFXVy8iio5F1PlMWcUQbmlgyEFFM+LE7ho/2xnB4vxIyg4tWbZDjtZzsNtA20coizcls21zg+/U4qjS6C1YAvLKjhqJSLasJNZVhUxUw3jHo4EBxqz0vlKI1oosJGg7iKbKzZm2qcCRbPoXO9U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Dlq4J8ef; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Dlq4J8ef" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7940D1F00A3D; Tue, 1 Sep 2026 13:40:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270024; bh=75DLPq2phiDPM6QP/kcxsw7yLQHu6cTwztRq9l+GBqw=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Dlq4J8ef8N+LPM55uHdTp4S/0CNXVYDOKgB0Yx9cnKqBsfbigSIAyFKniV1ajpyGv YXLAX9H1nvO/yEQjjGP7Fhxfj8PhdSix1gDkY8DZuMGoLxe5EPqrCqVPCKubr5/Il7 vnNRyf+8EyyKdmwnIO802SIprORqTnYJmHaP1vqDVk6RqUrvdjSATr+3EWJja1Hts7 /qtUjUPa24UxuH5hiOCVLu0OQqqzCX5ChGeFsR4Mu/CeMaltH5bvopzhnX1FzlSIGM wECRPs+BCRmWNJepGNo9TinHfATPWjyKLD4qOK4dxGpMSa/wPB9OBEl+IouS5qHzo4 sefQymqxmHQ2w== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:51 -0400 Subject: [PATCH v5 13/15] selftests/nfsd: add a per-netns rpcbind stub and the listener round-trips Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-13-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=29774; i=jlayton@kernel.org; h=from:subject:message-id; bh=mDhAmZ8cXpFxO5erFE879uBMJj9MnA7zxhfGhfSEDes=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWxvvJGf6ukgcZLXcBZ6PQiqnVlJwYgIb800 tV8r/SZUbyJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVsQAKCRAADmhBGVaC FWATEADBAQRpNmhTpgk1114yWfo9Xf8RmtuRI/XTMfSfW0L05+xR0eyhACri23JOt9zYqmQVKV1 5sNZ0pVIhbALYq9xTjIXaeh8KEjyd0TYtO26UyUWPKIOqqGdh00E/r0N81YfRcrd8Sf1GTRHMV0 4VNIKgXrF2+k8lo7ig9qSInkdBWu4721u3swXeUnI7Lp5BKaQHO+j+hsJtRvs5ZXm71BHpJr0O+ OOFAMtUdHu9whB040+uDDLQh5/HHTyxtr2YkQsEML5T8bw+5rpoSSLM90Ebf1IQ2C7oCD7hKwJd 7VwW+1xze96Vbgbein7sNqWKznSwMu4E4VRTONeWKm1wBJ/tOZd5BqK0GyJMyFZK67EnASHI7qf hciuZjIbarI8uGhGbS+puoiK4rTh7Vsn88gmTgue7ngyejhCXKFEtEnNd+4a84q5qVyyf3g4YZf Z9iCqIiBhD0zigqR9Rh0IHALjlMBGrOqYr7zDXaUELOuQmadqJxXl8BHKZiueVCVfyeNhjIKB90 EiOciJdHXF2MQK0pbcGgc6zZX7ECziN09VDezy4OagEPaGRFpz6BTCmdmAFglINXpQlq9lGVo2t iTB3sc33LpuotJjkIr8SIWf8G43r2X5n3WUyluGednh8qfNjruRAHWLM6vkF/0LTRS0CBHkFXgc xKkVj47/CRkXcBA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 The creation of a listener registers with rpcbind. svc_xprt_create_from_sa() passes flags of 0, so pmap_register is true in svc_setup_socket(). A fresh netns has no rpcbind. Every registration therefore waits out the local rpcbind timeout, once for each program and version, and the registration failure then takes the listener down. The host's rpcbind is not an option either. svc_rpcb_setup() opens with a call to svc_unregister(), which would clear the host's nfsd entries. Serve rpcbind from inside the namespace instead. The abstract AF_LOCAL name that the kernel tries first is per-netns, because unix_find_abstract() takes a struct net. Bind "\0/run/rpcbind.sock" and fork a minimal responder: - the responder never decodes arguments. The NULL procedure gets an empty success, and SET and UNSET get TRUE. - the responder answers RPCBVERS_4 as well as RPCBVERS_2. A v4 refusal makes __svc_rpcb_register6() return -EAFNOSUPPORT, which would leave every IPv6 listener unregistered. - the responder counts accepted connections and received calls in a page that it shares with the test, and reads its mode from that page on every call. The mode lives there rather than in the child so that a test can change it with a serv already up: killing and restarting the stub would close the connection the kernel holds, and rpcb_register_call() issues UNSET over AF_LOCAL with RPC_TASK_NOCONNECT, so the next call would fail at once with -ENOTCONN instead of waiting out a timeout. - PR_SET_PDEATHSIG plus an explicit kill in FIXTURE_TEARDOWN make sure that no stub outlives its test. With the stub in place, add the tests that need a serv. These cover the create, add and remove paths and the LISTENER_GET round trips: tcp, udp, several listeners at once, an idempotent re-set, the removal of a subset, the destruction of a serv from an empty list, and IPv6. Two more tests cover the empty-list request and the -EBUSY refusal after THREADS_SET has started threads. The netlink socket asks for NETLINK_EXT_ACK, so that the tests can read the extack message. It also asks for NETLINK_CAP_ACK, so that the kernel does not echo the request back and the TLVs sit at a fixed offset. FIXTURE_TEARDOWN drops whatever a test left running. A listener holds a reference to the netns, and that netns outlives the test process. Threads pin the listeners. Anything left up therefore leaks the namespace. Several of these tests exist to catch a revert, not to describe the interface. The errno alone shows none of them: - val_reject_keeps_listeners. An unknown transport name ends in -EPROTONOSUPPORT either way, because svc_xprt_create_from_sa() returns that error too. The difference is the state that it leaves. Without the up-front check, nfsd_nl_listener_set_doit() has already destroyed the listeners that did not match by the time the name fails. - val_bad_transport. This test now also requires that the stub saw no traffic. A request that reaches svc_xprt_create_from_sa() has run nfsd_create_serv(). svc_bind() then pings rpcbind at client creation and sweeps stale entries with svc_unregister(). Silence at the stub is therefore what shows that the kernel refused the request up front. - val_second_entry_bad. This test now also sends a LISTENER_GET. svc_xprt_create_from_sa() also returns -EAFNOSUPPORT for the bad entry, and the doit keeps the listeners that it did create. The well-formed tcp entry ahead of it would otherwise still be up. - sem_register_refused. This test puts the stub in a mode that answers RPCBPROC_SET with FALSE. rpcb_register_call() turns that answer into -EACCES, svc_register() then fails, and svc_setup_socket() creates no listener. The bare errno does not show that, because a bind can return -EACCES too, so the test reads the listener set back and requires that it is empty. - sem_create_failure_extack. This test squats on the port first, so the listener cannot bind. The extack must then name the transport that failed. - func_empty_destroys. This test uses the connection count. LISTENER_GET replies empty both for a destroyed serv and for a live serv with no permsocks. But only nfsd_destroy_serv() reaches svc_xprt_destroy_all(..., unregister=3Dtrue) -> svc_rpcb_cleanup() -> rpcb_put_local(), which drops the last user and shuts the local client down. The next serv has to connect again. - sem_busy_on_change and sem_busy_on_remove read the listeners back, because -EBUSY says nothing about what the doit did before it returned. find_listener() matches the address as well as the transport, the family and the port. Every listener here is created on loopback, so a reply that names 0.0.0.0 has to fail. Assisted-by: LLM Signed-off-by: Jeff Layton --- .../testing/selftests/nfsd/nfsd_netlink_listener.c | 635 +++++++++++++++++= +++- 1 file changed, 625 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/nfsd/nfsd_netlink_listener.c b/tools/t= esting/selftests/nfsd/nfsd_netlink_listener.c index 3d2f0e3ee8d9..736691ce9a20 100644 --- a/tools/testing/selftests/nfsd/nfsd_netlink_listener.c +++ b/tools/testing/selftests/nfsd/nfsd_netlink_listener.c @@ -3,30 +3,41 @@ * Regression tests for the NFSD generic-netlink listener interface * (NFSD_CMD_LISTENER_SET / NFSD_CMD_LISTENER_GET). * - * These cover the request validation that nfsd_nl_validate_listeners() do= es - * before nfsd_mutex is taken: bad or absent transport name, missing addre= ss, - * truncated or unsupported sockaddr, oversized list. None of them reach - * nfsd_create_serv(), so nothing here creates a serv or talks to rpcbind. + * Three groups: + * validation - malformed/abusive LISTENER_SET requests are rejected by + * nfsd_nl_validate_listeners(), before nfsd_mutex is take= n. + * functional - create/add/remove listeners and verify LISTENER_GET + * reflects the set (round-trip of transport + addr:port). + * semantics - once threads are running (THREADS_SET) a listener change + * is refused with -EBUSY. * * Each test runs in its own private net + mount namespace (unshare in * FIXTURE_SETUP). /run is masked there: a pathname AF_LOCAL connect is not * scoped by the network namespace, since unix_find_bsd() resolves by inode * and takes no struct net, so the kernel's rpcbind client would otherwise= be - * able to reach the rpcbind running on the host. + * able to reach the rpcbind running on the host. Anything that creates a + * serv is served by the per-netns rpcbind stub below instead. */ #define _GNU_SOURCE #include +#include #include +#include +#include #include #include #include #include #include +#include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -40,7 +51,10 @@ #define MAX_LISTENERS 8 #define RECV_TIMEO_SEC 30 =20 -static int nfsd_family; /* set per-test in FIXTURE_SETUP */ +static int nfsd_family =3D -1; /* set per-test in FIXTURE_SETUP */ + +/* Extack message from the last genl_request(); empty if there was none. */ +static char last_extack[128]; =20 static void die(const char *msg) { @@ -55,15 +69,50 @@ static int genl_open(void) struct sockaddr_nl sa =3D { .nl_family =3D AF_NETLINK }; struct timeval tv =3D { .tv_sec =3D RECV_TIMEO_SEC }; int fd =3D socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + int on =3D 1; =20 if (fd < 0) die("socket(NETLINK_GENERIC)"); if (bind(fd, (void *)&sa, sizeof(sa)) < 0) die("bind(netlink)"); setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + /* + * Ask for extack, and cap the ack so the request is not echoed back: + * the TLVs then always follow the fixed part of the error message. + */ + setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &on, sizeof(on)); + setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on)); return fd; } =20 +/* Stash the extack message of an ack, if it carries one. */ +static void parse_extack(const char *rbuf) +{ + const struct nlmsghdr *nlh =3D (const void *)rbuf; + const struct nlattr *na; + int off, left; + + last_extack[0] =3D '\0'; + if (nlh->nlmsg_type !=3D NLMSG_ERROR || + !(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) + return; + + off =3D NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(struct nlmsgerr)); + left =3D nlh->nlmsg_len - off; + na =3D (const void *)(rbuf + off); + + while (left >=3D (int)NLA_HDRLEN) { + if ((na->nla_type & NLA_TYPE_MASK) =3D=3D NLMSGERR_ATTR_MSG) { + strncpy(last_extack, (const char *)na + NLA_HDRLEN, + sizeof(last_extack) - 1); + last_extack[sizeof(last_extack) - 1] =3D '\0'; + return; + } + left -=3D NLA_ALIGN4(na->nla_len); + na =3D (const void *)((const char *)na + NLA_ALIGN4(na->nla_len)); + } +} + /* Append an attribute at @off; return the new (aligned) offset. */ static int put_attr(char *buf, int off, uint16_t type, const void *data, int len) @@ -110,13 +159,16 @@ static int genl_request(uint8_t cmd, const char *attr= s, int attrs_len) if (send(fd, buf, off, 0) < 0) die("send(genl)"); =20 + last_extack[0] =3D '\0'; n =3D recv(fd, rbuf, sizeof(rbuf), 0); - if (n < 0) + if (n < 0) { ret =3D (errno =3D=3D EAGAIN || errno =3D=3D EWOULDBLOCK) ? -ETIMEDOUT := -errno; - else if (((struct nlmsghdr *)rbuf)->nlmsg_type =3D=3D NLMSG_ERROR) + } else if (((struct nlmsghdr *)rbuf)->nlmsg_type =3D=3D NLMSG_ERROR) { ret =3D ((struct nlmsgerr *)NLMSG_DATA(rbuf))->error; - else + parse_extack(rbuf); + } else { ret =3D 0; + } close(fd); return ret; } @@ -320,10 +372,296 @@ static int listener_get(struct listener_ent *out, in= t max) return parse_listener_get(rbuf, n, out, max); } =20 +/* + * Every listener these tests create comes from put_listener_af(), so the + * address is always loopback. Match on it too: without that, a reply that + * gave the right transport and port on the wrong address (0.0.0.0, say) + * would pass. + */ +static struct listener_ent *find_listener(struct listener_ent *e, int n, + const char *xprt, int family, + uint16_t port) +{ + int i; + + for (i =3D 0; i < n; i++) { + if (e[i].family !=3D family || e[i].port !=3D port || + strcmp(e[i].xprt, xprt)) + continue; + if (family =3D=3D AF_INET6) { + if (memcmp(&e[i].a6, &in6addr_loopback, sizeof(e[i].a6))) + continue; + } else if (e[i].a4.s_addr !=3D htonl(INADDR_LOOPBACK)) { + continue; + } + return &e[i]; + } + return NULL; +} + +/* Start (@n > 0) or stop (@n =3D=3D 0) nfsd threads in this netns. */ +static int threads_set(int n) +{ + char attrs[64]; + uint32_t v =3D n; + int off =3D put_attr(attrs, 0, NFSD_A_SERVER_THREADS, &v, sizeof(v)); + + return genl_request(NFSD_CMD_THREADS_SET, attrs, off); +} + +/* ------------------- per-netns local rpcbind stub ------------------- */ + +/* + * Creating a listener registers with rpcbind: nfsd_nl_listener_set_doit() + * passes no SVC_SOCK_ANONYMOUS for the first entry of a request, so + * pmap_register is true in svc_setup_socket(). The fixture's server has v3 + * enabled, and nfsd_version3 does not set vs_rpcb_optnl, so a failure the= re + * comes back out of svc_register() and takes the listener down with it. + * With nothing listening, every attempt first waits out the local rpcbind + * timeout. The abstract AF_LOCAL name the kernel tries first is per-netns + * (unix_find_abstract() takes a struct net), so answer it here and stay o= ut + * of the host's rpcbind. + * + * Arguments are never decoded. The NULL procedure gets an empty success a= nd + * SET/UNSET get TRUE, for both RPCBVERS_2 and RPCBVERS_4. v4 has to be + * answered because __svc_rpcb_register6() turns a v4 refusal into + * -EAFNOSUPPORT, which would leave every IPv6 listener unregistered. + * + * In RPCB_STUB_REFUSE mode SET is answered FALSE instead, which + * rpcb_register_call() reports as -EACCES. UNSET is left alone: only + * svc_unregister() issues it, and it discards the result. + * + * The stub also keeps counters and the mode in a page shared with the tes= t, so + * a test can assert that the kernel never talked to rpcbind at all, or th= at it + * dropped the local rpcbind client and had to reconnect. + * + * The mode lives there rather than in the child so that a test can change= it + * with a serv already up. Killing and restarting the stub would close the + * connection the kernel holds, and rpcb_register_call() issues UNSET over + * AF_LOCAL with RPC_TASK_NOCONNECT, so the next call would fail at once w= ith + * -ENOTCONN instead of waiting out a timeout. + */ +#define RPCB_PROGRAM 100000 +#define RPCB_PROC_NULL 0 +#define RPCB_PROC_SET 1 +#define RPCB_PROC_UNSET 2 +#define RPCB_ABSTRACT_NAME "/run/rpcbind.sock" +#define RPCB_STUB_MAXCONN 4 + +enum { RPCB_STUB_ACCEPT, RPCB_STUB_REFUSE }; + +struct rpcb_stub_stats { + unsigned int conns; /* connections accepted */ + unsigned int calls; /* calls received */ + unsigned int mode; /* RPCB_STUB_*, read on every call */ +}; + +static volatile struct rpcb_stub_stats *rpcb_stats; /* MAP_SHARED */ + +static int rpcb_stats_alloc(void) +{ + void *p =3D mmap(NULL, sizeof(*rpcb_stats), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + if (p =3D=3D MAP_FAILED) + return -1; + rpcb_stats =3D p; + return 0; +} + +/* + * The stub bumps these before it replies and the kernel waits for that re= ply, + * so whatever a netlink request provoked is visible once it returns. + */ +static int rpcb_calls(void) +{ + return rpcb_stats ? (int)rpcb_stats->calls : 0; +} + +static int rpcb_conns(void) +{ + return rpcb_stats ? (int)rpcb_stats->conns : 0; +} + +/* Takes effect on the stub's next call; the caller has not sent one yet. = */ +static void rpcb_stub_set_mode(int mode) +{ + rpcb_stats->mode =3D mode; +} + +static int rpcb_stub_listen(void) +{ + struct sockaddr_un sun =3D { .sun_family =3D AF_UNIX }; + size_t nlen =3D strlen(RPCB_ABSTRACT_NAME); + socklen_t alen; + int fd; + + /* Abstract names are length-delimited, so the length must match. */ + memcpy(sun.sun_path + 1, RPCB_ABSTRACT_NAME, nlen); + alen =3D offsetof(struct sockaddr_un, sun_path) + 1 + nlen; + + fd =3D socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return -1; + if (bind(fd, (struct sockaddr *)&sun, alen) < 0 || + listen(fd, RPCB_STUB_MAXCONN) < 0) { + close(fd); + return -1; + } + return fd; +} + +static int rpcb_stub_read(int fd, void *buf, size_t len) +{ + size_t done =3D 0; + + while (done < len) { + ssize_t n =3D read(fd, (char *)buf + done, len - done); + + if (n <=3D 0) + return -1; + done +=3D n; + } + return 0; +} + +/* Handle one record-marked RPC call. Returns -1 when the peer is done. */ +static int rpcb_stub_call(int fd) +{ + unsigned int len, nrep =3D 6, mode =3D rpcb_stats->mode; + uint32_t mark, call[6], rep[7]; + size_t replen; + + if (rpcb_stub_read(fd, &mark, sizeof(mark))) + return -1; + len =3D ntohl(mark) & 0x7fffffff; + if (len < sizeof(call) || len > 4096) + return -1; + if (rpcb_stub_read(fd, call, sizeof(call))) + return -1; + + /* xid, msg_type, rpcvers, prog, vers, proc; the rest is discarded */ + for (len -=3D sizeof(call); len; ) { + char sink[256]; + unsigned int n =3D len > sizeof(sink) ? sizeof(sink) : len; + + if (rpcb_stub_read(fd, sink, n)) + return -1; + len -=3D n; + } + + if (rpcb_stats) + rpcb_stats->calls++; + + rep[0] =3D call[0]; /* xid */ + rep[1] =3D htonl(1); /* REPLY */ + rep[2] =3D htonl(0); /* MSG_ACCEPTED */ + rep[3] =3D htonl(0); /* verifier flavor AUTH_NULL */ + rep[4] =3D htonl(0); /* verifier length */ + rep[5] =3D htonl(0); /* SUCCESS */ + + if (ntohl(call[3]) !=3D RPCB_PROGRAM) { + rep[5] =3D htonl(1); /* PROG_UNAVAIL */ + } else { + switch (ntohl(call[5])) { + case RPCB_PROC_NULL: + break; + case RPCB_PROC_SET: + rep[6] =3D htonl(mode =3D=3D RPCB_STUB_REFUSE ? 0 : 1); + nrep =3D 7; + break; + case RPCB_PROC_UNSET: + rep[6] =3D htonl(1); /* TRUE */ + nrep =3D 7; + break; + default: + rep[5] =3D htonl(3); /* PROC_UNAVAIL */ + } + } + + replen =3D nrep * sizeof(rep[0]); + mark =3D htonl(0x80000000 | replen); + if (write(fd, &mark, sizeof(mark)) !=3D (ssize_t)sizeof(mark) || + write(fd, rep, replen) !=3D (ssize_t)replen) + return -1; + return 0; +} + +static void rpcb_stub_serve(int lfd) +{ + struct pollfd pfd[1 + RPCB_STUB_MAXCONN]; + nfds_t n =3D 1, i; + + pfd[0].fd =3D lfd; + + for (;;) { + /* stop polling the listener when full, or poll() spins */ + pfd[0].events =3D n < 1 + RPCB_STUB_MAXCONN ? POLLIN : 0; + + if (poll(pfd, n, -1) < 0) + return; + + if (pfd[0].revents & POLLIN) { + int c =3D accept(lfd, NULL, NULL); + + if (c >=3D 0) { + pfd[n].fd =3D c; + pfd[n].events =3D POLLIN; + /* + * poll() ran with the old n, so it did not + * write this revents. The loop below reads it. + */ + pfd[n].revents =3D 0; + n++; + if (rpcb_stats) + rpcb_stats->conns++; + } + } + + for (i =3D 1; i < n; i++) { + if (!(pfd[i].revents & (POLLIN | POLLHUP | POLLERR))) + continue; + if (rpcb_stub_call(pfd[i].fd)) { + close(pfd[i].fd); + pfd[i] =3D pfd[--n]; + } + } + } +} + +/* Returns the stub's pid, or -1. The socket is listening before we fork. = */ +static pid_t rpcb_stub_start(int mode) +{ + int lfd =3D rpcb_stub_listen(); + pid_t pid; + + if (lfd < 0) + return -1; + + rpcb_stats->mode =3D mode; + + pid =3D fork(); + if (pid < 0) { + close(lfd); + return -1; + } + if (pid =3D=3D 0) { + signal(SIGPIPE, SIG_IGN); + prctl(PR_SET_PDEATHSIG, SIGKILL); + if (getppid() =3D=3D 1) /* raced with parent exit */ + _exit(0); + rpcb_stub_serve(lfd); + _exit(0); + } + + close(lfd); + return pid; +} + /* --------------------------- fixture --------------------------- */ =20 FIXTURE(nfsd_listener) { - int placeholder; + pid_t rpcbd; }; =20 FIXTURE_SETUP(nfsd_listener) @@ -371,14 +709,43 @@ FIXTURE_SETUP(nfsd_listener) nfsd_family =3D genl_resolve_nfsd(); if (nfsd_family < 0) SKIP(return, "nfsd genl family not found (modprobe nfsd?)"); + + if (rpcb_stats_alloc() < 0) + SKIP(return, "mmap(rpcbind stub counters): %s", strerror(errno)); + + self->rpcbd =3D rpcb_stub_start(RPCB_STUB_ACCEPT); + if (self->rpcbd < 0) + SKIP(return, "cannot start the rpcbind stub: %s", + strerror(errno)); } =20 FIXTURE_TEARDOWN(nfsd_listener) { + /* + * A listener holds a reference to this netns, which outlives the test + * process, so anything still up leaks it. Threads pin the listeners in + * turn; dropping them destroys the serv and everything under it. + */ + if (nfsd_family >=3D 0 && listener_set(NULL, 0) =3D=3D -EBUSY) + threads_set(0); + + if (self->rpcbd > 0) { + kill(self->rpcbd, SIGKILL); + waitpid(self->rpcbd, NULL, 0); + } + if (rpcb_stats) { + munmap((void *)rpcb_stats, sizeof(*rpcb_stats)); + rpcb_stats =3D NULL; + } } =20 /* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D validat= ion / negative =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D */ =20 +TEST_F(nfsd_listener, val_empty_list_ok) +{ + EXPECT_EQ(0, listener_set(NULL, 0)); +} + TEST_F(nfsd_listener, val_too_many) { static char attrs[1 << 20]; @@ -413,13 +780,21 @@ TEST_F(nfsd_listener, val_missing_transport) * A name matching no transport class must be refused before nfsd_mutex is * taken, so it never reaches svc_xprt_create_from_sa() and its * request_module("svc%s", name) upcall. + * + * The errno cannot show that -- svc_xprt_create_from_sa() returns + * -EPROTONOSUPPORT for an unknown name too. The rpcbind traffic can: + * getting that far means nfsd_create_serv() ran, and svc_bind() pings + * rpcbind at client creation and then sweeps stale entries with + * svc_unregister(). A silent stub is the proof nothing was created. */ TEST_F(nfsd_listener, val_bad_transport) { char attrs[64]; int off =3D put_listener(attrs, 0, "bogus_xprt", TEST_PORT); =20 + ASSERT_EQ(0, rpcb_calls()); EXPECT_EQ(-EPROTONOSUPPORT, listener_set(attrs, off)); + EXPECT_EQ(0, rpcb_calls()); } =20 TEST_F(nfsd_listener, val_addr_too_short) @@ -471,14 +846,49 @@ TEST_F(nfsd_listener, val_second_entry_bad) struct sockaddr_storage ss =3D { .ss_family =3D AF_UNIX }; struct raw_listener bad =3D { .xprt =3D "tcp", .emit_addr =3D 1, .addr = =3D &ss, .addr_len =3D sizeof(struct sockaddr_in) }; + struct listener_ent got[MAX_LISTENERS]; char attrs[128]; int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); =20 off =3D put_raw_listener(attrs, off, &bad); /* The whole request is rejected during validation; nothing applied. */ EXPECT_EQ(-EAFNOSUPPORT, listener_set(attrs, off)); + /* + * Again the errno alone does not say so: svc_xprt_create_from_sa() + * also returns -EAFNOSUPPORT, and the doit keeps the listeners it did + * manage to create, so the well-formed tcp entry ahead of the bad one + * would still be up. + */ + EXPECT_EQ(0, listener_get(got, MAX_LISTENERS)); +} + +/* + * A rejected request must leave the listeners that are already up alone. + * The errno alone does not show that: svc_xprt_create_from_sa() returns + * -EPROTONOSUPPORT for an unknown name too. What differs is how far the + * request gets -- without the check in nfsd_nl_validate_listeners(), + * nfsd_nl_listener_set_doit() has already moved the unmatched tcp listener + * off sv_permsocks and run svc_xprt_destroy_all() on it by the time the + * name fails. + */ +TEST_F(nfsd_listener, val_reject_keeps_listeners) +{ + struct listener_ent got[MAX_LISTENERS]; + char good[64], bad[64]; + int og =3D put_listener(good, 0, "tcp", TEST_PORT); + int ob =3D put_listener(bad, 0, "bogus_xprt", TEST_PORT); + + ASSERT_EQ(0, listener_set(good, og)); + ASSERT_EQ(1, listener_get(got, MAX_LISTENERS)); + + EXPECT_EQ(-EPROTONOSUPPORT, listener_set(bad, ob)); + + ASSERT_EQ(1, listener_get(got, MAX_LISTENERS)); + EXPECT_NE(NULL, find_listener(got, 1, "tcp", AF_INET, TEST_PORT)); } =20 +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D functio= nal / round-trip =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D */ + /* LISTENER_GET with no serv in this netns returns an empty list. */ TEST_F(nfsd_listener, func_get_empty) { @@ -487,4 +897,209 @@ TEST_F(nfsd_listener, func_get_empty) EXPECT_EQ(0, listener_get(got, MAX_LISTENERS)); } =20 +TEST_F(nfsd_listener, func_create_tcp) +{ + struct listener_ent got[MAX_LISTENERS]; + char attrs[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, listener_set(attrs, off)); + EXPECT_STREQ("", last_extack); /* nothing to warn about */ + ASSERT_EQ(1, listener_get(got, MAX_LISTENERS)); + EXPECT_NE(NULL, find_listener(got, 1, "tcp", AF_INET, TEST_PORT)); +} + +TEST_F(nfsd_listener, func_create_udp) +{ + struct listener_ent got[MAX_LISTENERS]; + char attrs[64]; + int off =3D put_listener(attrs, 0, "udp", TEST_PORT); + + ASSERT_EQ(0, listener_set(attrs, off)); + ASSERT_EQ(1, listener_get(got, MAX_LISTENERS)); + EXPECT_NE(NULL, find_listener(got, 1, "udp", AF_INET, TEST_PORT)); +} + +TEST_F(nfsd_listener, func_create_multi) +{ + struct listener_ent got[MAX_LISTENERS]; + char attrs[128]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + + off =3D put_listener(attrs, off, "udp", TEST_PORT); + ASSERT_EQ(0, listener_set(attrs, off)); + ASSERT_EQ(2, listener_get(got, MAX_LISTENERS)); + EXPECT_NE(NULL, find_listener(got, 2, "tcp", AF_INET, TEST_PORT)); + EXPECT_NE(NULL, find_listener(got, 2, "udp", AF_INET, TEST_PORT)); +} + +TEST_F(nfsd_listener, func_idempotent) +{ + struct listener_ent got[MAX_LISTENERS]; + char attrs[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, listener_set(attrs, off)); + EXPECT_EQ(0, listener_set(attrs, off)); /* re-set same list */ + ASSERT_EQ(1, listener_get(got, MAX_LISTENERS)); + EXPECT_NE(NULL, find_listener(got, 1, "tcp", AF_INET, TEST_PORT)); +} + +TEST_F(nfsd_listener, func_add) +{ + struct listener_ent got[MAX_LISTENERS]; + char one[64], two[128]; + int o1 =3D put_listener(one, 0, "tcp", TEST_PORT); + int o2 =3D put_listener(two, 0, "tcp", TEST_PORT); + + o2 =3D put_listener(two, o2, "udp", TEST_PORT); + ASSERT_EQ(0, listener_set(one, o1)); + ASSERT_EQ(0, listener_set(two, o2)); /* add udp, keep tcp */ + ASSERT_EQ(2, listener_get(got, MAX_LISTENERS)); + EXPECT_NE(NULL, find_listener(got, 2, "tcp", AF_INET, TEST_PORT)); + EXPECT_NE(NULL, find_listener(got, 2, "udp", AF_INET, TEST_PORT)); +} + +TEST_F(nfsd_listener, func_remove_subset) +{ + struct listener_ent got[MAX_LISTENERS]; + char both[128], one[64]; + int ob =3D put_listener(both, 0, "tcp", TEST_PORT); + int oo =3D put_listener(one, 0, "tcp", TEST_PORT); + + ob =3D put_listener(both, ob, "udp", TEST_PORT); + ASSERT_EQ(0, listener_set(both, ob)); + ASSERT_EQ(0, listener_set(one, oo)); /* drop udp */ + ASSERT_EQ(1, listener_get(got, MAX_LISTENERS)); + EXPECT_NE(NULL, find_listener(got, 1, "tcp", AF_INET, TEST_PORT)); +} + +/* + * LISTENER_GET cannot tell a destroyed serv from a live one with no + * permsocks: nfsd_nl_listener_get_doit() replies empty either way. The + * rpcbind client can. nfsd_destroy_serv() is the only path that reaches + * svc_xprt_destroy_all(..., unregister=3Dtrue) -> svc_rpcb_cleanup() -> + * rpcb_put_local(), which drops the last user and shuts the local client + * down; the next serv then has to connect again. Leaving the serv in place + * would keep the first connection and the stub would see just the one. + */ +TEST_F(nfsd_listener, func_empty_destroys) +{ + struct listener_ent got[MAX_LISTENERS]; + char attrs[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + int conns; + + ASSERT_EQ(0, listener_set(attrs, off)); + conns =3D rpcb_conns(); + ASSERT_GT(conns, 0); + + EXPECT_EQ(0, listener_set(NULL, 0)); /* empty -> destroy serv */ + EXPECT_EQ(0, listener_get(got, MAX_LISTENERS)); + + ASSERT_EQ(0, listener_set(attrs, off)); + EXPECT_GT(rpcb_conns(), conns); +} + +TEST_F(nfsd_listener, func_ipv6) +{ + struct listener_ent got[MAX_LISTENERS]; + char attrs[64]; + int off, s; + + s =3D socket(AF_INET6, SOCK_STREAM, 0); + if (s < 0) + SKIP(return, "IPv6 unavailable: %s", strerror(errno)); + close(s); + + off =3D put_listener_af(attrs, 0, "tcp", AF_INET6, TEST_PORT); + ASSERT_EQ(0, listener_set(attrs, off)); + ASSERT_EQ(1, listener_get(got, MAX_LISTENERS)); + EXPECT_NE(NULL, find_listener(got, 1, "tcp", AF_INET6, TEST_PORT)); +} + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D rpcbind= registration =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D */ + +/* + * A rpcbind that refuses the registration takes the listener down with it. + * svc_register() fails, so svc_setup_socket() fails, so no listener is + * created. -EACCES alone does not show that, since a bind can return it + * too, so read the listener set back as well. + */ +TEST_F(nfsd_listener, sem_register_refused) +{ + struct listener_ent got[MAX_LISTENERS]; + char attrs[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + + rpcb_stub_set_mode(RPCB_STUB_REFUSE); + + EXPECT_EQ(-EACCES, listener_set(attrs, off)); + EXPECT_STRNE("", last_extack); + EXPECT_EQ(0, listener_get(got, MAX_LISTENERS)); +} + +/* + * A listener that cannot be created reports which one it was: the errno + * alone does not name the entry in a multi-listener request. + */ +TEST_F(nfsd_listener, sem_create_failure_extack) +{ + struct sockaddr_in s4 =3D { .sin_family =3D AF_INET, + .sin_port =3D htons(TEST_PORT), + .sin_addr.s_addr =3D htonl(INADDR_LOOPBACK) }; + struct listener_ent got[MAX_LISTENERS]; + char attrs[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + int s; + + /* squat on the port so the listener cannot bind */ + s =3D socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GE(s, 0); + ASSERT_EQ(0, bind(s, (struct sockaddr *)&s4, sizeof(s4))); + + EXPECT_EQ(-EADDRINUSE, listener_set(attrs, off)); + EXPECT_STRNE("", last_extack); + EXPECT_EQ(0, listener_get(got, MAX_LISTENERS)); + close(s); +} + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D threads= / -EBUSY semantics =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D */ + +TEST_F(nfsd_listener, sem_busy_on_change) +{ + struct listener_ent got[MAX_LISTENERS]; + char one[64], two[128]; + int o1 =3D put_listener(one, 0, "tcp", TEST_PORT); + int o2 =3D put_listener(two, 0, "tcp", TEST_PORT); + + o2 =3D put_listener(two, o2, "udp", TEST_PORT); + ASSERT_EQ(0, listener_set(one, o1)); + ASSERT_EQ(0, threads_set(1)); /* threads now running */ + EXPECT_EQ(-EBUSY, listener_set(two, o2)); /* add refused */ + + /* refused means refused: the udp listener must not have been added */ + EXPECT_EQ(1, listener_get(got, MAX_LISTENERS)); + EXPECT_NE(NULL, find_listener(got, 1, "tcp", AF_INET, TEST_PORT)); + + threads_set(0); /* stop before netns exit */ +} + +TEST_F(nfsd_listener, sem_busy_on_remove) +{ + struct listener_ent got[MAX_LISTENERS]; + char one[64]; + int o1 =3D put_listener(one, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, listener_set(one, o1)); + ASSERT_EQ(0, threads_set(1)); + EXPECT_EQ(-EBUSY, listener_set(NULL, 0)); /* remove refused */ + + /* the doit moves the permsocks to a temp list before it can fail */ + EXPECT_EQ(1, listener_get(got, MAX_LISTENERS)); + EXPECT_NE(NULL, find_listener(got, 1, "tcp", AF_INET, TEST_PORT)); + + threads_set(0); +} + TEST_HARNESS_MAIN --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BA5E03A7F54; Tue, 1 Sep 2026 13:40:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270028; cv=none; b=L19fIzM+I7QzApTA1ULZUOmgHDVOxoqzlaCU63N7Spif7d+zz3VhAcjwWF2+0EgIS1yobsrueDUQKOVavzvqXt3mzoxDKvCEYtzuPXOX8HGZELzPiVaFF9HeCejAtAw1ZWJiIlpyIRyo2RW4E9DOIIob3v7dg8NbW75N81akq1c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270028; c=relaxed/simple; bh=ZdQ2DwHT6+BFMpslmGQ2xxKdyv96/k/ivRDtgIuGRWU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rNbAyDHgeh4t7vLyv0Lmw+t2llTs+ZPJeqm4d69DEWpUZyuUmnFdJAuc/WwIWVDZ+BDfPEPs10KIL8E+fg2nAFSe1ONYOMbVf4W7B5xz5kLGbLwwq0Jh7VmB/GfiV1ExO52DFSR2oFnybeT5GiCT6DFU8fs1QSsZjth4Bg5NjXU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OHdxrpRV; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="OHdxrpRV" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 273CF1F00A3E; Tue, 1 Sep 2026 13:40:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270026; bh=sPiGeA0yUw9jwfTLJw/xxScsnI1bmbUKsOj3tK2uhro=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=OHdxrpRV3RIUDL54ZwQpKSCnaSy7VLUucP7yfVOlaASSQnsmsQrJIutGPI6UVVJaf GAju2CPjyoA3ynHSCw31QJfqRdfxnibUvrJx8+x7wSEpvJMwSuPYe5qLnoVJQ/FYEA W6BhHNcUS/En6EChHBulduaN7f2wgbtK76IUkoALw8IbzXGKOzxo2co/LqRIKecjUc 5ZyTCxquZ7pDnu+fmRsKo7ZRX1XXtxIX5Qu72A26mdi/fv9RcHhCUPnM6AIvndJUZZ RXCyjjo6fwJRRcMCSz4EfIA9PinN+MVIUXHlQL/j4PfVWbzxvFM47QL5bG7IQ5bKEN L9BH18Ms/+8Gw== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:52 -0400 Subject: [PATCH v5 14/15] selftests/nfsd: check that listener_set asks rpcbind once Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-14-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=10730; i=jlayton@kernel.org; h=from:subject:message-id; bh=ZdQ2DwHT6+BFMpslmGQ2xxKdyv96/k/ivRDtgIuGRWU=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWx8jkKWLN8poS5xFkUxh9XnmfxekD5oyUAR HF4XMct/VSJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVsQAKCRAADmhBGVaC FbRrEACaMdCVn1dHhfKwOn98PKc/P99/mEN/igzT3YuYioeb53aOpd7zs15kzSN5TO3PMq5MY0P 8w1IcZ6SayUNXvER+ynf4fIfoqbH/524E8cE4GtLqgqpi1O8zVY04/jl0uknVSp/2qB7SlwQxvv +RCclRNcuuu3Sc4wWgkukZcTfXoDoeyXPDMEam9xJvVg3Fs70A/qo6Uepwgc1ti59/Kf0+ABhh/ aA51ScF6OI788ssG3BqmgUEj0l3sUqvH2pqAilQHuVecED7WzWXnGmsEP70Am5e3C7dALOHyPi2 fXRWCUG0o/E2hbr54np1Ui2EtHJ+isGlDvGMygM+lAhBWpAE0WYq7yfFIPdOdeuSHHuOVmRKzaU Soxj7QNJ1vXAjzxj0oqG5MedpFWipXLLyZ2rfap32ngEPzgW76Bi8LgYFP6emUqeMLuXazZSuvf MQubiMqF4dbG183VhRIXGXLwfncGVzFIUMyVPmzuDjCRpFT1W5tpu1RBExxT6FAgJKf/+l2Ijcn zLGkGdtMjQTBfd5VorwwidSYlLyyGjW9TtpK6cg4zRUdm7aPqmSmFnHOSvHtFKfBJG4wUWDlyfF qCStx7U/IG8FN6tQaqWgjlp9rD7czwis6mJAKHEfiaIwj7pIQJKbixJxh4rts+iP9TQbf45LnZ+ Vfq9R6Ds+KYtf1Q== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Cover the change that stops a listener_set request from registering after rpcbind stops answering. RPCB_STUB_SILENT is new. It reads a call and writes nothing back, so the kernel waits out its own timeout. RPCB_STUB_REFUSE cannot serve here: a refusal is an answer, and the count ignores it on purpose. Every procedure but the NULL one is silenced, so an unregistration goes unanswered as well. The NULL one has to be answered: rpcb_create_af_local() builds its client without RPC_CLNT_CREATE_NOPING, so rpc_create() pings at creation. Silencing that ping too would fail the AF_LOCAL client, send rpcb_create_local() on to the loopback client of rpcb_create_local_net(), and leave the stub seeing one call per request no matter how many listeners it carried. - rpcb_stop_after_failure. Ask for one listener, then for three, and compare what the stub saw. Three entries must not cost three times as much. - rpcb_silent_set_complete. The entry that finds rpcbind silent is the one that pays the timeout, and with v3 enabled it is the only entry whose listener would be lost. Require that a three-entry request brings up all three, succeeds, and warns. - rpcb_v4_only_bounded. The case that needs the count rather than a failed listener. version_set_only() makes the server v4-only, so vs_rpcb_optnl discards every error and every listener comes up. Require that the listeners are present, that the ack warns about them, and that three entries do not cost three round trips. - rpcb_retry_next_request. The stop lasts for one request. After the stub starts to answer, the next request must reach rpcbind again. The counts these tests compare include the unregistrations that the teardown between the two measurements issues. The preceding patches bound that direction too, so the sweeps stay at one timeout rather than one per program and version. version_set_only() is new. NFSD_CMD_VERSION_SET clears every version before it reads the request, so one nest leaves the server v4-only. It returns -EBUSY once a serv exists, so the test calls it first. Assisted-by: LLM Signed-off-by: Jeff Layton --- .../testing/selftests/nfsd/nfsd_netlink_listener.c | 183 +++++++++++++++++= +++- 1 file changed, 181 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/nfsd/nfsd_netlink_listener.c b/tools/t= esting/selftests/nfsd/nfsd_netlink_listener.c index 736691ce9a20..d0f4258f905d 100644 --- a/tools/testing/selftests/nfsd/nfsd_netlink_listener.c +++ b/tools/testing/selftests/nfsd/nfsd_netlink_listener.c @@ -361,6 +361,28 @@ static int listener_set(const char *attrs, int len) return genl_request(NFSD_CMD_LISTENER_SET, attrs, len); } =20 +/* + * Enable exactly one NFS version in this netns. NFSD_CMD_VERSION_SET clea= rs + * every version first, so one nest is enough to leave the server v4-only. + * It refuses once a serv exists, so call it before any listener. + */ +static int version_set_only(uint32_t major, uint32_t minor) +{ + char attrs[64]; + struct nlattr *nest =3D (void *)attrs; + int inner =3D NLA_HDRLEN; + + inner =3D put_attr(attrs, inner, NFSD_A_VERSION_MAJOR, + &major, sizeof(major)); + inner =3D put_attr(attrs, inner, NFSD_A_VERSION_MINOR, + &minor, sizeof(minor)); + inner =3D put_attr(attrs, inner, NFSD_A_VERSION_ENABLED, NULL, 0); + nest->nla_type =3D NFSD_A_SERVER_PROTO_VERSION | NLA_F_NESTED; + nest->nla_len =3D inner; + + return genl_request(NFSD_CMD_VERSION_SET, attrs, NLA_ALIGN4(inner)); +} + /* Fetch the current listeners; returns count (>=3D0) or -errno. */ static int listener_get(struct listener_ent *out, int max) { @@ -431,6 +453,14 @@ static int threads_set(int n) * rpcb_register_call() reports as -EACCES. UNSET is left alone: only * svc_unregister() issues it, and it discards the result. * + * In RPCB_STUB_SILENT mode a SET or an UNSET is read and nothing is writt= en + * back, so the kernel waits out its own timeout. That is the only mode th= at + * makes rpcb_register_call() report a call that got no answer, which is w= hat + * the per-net failure count records. The NULL procedure is still answered: + * rpcb_create_af_local() builds its client without RPC_CLNT_CREATE_NOPING= , so + * rpc_create() pings, and a ping that goes unanswered drops the kernel on= to + * the loopback rpcb_create_local_net() client, which never reaches this s= tub. + * * The stub also keeps counters and the mode in a page shared with the tes= t, so * a test can assert that the kernel never talked to rpcbind at all, or th= at it * dropped the local rpcbind client and had to reconnect. @@ -448,7 +478,7 @@ static int threads_set(int n) #define RPCB_ABSTRACT_NAME "/run/rpcbind.sock" #define RPCB_STUB_MAXCONN 4 =20 -enum { RPCB_STUB_ACCEPT, RPCB_STUB_REFUSE }; +enum { RPCB_STUB_ACCEPT, RPCB_STUB_REFUSE, RPCB_STUB_SILENT }; =20 struct rpcb_stub_stats { unsigned int conns; /* connections accepted */ @@ -563,7 +593,9 @@ static int rpcb_stub_call(int fd) if (ntohl(call[3]) !=3D RPCB_PROGRAM) { rep[5] =3D htonl(1); /* PROG_UNAVAIL */ } else { - switch (ntohl(call[5])) { + unsigned int proc =3D ntohl(call[5]); + + switch (proc) { case RPCB_PROC_NULL: break; case RPCB_PROC_SET: @@ -577,6 +609,15 @@ static int rpcb_stub_call(int fd) default: rep[5] =3D htonl(3); /* PROC_UNAVAIL */ } + + /* + * Answer nothing, so the caller waits out its timeout. The + * NULL procedure is answered even here: the kernel pings at + * client creation, and a ping with no answer takes it off + * this socket entirely. + */ + if (mode =3D=3D RPCB_STUB_SILENT && proc !=3D RPCB_PROC_NULL) + return 0; } =20 replen =3D nrep * sizeof(rep[0]); @@ -1064,6 +1105,144 @@ TEST_F(nfsd_listener, sem_create_failure_extack) close(s); } =20 +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D one rpcbind attempt for each reque= st =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D */ + +/* + * Every listener used to register on its own, so a rpcbind that never + * answers cost one timeout for each entry. Ask for one listener, then for + * three, and compare what the stub saw. Three entries must not cost three + * times as much. + * + * The stub has to stay silent rather than refuse. A refusal is an answer, + * and rpcbind refuses one entry at a time, so the count ignores it. + */ +TEST_F(nfsd_listener, rpcb_stop_after_failure) +{ + int before, one, three, off; + char attrs[192]; + + rpcb_stub_set_mode(RPCB_STUB_SILENT); + + before =3D rpcb_calls(); + off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + listener_set(attrs, off); + one =3D rpcb_calls() - before; + ASSERT_GT(one, 0); + + ASSERT_EQ(0, listener_set(attrs, 0)); + + before =3D rpcb_calls(); + off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + off =3D put_listener(attrs, off, "tcp", TEST_PORT + 1); + off =3D put_listener(attrs, off, "tcp", TEST_PORT + 2); + listener_set(attrs, off); + three =3D rpcb_calls() - before; + + /* the second and third entries must not reach rpcbind at all */ + EXPECT_LE(three, one); +} + +/* + * The entry that finds rpcbind silent is the one that pays for the + * discovery, and v3 has no vs_rpcb_optnl to discard the error, so it is t= he + * only entry whose listener would be lost. Nothing distinguishes it from = the + * rest of the request, and a retry of the same request would fail the same + * entry again, so the set would stay short for as long as rpcbind was qui= et. + * + * Ask for three listeners against a silent stub and require the whole set, + * a success, and a warning that says why. + */ +TEST_F(nfsd_listener, rpcb_silent_set_complete) +{ + struct listener_ent got[MAX_LISTENERS]; + char attrs[192]; + int off; + + rpcb_stub_set_mode(RPCB_STUB_SILENT); + + off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + off =3D put_listener(attrs, off, "tcp", TEST_PORT + 1); + off =3D put_listener(attrs, off, "tcp", TEST_PORT + 2); + EXPECT_EQ(0, listener_set(attrs, off)); + + /* the first entry is not the odd one out */ + EXPECT_EQ(3, listener_get(got, MAX_LISTENERS)); + /* no errno reports this, so the ack has to */ + EXPECT_STRNE("", last_extack); +} + +/* + * The case that needs the count rather than a failed listener. NFSv4 sets + * vs_rpcb_optnl, so svc_generic_rpcbind_set() discards the error, every + * listener comes up, and nothing reports a failure. Without the fix each + * entry still waits for rpcbind on its own. + * + * Make the server v4-only, answer no SET, and require three things: the + * listeners come up, the ack warns that they are not registered, and the + * stub does not see one round trip for each entry. + */ +TEST_F(nfsd_listener, rpcb_v4_only_bounded) +{ + struct listener_ent got[MAX_LISTENERS]; + int before, one, three, off; + char attrs[192]; + + /* refuses once a serv exists, so this has to come first */ + ASSERT_EQ(0, version_set_only(4, 1)); + rpcb_stub_set_mode(RPCB_STUB_SILENT); + + before =3D rpcb_calls(); + off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + ASSERT_EQ(0, listener_set(attrs, off)); + one =3D rpcb_calls() - before; + ASSERT_GT(one, 0); + + /* start over, so the second measurement also builds a serv */ + ASSERT_EQ(0, listener_set(attrs, 0)); + + before =3D rpcb_calls(); + off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + off =3D put_listener(attrs, off, "tcp", TEST_PORT + 1); + off =3D put_listener(attrs, off, "tcp", TEST_PORT + 2); + ASSERT_EQ(0, listener_set(attrs, off)); + three =3D rpcb_calls() - before; + + /* the listeners are up even though rpcbind never answered */ + EXPECT_EQ(3, listener_get(got, MAX_LISTENERS)); + /* and the ack says they are unregistered, since no errno can */ + EXPECT_STRNE("", last_extack); + EXPECT_LE(three, one); +} + +/* + * The stop applies to one request only. After rpcbind starts answering, + * the next request must register without any other step. + */ +TEST_F(nfsd_listener, rpcb_retry_next_request) +{ + int before, after, off; + char attrs[192]; + + rpcb_stub_set_mode(RPCB_STUB_SILENT); + + off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + off =3D put_listener(attrs, off, "tcp", TEST_PORT + 1); + listener_set(attrs, off); + ASSERT_EQ(0, listener_set(attrs, 0)); + + /* rpcbind recovers */ + rpcb_stub_set_mode(RPCB_STUB_ACCEPT); + + before =3D rpcb_calls(); + off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + EXPECT_EQ(0, listener_set(attrs, off)); + after =3D rpcb_calls(); + + /* a fresh request starts from a fresh reading and tries again */ + EXPECT_GT(after, before); + EXPECT_STREQ("", last_extack); +} + /* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D threads= / -EBUSY semantics =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D */ =20 TEST_F(nfsd_listener, sem_busy_on_change) --=20 2.55.0 From nobody Sat Sep 26 12:28:38 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5A40F43A7F7; Tue, 1 Sep 2026 13:40:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270029; cv=none; b=lxdTTBYg2LsaEeFxq+rqqHUgoP4aSoNprB9v4/so+WlbjFg+Og7zVUTtl4jPnR9KkmPdVWm7mv0RA7S/+eXG4JweO09RyZsROZqJLetl03Sd2hZrQe8bSfFPdFtMBVHSHp6iUhgvtJFaEuGbyAzRLOEKIm0ZSs5zExSsEnfUmII= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788270029; c=relaxed/simple; bh=75QO4SurT2sIQy+kvdxUPZUZy8/rxZ/KBie3jlSvnBY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MGDDeLRAghIV1mBV+TdyDxwZiRnkD00pWKkENJ/g+VTa77aVNRc2/gI0dBKnslYJci3+v1dGzxnyvN7IG1ViLWVmqryWK3lcGvcVBuQOnIFzHdayJfwfKfYznPh1ujMGr3Btg2NgxQkI+yvwU85ooHXkgx4OResR+v0/ruiYmsY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bJRJrhk+; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bJRJrhk+" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C88AB1F00A3F; Tue, 1 Sep 2026 13:40:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788270028; bh=V8VOs9E8hva8yGO31qnDmSPXxyBXMikzs/FU9U06NdY=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=bJRJrhk+QLIfmBVWyhrNApv0vVVPTB/aXPkmQ5xaWLgqdA9jfNNAo/UhWSdY2G+h9 8NqMuFClJt7UrxC7Em6yqoDgTuiFRF7t2UcrVdeSK6qbHzX+91RIiIgj16gdunFE2O 5wnScRd1IKVRLpY4qjGWiAviNiHJGEGG9PxWd5N3eji/LDFiRLkcERhtbqvAYEDzaF aOWC3rruUF6YkqDVXC80iht7wA33gv3zX+5s37dGcY2AfgIZHgl8eUw/NdzEhIPJko U6YCyOdGuRilkkNA2EcZwRWgpMQ7qwAj9XeN7Dg00n1zXprybPPFvqdfWvNtU8lKRR v7pHZ+b5RfbIQ== From: Jeff Layton Date: Tue, 01 Sep 2026 09:39:53 -0400 Subject: [PATCH v5 15/15] selftests/nfsd: check that listener removal asks rpcbind once Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260901-nfsd-nl-hang-v5-15-a540d86668b0@kernel.org> References: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> In-Reply-To: <20260901-nfsd-nl-hang-v5-0-a540d86668b0@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: Slawomir Stepien , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Trond Myklebust , linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2494; i=jlayton@kernel.org; h=from:subject:message-id; bh=75QO4SurT2sIQy+kvdxUPZUZy8/rxZ/KBie3jlSvnBY=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqltWxzlZQ6zkCOPXJCRJeGCIuwjvpv17ImQeBG l3KSlA8xNiJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCapbVsQAKCRAADmhBGVaC FQYDD/0QKqW/kjPzJ4hzy4NFHOIaoufargbQwwI9L+7045iYjeuEb/AEWhIprAf4pCWRJ5WrSz1 U8F2a+VEtRaXKUr0aenCvT12UQC5VLMChwnptrrmkft8jFme2Xo18AjD+fWv3jcUiZ+KhgjZ+dp CuaTQDniPZeQGFjJgdABcuAdDyUzsVFJiepD98iQ/25pdb8Kn687lSp9tSgODOyeKJKNeaiknvD hak0V8vTPUXdKz+OmTht9A7+WiESTUW5nhUUw+LYhk4PAM8e2me29t8bxYrVTc3HR1Re+m0HeHo d8zXLU2atK/a8huQ3rwWz6Mp5mVHMCmLz/gb7RKol88IYsnk3zeeOLrQKj58KmeJL6SAvbQELjV XkZvDMCn64uhkwyS2JkYvfw0rMH6UIuwdiDfp6ygs/kfv7bsSJcPOBj6RFU407oJsMizfMHlE9t MNOcVSVWJkcQvNzZjy4tMRmzw019XeyinTx4xQo2UUkzz8e9XGrJv8MVQf6hABb802X9fF/D4vF SbwP790gwHmVJHZk1h+4rUX+3iVIBMpIA9Q/YpXCDswOJKH/jnzXqPtq7E7tQOZu058cuubhW8D 5ohRTg4x2pGiI75u4jdiG23Zqc3eZBaIoRar3Lk6pst66mgHfsOco6Sp6eFVS26K5C4E1G9C23u gVmIyUF9v7QcnUA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Cover the teardown side of the same rule. Remove one listener, then three, with the stub silent, and compare what it saw. Three removals must not cost three timeouts. Both measurements also pay the svc_unregister() sweep that nfsd_destroy_serv() runs once the last listener is gone, so that cancels out of the comparison. The listeners are registered with the stub answering, so each one has an entry to remove. Assisted-by: LLM Signed-off-by: Jeff Layton --- .../testing/selftests/nfsd/nfsd_netlink_listener.c | 39 ++++++++++++++++++= ++++ 1 file changed, 39 insertions(+) diff --git a/tools/testing/selftests/nfsd/nfsd_netlink_listener.c b/tools/t= esting/selftests/nfsd/nfsd_netlink_listener.c index d0f4258f905d..106360f87b99 100644 --- a/tools/testing/selftests/nfsd/nfsd_netlink_listener.c +++ b/tools/testing/selftests/nfsd/nfsd_netlink_listener.c @@ -1243,6 +1243,45 @@ TEST_F(nfsd_listener, rpcb_retry_next_request) EXPECT_STREQ("", last_extack); } =20 +/* + * The same rule on the way out. Removing a listener unregisters it, so a + * rpcbind that stops answering used to cost one timeout for each listener + * removed. Register one listener while the stub answers, silence the stub, + * remove it and count; then do the same with three. + * + * Both measurements also pay the svc_unregister() sweep that + * nfsd_destroy_serv() runs once the last listener is gone, so that cancels + * out of the comparison. + */ +TEST_F(nfsd_listener, rpcb_unreg_stop_after_failure) +{ + int before, one, three, off; + char attrs[192]; + + off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + ASSERT_EQ(0, listener_set(attrs, off)); + + rpcb_stub_set_mode(RPCB_STUB_SILENT); + before =3D rpcb_calls(); + ASSERT_EQ(0, listener_set(NULL, 0)); + one =3D rpcb_calls() - before; + ASSERT_GT(one, 0); + + rpcb_stub_set_mode(RPCB_STUB_ACCEPT); + off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + off =3D put_listener(attrs, off, "tcp", TEST_PORT + 1); + off =3D put_listener(attrs, off, "tcp", TEST_PORT + 2); + ASSERT_EQ(0, listener_set(attrs, off)); + + rpcb_stub_set_mode(RPCB_STUB_SILENT); + before =3D rpcb_calls(); + ASSERT_EQ(0, listener_set(NULL, 0)); + three =3D rpcb_calls() - before; + + /* the second and third removals must not reach rpcbind at all */ + EXPECT_LE(three, one); +} + /* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D threads= / -EBUSY semantics =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D */ =20 TEST_F(nfsd_listener, sem_busy_on_change) --=20 2.55.0