From nobody Fri Sep 25 07:22:10 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 DC8F74BB5C2; Tue, 15 Sep 2026 16:58:22 +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=1789491504; cv=none; b=dIieFV7qgWkcVZDR9u73vtC9ICVPuNXJQuCfWOZAnwq2y9Hp+gfmOhz/slEeW7I3HLfKN5zl3C8THHuJhxTGYw+97rX/+bzrK5hPiYSgOSdfJ5T9eKB/on2pXEA7C+WRSiLuXHrP1X3SXplyENfBVSa58rAr3xcku539i1VHqJk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789491504; c=relaxed/simple; bh=UoWQHbkr2M9okG7sxUiZYcmC9RgSw9SeU8siqAcbPag=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LnVSTMZu0PgVnsreq9jQMPwOw581DN4sLHb+71wDt26LcONwZRy5szvT34NfHLiNVMH1ZMItEkk0dRBZTe0P6cQPnm/suFEGhlE7ILFgEG0eSGMZRsYdzUCAvWM4YbY34UscLoB44meTvFZzcAzxYhK+46VFt+biQrx5yxUQHPc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=gEE/HBnb; 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="gEE/HBnb" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 066A21F00898; Tue, 15 Sep 2026 16:58:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789491502; bh=OeeQ+ZrmuUedsYcXYwL4tkTD/pZRXUuRAVVdzvyPLKE=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=gEE/HBnbqVhC19JguXbHEXqOV3I+Pg1vdl2LIrkN4dg5xJSloG1NPo/D7aXNZHjQO DGM5R5haMaeLeVdgRssmTFALOc0mzZtvbhqjkUNlGPaUbCEF+J3itD4vrUJZCt3JjN AuCPRq4t6ESxqKCgIm1pYkqoWUy84ReYoBNYuvuvGVRQ+4zSZwUb7+Duak72w3Qd1o YadnNdaaDdc5egPDf40vtDB6vAZC98s86voFbTD1emeXpcSVbJJmPHQJBS315/4uvL V+tzNJnJc0vbTWUo9RHWvyr147bhDKgmPamIEsTU+BTS99ySR16LKscQEPbKpe/kp+ SDA8CitIXnSHg== From: Jeff Layton Date: Tue, 15 Sep 2026 12:57:42 -0400 Subject: [PATCH v3 1/5] SUNRPC: allow a service to opt out of rpcbind registration 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: <20260915-nfsd-norpcb-v3-1-3c60d49ade02@kernel.org> References: <20260915-nfsd-norpcb-v3-0-3c60d49ade02@kernel.org> In-Reply-To: <20260915-nfsd-norpcb-v3-0-3c60d49ade02@kernel.org> To: Trond Myklebust , Anna Schumaker , Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Donald Hunter , Shuah Khan Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2210; i=jlayton@kernel.org; h=from:subject:message-id; bh=UoWQHbkr2M9okG7sxUiZYcmC9RgSw9SeU8siqAcbPag=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqqXkqcaLQI+8LO3EAvOhs84pW2NS1GNfDVBNAV c+EDqOVk/2JAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaql5KgAKCRAADmhBGVaC FenbD/9knJs18slI5cZ7sI/4yJT0FxNGelszz3DBCciMgd1rMwoSEWK/knIysaOEDov9eObXl55 /kWohvTkoeypggxbS1+CwnstZKsMW83vvnlGSK9IpD2Y13W1vBoXpyWNDfKbhMK0VPsTvA9ZIqt Q12eiBT4kSiW5+mN+GBDPTesx4qrBEYn1l96g56kV2Y3hNGo6vH4oJdeC8Th1y6EqF63igAKLsI vNms80LPvhdUwhE1nbtYkTTjGbMYVgUdp5cD2iu+JxnDg5c/UmF7j6i9S0Mbdns7VsPNlw5vGRY iiNWPdm+B3dtQ85EKoKmG2HZcvjjtzsudFtp2S/nOFGmaqA6g193nLQCJEwiFWuyExUl/W5W14S ErjIC7bvsrmvKyQBkqg3SbJYiP+LGpIm7v6PXJzGkzng48aFDhvgNo9RDitxqYKFmuII7CzNPEE RDmNf+uPqkva49iw7vxaEkuCYLdv8qNQWHIfN9Tnpv+sLd4+wT86vhifs7e4zTVjboaMIt1f15U rWRSA32+q1NsS75EgEX/0+f56gv+KA4rkR42+K+o12urq4saYUHq2Kthk4WRYof7cCZONPwpn/i lfDsc4QkUj2WZYLvE9lSB9U8ili0mZr+4iXchHyGUtRmiK3Td/ENgHeeqLDK/8RCO9JICrF/2yp AxapZfPznb8SQ4w== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 svc_bind() creates the local rpcbind client, and svc_register() then makes one synchronous call for each program and version. Both run under the caller's mutex. A caller that registers from userland needs neither. Add sv_no_rpcbind to struct svc_serv, and fix up the code to honor it. No caller sets the flag yet, so behaviour does not change. Assisted-by: LLM Signed-off-by: Jeff Layton --- include/linux/sunrpc/svc.h | 2 ++ net/sunrpc/svc.c | 5 +++++ net/sunrpc/svc_xprt.c | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 24698856eb40..cfb5ab17dabd 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -87,6 +87,8 @@ struct svc_serv { char * sv_name; /* service name */ =20 bool sv_is_pooled; /* is this a pooled service? */ + /* Caller registers with rpcbind itself. Set before svc_bind(). */ + bool sv_no_rpcbind; struct svc_pool * sv_pools; /* array of thread pools */ int (*sv_threadfn)(void *data); =20 diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index f73412e123a1..7e23af94a719 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -337,6 +337,8 @@ static int svc_uses_rpcbind(struct svc_serv *serv) =20 int svc_bind(struct svc_serv *serv, struct net *net) { + if (serv->sv_no_rpcbind) + return 0; if (!svc_uses_rpcbind(serv)) return 0; return svc_rpcb_setup(serv, net); @@ -1235,6 +1237,9 @@ int svc_register(struct svc_serv *serv, struct net *n= et, if (proto =3D=3D 0 && port =3D=3D 0) return -EINVAL; =20 + if (serv->sv_no_rpcbind) + return 0; + for (p =3D 0; p < serv->sv_nprogs; p++) { struct svc_program *progp =3D &serv->sv_programs[p]; =20 diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 5e210bc68422..08cfe8a31189 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -1266,7 +1266,7 @@ void svc_xprt_destroy_all(struct svc_serv *serv, stru= ct net *net, msleep(delay++); } =20 - if (unregister) + if (unregister && !serv->sv_no_rpcbind) svc_rpcb_cleanup(serv, net); } EXPORT_SYMBOL_GPL(svc_xprt_destroy_all); --=20 2.55.0 From nobody Fri Sep 25 07:22:10 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 47D314BD7B2; Tue, 15 Sep 2026 16:58:24 +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=1789491509; cv=none; b=hJysyLaQpDZ2iDQxU7evhfo6sAzPbtAO7cHIjbthb00nzk660oeJu8DcVvccoFBknKdi5KtjJpuzmbWm6zp2lwCWrogBV/ga2atv6NGHs22vuasWdaDlc9GgBkU3hBphik168hmYFFKQdoVPlQnha209wuVmPPBnWxnZ3x3Ti1g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789491509; c=relaxed/simple; bh=dYm5b7B6dTvNKjVYzuF7/FkF8IDWphASM2cJV/5trG4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qQI3IAF5XerXeH4lAunpf8U0wvUd5NGajfGy233/ZqDjTT8tH2guvOTuwaK2wmQ0kk+fzSJtJ+9JhcVBv9h9KKmrD3t0cnR/ZqMOzJvZmp7qRT3OzPFpaG8mALptTWYt5KFHWgygbhCb+k6JYUtcxPoCiPIr1vs+cv3kwtI7WqU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QwEPeT1G; 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="QwEPeT1G" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9A7F91F000FF; Tue, 15 Sep 2026 16:58:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789491504; bh=QH3G09fgNYNq57/vr1pk3tneJpQFCpXfha0cp9uQYVE=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=QwEPeT1G5yzGOEs6JBwSCHBG5dch1Tzz+85h6zZC6Y9IOenhqyKSHxCVbgWShAQ7c +4nDgaITsiy85liG7dSftQxK1PnIZrPOC1Bnmct2AmkPiqZ/+I0UgJNEhpSYSNzqLx /nM/fCeyF3Gx/4ezfkATRc3p2fEzsOfUILq4do8Turr27QK1tFAg77HHiCZE8kEIIG UJGex8TjaaEVkwY3e1ingNct0b65ii5qmCgUrryBrtCjApcgACH32fmYb0LG9dk5wP t9ht3FG+QMdF6plIWzgsHNf+31hD4513TCNwuLlh5QfroLA9sBVd10rnnR/y3uNlvs 4ij+RtQo8Xvmw== From: Jeff Layton Date: Tue, 15 Sep 2026 12:57:43 -0400 Subject: [PATCH v3 2/5] NFSD: add a userspace-rpcbind flag to 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: <20260915-nfsd-norpcb-v3-2-3c60d49ade02@kernel.org> References: <20260915-nfsd-norpcb-v3-0-3c60d49ade02@kernel.org> In-Reply-To: <20260915-nfsd-norpcb-v3-0-3c60d49ade02@kernel.org> To: Trond Myklebust , Anna Schumaker , Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Donald Hunter , Shuah Khan Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=5624; i=jlayton@kernel.org; h=from:subject:message-id; bh=dYm5b7B6dTvNKjVYzuF7/FkF8IDWphASM2cJV/5trG4=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqqXkqChjh9OPXC2JsyxmEeUx1A2eWY/elWVW6R SSUZwFwGLGJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaql5KgAKCRAADmhBGVaC FfTuEAC67d83B7EffeZGDCzLcuZCZZgVK4eV7V7YF6icKZXxv9AFBm8Q9JF2h2uopkG9CsrOwLm g58HbXcSqzT+5M7mm8giNhgkxQObJAPXRGODJRNwOVOR8oDLBu+mFOUl025eGV5veYWmeWVn7nq rJfIo/sm38QCaSi8/p2HhjppXvU1/K4nRyRrS5Yzu0SlKh+cLBh4o6MU+Lq4z01+vRu220Oe/8y znndLfYBUcPIfBz7vNsMxQu2aohTR5D9hpVr6F80I2nUea7KwsrvdFG9jyPGwm2JdPZO8rab1Gw WQfBqKBCKKvakUCw9nH0AYFjM+d5K3p9zQnwFkPNHZ061ak7xG6YlHCy6eisO8sWC8ZXC5HySB3 wo7ejcuEyrjg4KFHxGAUCRzB0DSramQ5Q7oaTwlRLXFebpAJ1TFtLGGDyOlwPRzX7reEFuNTHxt V+f/PlbNz0rEthOnhzOsWIsMl7M8qSVS8XfuGIqz3CtppXT8BuSiD7H40VEspSLP/kQe3jgfdqb rfv2DK8UpId7XPuoZTAnKM4SbJC4SbhSi/ipVEQt8/gBd5tBjfqdzzHbG4tr1LP84d2upHdcz13 ACaoIO0IsiQ/JTHPpiQoI90HF2Pmb6f8cyL44QtezeCrToFZpSNs/8XFHXt+Uq9tF6FdSNhesDu qs3G5VdlHRj8Zug== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Describe the interface that lets a caller take over rpcbind registration. The request gains a userspace-rpcbind flag. The reply echoes the flag, lists the programs and versions that the caller should register, and names the listeners that came up. NLM is absent. lockd owns its own svc_serv and still registers itself. Assisted-by: LLM Signed-off-by: Jeff Layton --- Documentation/netlink/specs/nfsd.yaml | 55 +++++++++++++++++++++++++++++++= +++- fs/nfsd/netlink.c | 5 ++-- include/uapi/linux/nfsd_netlink.h | 20 +++++++++++++ 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/= specs/nfsd.yaml index 642268819c6f..9ae37bf3ea71 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -42,6 +42,15 @@ definitions: - none - tls - mtls + - + type: flags + name: rpcbind-flags + doc: >- + Constraints that apply to an rpcbind registration. no-udp means the + kernel would not have registered this program and version over UDP, + so the caller must skip the udp and udp6 netids for it. + entries: + - no-udp =20 attribute-sets: - @@ -159,6 +168,23 @@ attribute-sets: - name: transport-name type: string + - + name: rpcbind + attributes: + - + name: program + type: u32 + doc: RPC program number to register. + - + name: version + type: u32 + doc: RPC version number to register. + - + name: flags + type: u32 + enum: rpcbind-flags + enum-as-flags: true + doc: Constraints on the listeners this entry applies to. - name: server-sock attributes: @@ -167,6 +193,24 @@ attribute-sets: type: nest nested-attributes: sock multi-attr: true + - + name: userspace-rpcbind + type: flag + doc: >- + The caller registers the listeners with rpcbind itself, so the + kernel must not do it. The kernel echoes this attribute in the + reply when it accepts the request. Ownership cannot change while + a server exists. + - + name: rpcbind + type: nest + nested-attributes: rpcbind + multi-attr: true + doc: >- + A program and version that the caller should register for every + listener reported in the same reply, except the netids that flags + rules out. Reply only. NLM is absent because lockd still + registers itself. - name: pool-mode attributes: @@ -483,13 +527,22 @@ operations: - version - name: listener-set - doc: set nfs running sockets + doc: >- + set nfs running sockets. A request that carries userspace-rpcbind + is answered with a reply rather than a bare ack, and the addr list + in that reply names only the listeners that have an rpcbind netid. attribute-set: server-sock flags: [admin-perm] do: request: attributes: - addr + - userspace-rpcbind + reply: + attributes: + - addr + - userspace-rpcbind + - rpcbind - name: listener-get doc: get nfs running listeners diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index eba8b353f412..88a4a4ffcb7f 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -79,8 +79,9 @@ static const struct nla_policy nfsd_version_set_nl_policy= [NFSD_A_SERVER_PROTO_VE }; =20 /* NFSD_CMD_LISTENER_SET - do */ -static const struct nla_policy nfsd_listener_set_nl_policy[NFSD_A_SERVER_S= OCK_ADDR + 1] =3D { +static const struct nla_policy nfsd_listener_set_nl_policy[NFSD_A_SERVER_S= OCK_USERSPACE_RPCBIND + 1] =3D { [NFSD_A_SERVER_SOCK_ADDR] =3D NLA_POLICY_NESTED(nfsd_sock_nl_policy), + [NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND] =3D { .type =3D NLA_FLAG, }, }; =20 /* NFSD_CMD_POOL_MODE_SET - do */ @@ -153,7 +154,7 @@ static const struct genl_split_ops nfsd_nl_ops[] =3D { .cmd =3D NFSD_CMD_LISTENER_SET, .doit =3D nfsd_nl_listener_set_doit, .policy =3D nfsd_listener_set_nl_policy, - .maxattr =3D NFSD_A_SERVER_SOCK_ADDR, + .maxattr =3D NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND, .flags =3D GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_ne= tlink.h index 87da1d0bb21e..c125af0cc6a5 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -50,6 +50,15 @@ enum nfsd_xprtsec_mode { NFSD_XPRTSEC_MODE_MTLS =3D 4, }; =20 +/* + * Constraints that apply to an rpcbind registration. no-udp means the ker= nel + * would not have registered this program and version over UDP, so the cal= ler + * must skip the udp and udp6 netids for it. + */ +enum nfsd_rpcbind_flags { + NFSD_RPCBIND_FLAGS_NO_UDP =3D 1, +}; + enum { NFSD_A_CACHE_NOTIFY_CACHE_TYPE =3D 1, =20 @@ -113,8 +122,19 @@ enum { NFSD_A_SOCK_MAX =3D (__NFSD_A_SOCK_MAX - 1) }; =20 +enum { + NFSD_A_RPCBIND_PROGRAM =3D 1, + NFSD_A_RPCBIND_VERSION, + NFSD_A_RPCBIND_FLAGS, + + __NFSD_A_RPCBIND_MAX, + NFSD_A_RPCBIND_MAX =3D (__NFSD_A_RPCBIND_MAX - 1) +}; + enum { NFSD_A_SERVER_SOCK_ADDR =3D 1, + NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND, + NFSD_A_SERVER_SOCK_RPCBIND, =20 __NFSD_A_SERVER_SOCK_MAX, NFSD_A_SERVER_SOCK_MAX =3D (__NFSD_A_SERVER_SOCK_MAX - 1) --=20 2.55.0 From nobody Fri Sep 25 07:22:10 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 D7CD74BD7BD; Tue, 15 Sep 2026 16:58: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=1789491508; cv=none; b=pqsBOBab5fMNJZReG2uHSorg4qa2b6M3GED4Z5GvhANOQHkRfz0CKpvfWtuANE3IGe6Yz2oekE2zreIoXzcf7kt6L8+vnGP8aIROA0Y26It5OcMusDQCRYjKm5aRUDnd/ZdbyKVrsIpjlOsi7iwO2xF5YTv3ZwIWFK2oxKLEMLE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789491508; c=relaxed/simple; bh=w8hVnSxhB7CSJMEamGUn2/nMsx+VxVbSzGGcAnKu1Dw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=erFw5ROF4t8Q1bmjSb6WeS9mr+wsAoHMQ/jeNhzWOgNUmFrf1EEiDHSmOXRARs4PNiXWfVOKegjoCch80EEZKPlpVAbm0pYyG3sdIL/FqsszVWYy05Yf+GjcXFvR120wWR8pDBJ1EdyM9px3AZVemugR8BKDbVnTs42ChjLsRI0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jrHnZn+o; 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="jrHnZn+o" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3A2F91F00893; Tue, 15 Sep 2026 16:58:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789491505; bh=sSzSDKtSjNzfxVhYsQdZGlnH5vafz2Jr0mhSSPbOLoc=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=jrHnZn+ojJvSU3jgk4tDyBeqDR9JizWO4omCRA2UuyewrH8MeBBlqqwqq+aMIfqfo W3xWI010LoXsn9b5EuVG62+NlD7xNSWol1kJTPEyzjVOJRoSibWj6oj6/K/Ej6VgLK iyjtVdmI+0I247oyf+GwrJTvybsE3wsVw2EdTfb/dlYsJ6vE7/G0L4YOCkv21a82Iv IJNx2FOCdThCLb4h+1F1eyiFV7+/T7+uloC4jEMIfedOlx9963NMaLOfxcUYeQh+8w +ElS0CnAIWgEOCvR79B+oTwKRq0IPSFiApil353/f4y3CQxrf6WTPwTMV1KvXRuFQC 2NdvQ/0eoIkLA== From: Jeff Layton Date: Tue, 15 Sep 2026 12:57:44 -0400 Subject: [PATCH v3 3/5] NFSD: honour the userspace-rpcbind flag 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: <20260915-nfsd-norpcb-v3-3-3c60d49ade02@kernel.org> References: <20260915-nfsd-norpcb-v3-0-3c60d49ade02@kernel.org> In-Reply-To: <20260915-nfsd-norpcb-v3-0-3c60d49ade02@kernel.org> To: Trond Myklebust , Anna Schumaker , Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Donald Hunter , Shuah Khan Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=7048; i=jlayton@kernel.org; h=from:subject:message-id; bh=w8hVnSxhB7CSJMEamGUn2/nMsx+VxVbSzGGcAnKu1Dw=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqqXkqgGJI00OXowIak4rDsA6fnXZf60sN0f3Fd r2asm3Qgw6JAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaql5KgAKCRAADmhBGVaC FURrD/9T3zrxxfW1iWe7tLrLTI9FMlk86JNDMnu9uoEQXZbNIS2lruNz+wnheWwLr7r0QAE9bZO +vmG0kSaNXBdm2MWTiLiNndvZwUsuT6fiTXYjI+reqot1LM0evLMLmbdDJ9xNGbainr1ZGkwqqG srjt1QP70k+QGgeVj9Ukd1IZ/c4GkUYoNdYaQaXbvObUbFvd2t6SvWvBeHTONxO0owTLnyn3gEX QAmSOX5GD+dgz6CB60BMDMV2XSVJaTFMdPJRo9X1Bmi0AtSMwMxQY2bMBRemt0D7WqlFw9s3e5d ix7io343525ISi1/FH+8rnNKYTay0aQ+FxE97Ib8wgs4Pj9OL8z77nUt8OQQnmNfgXfdAQkIGSc yQ6a61+oWg5ZgRAiWf5YlJoKfs4TmSvh/XXRLc5FxDAtLoo2Vh98QmhTdZU5tKZ8WHy1oqEiDDn yTMfoX8fqUve9g+orUV5fuD2UQggdUBXcnNcZKDQsBCjiin4J1QB4bEb9ScoTKaoWapEUUYKZcE dFuE/wNQLMDVZXvtfn02vlNxsGkDlTRb/y75gwVvJjkti/fydKhXRQgn5HBxclAQLkBiYsy5QWx T29wiQQsRXoCF5g0eVKQ8XoUR6uKWDV2Jjn0HvEYnft4pR92SAGr/8KWO7KynII9euf8276KrOM Bb1AEWL6h8gAUpg== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 A listener_set request that carries userspace-rpcbind now sets sv_no_rpcbind on the serv. The kernel then makes no rpcbind call at all, avoiding synchronous rpcbind RPCs under nfsd_mutex. Ownership cannot change under a live serv. An empty listener list is exempt: it destroys the serv, and nfsd_destroy_serv() drops whatever svc_bind() took either way, so teardown is not an ownership change. Without the exemption a caller that never learned about the flag could not shut nfsd down, and the extack blamed an ownership change it had not asked for. nfsd_nl_validate_listeners() returns the entry count so the gate can tell the two cases apart. The legacy portlist add-fd interface refuses to run against such a serv. nfsd_create_serv() returns early when the serv exists, so it cannot flip ownership back, svc_addsock() does not pass SVC_SOCK_ANONYMOUS and so expects svc_register() to register the listener, and there is no way to tell the rpcbind owner about it. -EBUSY, matching listener_set. add-xprt already passes SVC_SOCK_ANONYMOUS and never registered anything. lockd is unaffected. It owns a separate svc_serv and still registers NLM. Assisted-by: LLM Signed-off-by: Jeff Layton --- fs/nfsd/nfsctl.c | 49 ++++++++++++++++++++++++++++++++++++++----------- fs/nfsd/nfsd.h | 2 +- fs/nfsd/nfssvc.c | 14 ++++++++++++-- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 1be8f98a293d..6e57e4d20e75 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -748,7 +748,14 @@ static ssize_t __write_ports_addfd(char *buf, struct n= et *net, const struct cred return -EINVAL; trace_nfsd_ctl_ports_addfd(net, fd); =20 - err =3D nfsd_create_serv(net); + /* + * svc_register() is a no-op once userland owns rpcbind, and this + * interface has no way to hand the new listener to that owner. + */ + if (nn->nfsd_serv && nn->nfsd_serv->sv_no_rpcbind) + return -EBUSY; + + err =3D nfsd_create_serv(net, false); if (err !=3D 0) return err; =20 @@ -780,7 +787,7 @@ static ssize_t __write_ports_addxprt(char *buf, struct = net *net, const struct cr return -EINVAL; trace_nfsd_ctl_ports_addxprt(net, transport, port); =20 - err =3D nfsd_create_serv(net); + err =3D nfsd_create_serv(net, false); if (err !=3D 0) return err; =20 @@ -2022,7 +2029,8 @@ static bool nfsd_nl_transport_supported(const char *n= ame) * 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. + * Return: the number of entries if every entry is valid, or a negative + * errno otherwise. */ static int nfsd_nl_validate_listeners(struct genl_info *info) { @@ -2076,7 +2084,7 @@ static int nfsd_nl_validate_listeners(struct genl_inf= o *info) } } =20 - return 0; + return count; } =20 /** @@ -2095,11 +2103,13 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, = struct genl_info *info) unsigned int rpcb_failures; const struct nlattr *attr; bool skipped_rpcb =3D false; + bool userspace_rpcbind; bool bad_rpcb =3D false; struct svc_serv *serv; LIST_HEAD(permsocks); struct nfsd_net *nn; bool delete =3D false; + int nlisteners; int err, rem; =20 /* @@ -2107,19 +2117,36 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, = struct genl_info *info) * malformed request fails cleanly without creating a serv or touching * the existing listeners. */ - err =3D nfsd_nl_validate_listeners(info); - if (err) - return err; + nlisteners =3D nfsd_nl_validate_listeners(info); + if (nlisteners < 0) + return nlisteners; + + userspace_rpcbind =3D nla_get_flag(info->attrs[NFSD_A_SERVER_SOCK_USERSPA= CE_RPCBIND]); =20 mutex_lock(&nfsd_mutex); =20 - err =3D nfsd_create_serv(net); + nn =3D net_generic(net, nfsd_net_id); + + /* + * An empty list destroys the serv, and nfsd_destroy_serv() drops + * whatever svc_bind() took either way, so teardown is not an + * ownership change. Only a request that leaves a listener standing + * has to agree with the serv it found. + */ + if (nlisteners && nn->nfsd_serv && + nn->nfsd_serv->sv_no_rpcbind !=3D userspace_rpcbind) { + NL_SET_ERR_MSG(info->extack, + "cannot change rpcbind ownership while a server exists"); + mutex_unlock(&nfsd_mutex); + return -EBUSY; + } + + err =3D nfsd_create_serv(net, userspace_rpcbind); if (err) { mutex_unlock(&nfsd_mutex); return err; } =20 - nn =3D net_generic(net, nfsd_net_id); serv =3D nn->nfsd_serv; =20 spin_lock_bh(&serv->sv_lock); @@ -2213,12 +2240,12 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, = struct genl_info *info) continue; } =20 - flags =3D skipped_rpcb ? SVC_SOCK_ANONYMOUS : 0; + flags =3D (userspace_rpcbind || skipped_rpcb) ? SVC_SOCK_ANONYMOUS : 0; ret =3D svc_xprt_create_from_sa(serv, xcl_name, net, sa, flags, current_cred()); =20 hit_rpcb =3D false; - if (!skipped_rpcb && + if (!userspace_rpcbind && !skipped_rpcb && svc_rpcb_failure_count(serv) !=3D rpcb_failures) { skipped_rpcb =3D true; hit_rpcb =3D true; diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index a145294c59c8..dcce45d58322 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -119,7 +119,7 @@ enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVA= IL }; int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change); int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op = change); void nfsd_reset_versions(struct nfsd_net *nn); -int nfsd_create_serv(struct net *net); +int nfsd_create_serv(struct net *net, bool no_rpcbind); void nfsd_destroy_serv(struct net *net); =20 #ifdef CONFIG_DEBUG_FS diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index c04ef9d180ce..ef520d0562d6 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -607,7 +607,14 @@ struct svc_rqst *nfsd_current_rqst(void) return NULL; } =20 -int nfsd_create_serv(struct net *net) +/** + * nfsd_create_serv - create the svc_serv for a namespace if it has none + * @net: network namespace to operate within + * @no_rpcbind: true if the caller registers the listeners with rpcbind + * + * Return: 0 on success or a negative errno. + */ +int nfsd_create_serv(struct net *net, bool no_rpcbind) { int error; struct nfsd_net *nn =3D net_generic(net, nfsd_net_id); @@ -635,6 +642,9 @@ int nfsd_create_serv(struct net *net) return -ENOMEM; } =20 + /* svc_bind() reads this, so set it first. */ + serv->sv_no_rpcbind =3D no_rpcbind; + error =3D svc_bind(serv, net); if (error < 0) { svc_destroy(&serv); @@ -775,7 +785,7 @@ nfsd_svc(int n, int *nthreads, struct net *net, const s= truct cred *cred, const c strscpy(nn->nfsd_name, scope ? scope : utsname()->nodename, sizeof(nn->nfsd_name)); =20 - error =3D nfsd_create_serv(net); + error =3D nfsd_create_serv(net, false); if (error) goto out; serv =3D nn->nfsd_serv; --=20 2.55.0 From nobody Fri Sep 25 07:22:10 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 6F1174BD0F1; Tue, 15 Sep 2026 16:58:29 +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=1789491515; cv=none; b=GtVtSPh1XFwbO7Ef7z+zSaRkRV2aPIgmAZS0ko3tZNsUtvdLOKjvpyV/dlSyqjh+Je9LNyoy480Vuc89F78LKUpuQSGChv6LVvYelvxSUEz6W9xjSjz9wDgPqUzWQnhywmVi4+TS4OGCaIWi3ZQgAipDcdsxjob5QQHZ6X+7OEQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789491515; c=relaxed/simple; bh=ZFJ4FIvGlBPWqrpHWIRu0z11kYO2hDsLf6Lh3mDHM8w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=dGfINguqALD5KQ6JYqyGI/YNUv1SJ78onQOJW6eOfzpIt/y9S3J1EgYbXoEGbzT+6HF2zNWdgDIx6RcC+FydDEWrQ3tUCp3gqkRHp6aISZmrhTMcJfNWjbMufYWKPjsJmzaC+d92o7/+RNGJF51YcOaOVSGLhQ7A8tZRHIKY9LA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=I90ad8/W; 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="I90ad8/W" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CDE6A1F00899; Tue, 15 Sep 2026 16:58:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789491507; bh=7pvwtcbJiVJTSoXq0mtTQ4KVaqoIA/SIQAPR+4GBHWs=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=I90ad8/WcpHCZqYhA/cUXLyh76jyqbN1Q7Q0XQxvK5Et5GpSqUsPW8Ltk3CyzFyrN 99k7JRX0Lb/HKjazt4yWtSg8NGenj+z2RGXmOpvD9M+62Y95MN2uU8ADfWgNPl0C7O u/KAVRTmes0fNEHwLOv7EBVdEM7JW1YZ6nq9+U2muOu+bIw7N13/UZXzBeewiHW/Qn r8Tlrl+jGRe6rFj0QXqK5KQW6XPahHN7A30B/lteO+oyf3PL3fX1TusRnm4tjptJcV T9n2yCbYpEOEuVkbsk5Ct+zMGDvcpu0jE8Rp1ZXUNSz9pR8RXRMBMiBG2+nK9JVKi1 uZ7YUHi2b1FtA== From: Jeff Layton Date: Tue, 15 Sep 2026 12:57:45 -0400 Subject: [PATCH v3 4/5] NFSD: report registerable programs in the listener_set reply 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: <20260915-nfsd-norpcb-v3-4-3c60d49ade02@kernel.org> References: <20260915-nfsd-norpcb-v3-0-3c60d49ade02@kernel.org> In-Reply-To: <20260915-nfsd-norpcb-v3-0-3c60d49ade02@kernel.org> To: Trond Myklebust , Anna Schumaker , Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Donald Hunter , Shuah Khan Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=10561; i=jlayton@kernel.org; h=from:subject:message-id; bh=ZFJ4FIvGlBPWqrpHWIRu0z11kYO2hDsLf6Lh3mDHM8w=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqqXkqvrZiCLj/vfU4YOwZINZRA/qBfEItD63zy TwXZ+epFVqJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaql5KgAKCRAADmhBGVaC FUtLEACioo2yImbWDawoQ3eV4Vuxhpd4fk+7gg2eZ5htwtg6Jtjii8tFxq5sbYVS+j4Yj3sm79w BUDXJ2NGAo8GBRjZO2OsydZ6CRkf7DWTvbd16NtNFZDYmbkwFxq/P8mXRXcrszNQpe7ycwcAdhy 8sxgUKEG+GbM039ngAqxuauiD8lbOz0Ik6pM6FtnBolk2kiyCIITHbOqN7i8BOciWUGCyLZNSBh Idk475LleijWOFpVtHrXt18MQq4EMOlkM2zsCE6WIvXG/ZNPJ6LnPuiOKbu4vXbQxqQTTaNrluj SdmonfvdfJbQ0fKTyRY8rNzSsyevBXzjo4YkmFhAEVXEvnByAK+Z9uxatH7Tc5AiXmwWq055Dkx 5nauWiXDrMIVu+bCfobnjsLCMUeNvwDit7okWRQAf0VCx/31c1MAVOf+nlrPc0G1OcSy9N1td7v d6Oj5qBd41EgX9o/0SFI/TG2G753SwIt/QUTFwe0LkuFuTDkZY0fFPvNvKjflbCRa1rL1C88kbP j4JARpRfzW2GtY0lZtCbaNSIhVfpXTbm8GPC/6i1OmDDtZ4DHdnmOtext3tAZgY6E6RM1jqllGd f28OBR0ZdSVz3c4PTucQ9obwtAq4sTq3keuZjKHoZlm4eWrBnYB1riq/4DFlCzqQ22r/M8Kz6NO 9wZnEusmVrm2ZuA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 A caller that owns rpcbind must know what to register. It cannot work that out for itself: nfsd_acl_version[] is built by CONFIG_NFSD_V2_ACL and CONFIG_NFSD_V3_ACL, and no netlink command reports what is in it. Reply to a listener_set that carried userspace-rpcbind with the programs and versions that nfsd would have registered, plus the listeners to register them for. Send the reply only when the request asked for it, so an older caller still gets a bare ack. nfsd_version_registerable() answers the same question for the reply and for nfsd_rpcbind_set(), so nfsd_acl_rpcbind_set() goes away. It needs no nfsacl-specific test: nfsd_acl_version[] has entries only at 2 and 3, so the NULL pg_vers[] check already rejects everything nfsd_support_acl_version() would have. Listeners are reported only for the TCP and UDP classes, which is what XPT_RPCB_UNREG marks. RDMA never reached rpcbind from the kernel either, so there is nothing for the owner to register on its behalf. The reply is sized from its contents rather than GENLMSG_DEFAULT_SIZE, which listener_get uses and which overflows at ~27 listeners. The cost is a large contiguous skb: the addr nest carries a full sockaddr_storage, as listener_get's does, so a request at NFSD_NL_LISTENER_MAX needs ~148K and alloc_skb() lands on an order-6 page allocation that can fail. Nothing rolls back on failure, matching the rest of the command, so the extack says the listeners are up and that a retry fetches the reply. Retrying is safe: a request that matches the running set recreates nothing. Assisted-by: LLM Signed-off-by: Jeff Layton --- fs/nfsd/nfsctl.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++= ++++ fs/nfsd/nfsd.h | 2 + fs/nfsd/nfssvc.c | 52 ++++++++++++-------- 3 files changed, 181 insertions(+), 19 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 6e57e4d20e75..f32311f2d7cf 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2087,6 +2087,128 @@ static int nfsd_nl_validate_listeners(struct genl_i= nfo *info) return count; } =20 +static size_t nfsd_nl_listener_set_msgsize(struct svc_serv *serv) +{ + size_t size =3D GENL_HDRLEN + /* genlmsg_iput() */ + nla_total_size(0); /* userspace-rpcbind */ + struct svc_xprt *xprt; + unsigned int p; + + lockdep_assert_held(&nfsd_mutex); + + for (p =3D 0; p < serv->sv_nprogs; p++) + size +=3D serv->sv_programs[p].pg_nvers * + (nla_total_size(0) + /* rpcbind nest */ + nla_total_size(sizeof(u32)) + /* program */ + nla_total_size(sizeof(u32)) + /* version */ + nla_total_size(sizeof(u32))); /* flags */ + + spin_lock_bh(&serv->sv_lock); + list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) { + if (!test_bit(XPT_RPCB_UNREG, &xprt->xpt_flags)) + continue; + size +=3D nla_total_size(0) + /* addr nest */ + nla_total_size(strlen(xprt->xpt_class->xcl_name) + 1) + + nla_total_size(sizeof(struct sockaddr_storage)); + } + spin_unlock_bh(&serv->sv_lock); + + return size; +} + +static struct sk_buff * +nfsd_nl_listener_set_msg(struct genl_info *info, struct net *net, + struct svc_serv *serv) +{ + struct svc_xprt *xprt; + struct sk_buff *skb; + unsigned int p, i; + void *hdr; + int err; + + lockdep_assert_held(&nfsd_mutex); + + skb =3D genlmsg_new(nfsd_nl_listener_set_msgsize(serv), GFP_KERNEL); + if (!skb) + return ERR_PTR(-ENOMEM); + + hdr =3D genlmsg_iput(skb, info); + if (!hdr) { + err =3D -EMSGSIZE; + goto err_free_msg; + } + + if (nla_put_flag(skb, NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND)) { + err =3D -EMSGSIZE; + goto err_free_msg; + } + + for (p =3D 0; p < serv->sv_nprogs; p++) { + const struct svc_program *progp =3D &serv->sv_programs[p]; + + for (i =3D 0; i < progp->pg_nvers; i++) { + struct nlattr *attr; + u32 flags =3D 0; + + if (!nfsd_version_registerable(net, progp, i)) + continue; + + if (progp->pg_vers[i]->vs_need_cong_ctrl) + flags |=3D NFSD_RPCBIND_FLAGS_NO_UDP; + + attr =3D nla_nest_start(skb, NFSD_A_SERVER_SOCK_RPCBIND); + if (!attr) { + err =3D -EMSGSIZE; + goto err_free_msg; + } + if (nla_put_u32(skb, NFSD_A_RPCBIND_PROGRAM, + progp->pg_prog) || + nla_put_u32(skb, NFSD_A_RPCBIND_VERSION, i) || + (flags && nla_put_u32(skb, NFSD_A_RPCBIND_FLAGS, + flags))) { + err =3D -EMSGSIZE; + goto err_free_msg; + } + nla_nest_end(skb, attr); + } + } + + spin_lock_bh(&serv->sv_lock); + list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) { + struct nlattr *attr; + + if (!test_bit(XPT_RPCB_UNREG, &xprt->xpt_flags)) + continue; + + attr =3D nla_nest_start(skb, NFSD_A_SERVER_SOCK_ADDR); + if (!attr) { + err =3D -EMSGSIZE; + goto err_serv_unlock; + } + + if (nla_put_string(skb, NFSD_A_SOCK_TRANSPORT_NAME, + xprt->xpt_class->xcl_name) || + nla_put(skb, NFSD_A_SOCK_ADDR, + sizeof(struct sockaddr_storage), + &xprt->xpt_local)) { + err =3D -EMSGSIZE; + goto err_serv_unlock; + } + + nla_nest_end(skb, attr); + } + spin_unlock_bh(&serv->sv_lock); + + genlmsg_end(skb, hdr); + return skb; + +err_serv_unlock: + spin_unlock_bh(&serv->sv_lock); +err_free_msg: + nlmsg_free(skb); + return ERR_PTR(err); +} + /** * nfsd_nl_listener_set_doit - set the nfs running sockets * @skb: reply buffer @@ -2100,6 +2222,7 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, st= ruct genl_info *info) const struct nlattr *bad_attr =3D NULL; struct svc_xprt *xprt, *tmp; const char *bad_xprt =3D NULL; + struct sk_buff *rskb =3D NULL; unsigned int rpcb_failures; const struct nlattr *attr; bool skipped_rpcb =3D false; @@ -2289,12 +2412,35 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, = struct genl_info *info) "rpcbind did not answer, some listeners are not registered"); } =20 + /* + * Build the reply before the serv can go away, and only on success. + * A caller that got an errno has nothing to register. + */ + if (!err && userspace_rpcbind) { + rskb =3D nfsd_nl_listener_set_msg(info, net, serv); + if (IS_ERR(rskb)) { + err =3D PTR_ERR(rskb); + rskb =3D NULL; + /* + * The listeners are up and the errno alone reads as + * if nothing happened. Retrying is safe: a request + * that matches the running set recreates nothing. + */ + NL_SET_ERR_MSG(info->extack, + "listeners are up but the reply could not be built; retry to fe= tch it"); + } + } + if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks)) nfsd_destroy_serv(net); =20 out_unlock_mtx: mutex_unlock(&nfsd_mutex); =20 + /* rskb is only built once err is known to be zero. */ + if (rskb) + return genlmsg_reply(rskb, info); + return err; } =20 diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index dcce45d58322..69e3e92b3ec1 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -117,6 +117,8 @@ extern const struct svc_version localio_version1; =20 enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL }; int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change); +bool nfsd_version_registerable(struct net *net, + const struct svc_program *progp, u32 version); int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op = change); void nfsd_reset_versions(struct nfsd_net *nn); int nfsd_create_serv(struct net *net, bool no_rpcbind); diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index ef520d0562d6..cbc989238710 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -41,11 +41,6 @@ atomic_t nfsd_th_cnt =3D ATOMIC_INIT(0); static int nfsd(void *vrqstp); #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) -static int nfsd_acl_rpcbind_set(struct net *, - const struct svc_program *, - u32, int, - unsigned short, - unsigned short); static __be32 nfsd_acl_init_request(struct svc_rqst *, const struct svc_program *, struct svc_process_info *); @@ -127,7 +122,7 @@ struct svc_program nfsd_programs[] =3D { .pg_class =3D "nfsd", .pg_authenticate =3D svc_set_client, .pg_init_request =3D nfsd_acl_init_request, - .pg_rpcbind_set =3D nfsd_acl_rpcbind_set, + .pg_rpcbind_set =3D nfsd_rpcbind_set, }, #endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */ #if IS_ENABLED(CONFIG_NFS_LOCALIO) @@ -813,18 +808,6 @@ nfsd_support_acl_version(int vers) return false; } =20 -static int -nfsd_acl_rpcbind_set(struct net *net, const struct svc_program *progp, - u32 version, int family, unsigned short proto, - unsigned short port) -{ - if (!nfsd_support_acl_version(version) || - !nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST)) - return 0; - return svc_generic_rpcbind_set(net, progp, version, family, - proto, port); -} - static __be32 nfsd_acl_init_request(struct svc_rqst *rqstp, const struct svc_program *progp, @@ -859,12 +842,43 @@ nfsd_acl_init_request(struct svc_rqst *rqstp, } #endif =20 +/** + * nfsd_version_registerable - would nfsd register [@progp, @version]? + * @net: network namespace to query + * @progp: RPC program to query + * @version: RPC version to query + * + * Answers the question for a listener of any protocol. A caller that asks + * about one listener must apply vs_need_cong_ctrl itself. + * + * Return: true when the version is a candidate for rpcbind registration. + */ +bool nfsd_version_registerable(struct net *net, + const struct svc_program *progp, u32 version) +{ + struct nfsd_net *nn =3D net_generic(net, nfsd_net_id); + + /* + * nfsd_acl_version[] is built by CONFIG_NFSD_V2_ACL and + * CONFIG_NFSD_V3_ACL, so a NULL entry already answers for nfsacl and + * nfsd_support_acl_version() would add nothing here. + */ + if (version >=3D progp->pg_nvers || !progp->pg_vers[version]) + return false; + + /* nfslocalio is hidden and never reaches rpcbind. */ + if (progp->pg_vers[version]->vs_hidden) + return false; + + return nfsd_vers(nn, version, NFSD_TEST); +} + static int nfsd_rpcbind_set(struct net *net, const struct svc_program *progp, u32 version, int family, unsigned short proto, unsigned short port) { - if (!nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST)) + if (!nfsd_version_registerable(net, progp, version)) return 0; return svc_generic_rpcbind_set(net, progp, version, family, proto, port); --=20 2.55.0 From nobody Fri Sep 25 07:22:10 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 932254BE454; Tue, 15 Sep 2026 16:58:29 +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=1789491512; cv=none; b=JEKPK/vy4rJalgVagDQUGaHuGKm567IpzFMm9LAFkNnPf1kZPUnx1pMgRhcS4DQ5mGu6l++nY+0HBcg86ehww5//1WUq6XEYBEYpaejx52V/LHMhLjZ83v53BZU3FitfRo+47pNedi115H301Y2PeAFsySpqAJcNBaz4OjI1bX0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789491512; c=relaxed/simple; bh=QXI42khHV6az/md+69/vAfb5zIB4FyDI0xaH4hch+D0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JCbyPQz1Uwh58PyFmnHEh9NeX4mP/OLZQ6o8vBMePRYgSOefqr8XsqLxhEyy1T/rUiB5cRwPGUStDA5S2ilgKmoAs5EiuSczl6auNKlrTgiigeoDfSzH7B91GVuk7f5rqisaz9Vu4cMM/sLm9G0G0Ykyp57NoDM7rOVqmwzq/2g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=MCucyq/e; 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="MCucyq/e" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 76B671F00898; Tue, 15 Sep 2026 16:58:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789491508; bh=tsPUyFUbEv2OEChg9oIxZjltAFG13HxiE4JiMI1YBTA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=MCucyq/eN9XZPFfCA3tpIGVy/N6EbkJEZ2QrNqvduijMJ7KnU6dfnGOBhrmDOjaQN H0egufeYgQ5WnGhiGTB6sKUWCkp5486Ad4oc8RagxohnywlE6teQOfIMXQTnPnV0gf VQFgEhFgmH3C4W8htsF86uZPr94YSNUl5QCOFowyEwAmYv6V40VOte5YVW8auiJHob PsLgbZXbBvquiglvFDkfhmw6LfozXlEwjWdndGKv9DcFMgeAcYSR4Y9Vbixdox4op2 ska9tf9IHQiM458cnyfFGCinDwHbp4lvY7jnqpPaD8yrVM84JS4r7ZjbqDlOJcQ3pe rKoVbs1v878Bw== From: Jeff Layton Date: Tue, 15 Sep 2026 12:57:46 -0400 Subject: [PATCH v3 5/5] selftests/nfsd: exercise the userspace-rpcbind listener_set flag 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: <20260915-nfsd-norpcb-v3-5-3c60d49ade02@kernel.org> References: <20260915-nfsd-norpcb-v3-0-3c60d49ade02@kernel.org> In-Reply-To: <20260915-nfsd-norpcb-v3-0-3c60d49ade02@kernel.org> To: Trond Myklebust , Anna Schumaker , Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Donald Hunter , Shuah Khan Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=16445; i=jlayton@kernel.org; h=from:subject:message-id; bh=QXI42khHV6az/md+69/vAfb5zIB4FyDI0xaH4hch+D0=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqqXkrdBcAp4Q/O1XVBvkHr1lnWXat5SBLHBBUR EnzmPB+OcKJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaql5KwAKCRAADmhBGVaC FUSKEAC64TiU/LxdainQ3mDms/2ccoftMhYLZpY3IxDEKX6P+VIT2bxe7ywEBsZHo4dygNOgan2 MEcMo7FbLrRVZnwOCmcwLifgy8VOcjcz4Ozvj/TIz5WVwEtsE76lW5VEE2xjd1xaAMZRNd1bRhG ucB50RcIs38xfUmXuruQVmtwU3MssN5VSTwwmAoFDCGeDKxVEeMHfyUJ/dmgbtLcfDC/ga+d/ms yGmW892IzYS9Uw3z+uf79t4UrPSokpRimr5kSexe6vc4lab3mev9xFjdW73fEPOyYwtSjwiMYVk qeYvgnhgGarRPUBgQvOI5F2rjAUHm3nyGvUJsN+5ti3/Hnk/od96ieaC108QtoMtDwSlJrk4VfZ 8FeYkTueimRL12wu36PyI59cREswxUTDDVHJqzCLkqJXVJQ/5D5QkITsA3rCtGBqr9fiW7ERMAF 5/2KtYhpmL3PlIacfQ/HR+1KfsvSF8ofxDLBzUknLXr32OmF84+xbsEAzljzG0flgwX3TFwW4uB a/3ADm600ezUAtJg4wRoLCwltBBORCMjFuX+68cGuNRfDJa+D32bWCOaWkJRSmXtbUFtUq4hNiM Em4E88A5FjvYG019z/LSg4u6USL56rPUnGdBZLSdsve7EeMpzuhDvr1pzQywQ+GjslUjxBluelr 3OomgWTbkKQyC6g== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Cover the flag that moves rpcbind registration to the caller. rpcb_userspace_no_traffic is the one that matters. The errno says nothing about whether the kernel talked to rpcbind, but the stub counters do: a request that carries the flag must leave both at zero, which is what shows that svc_bind() and svc_register() were both skipped. The rest cover the reply and the ownership rule: - rpcb_userspace_reply. The ack flag, a non-empty program list, and the listener that came up. nfslocalio is hidden, so it must be absent. - rpcb_userspace_no_v4_udp. NFSv3 must be offered and must not carry no-udp; NFSv4 carries it, or the test skips where v4 is not built. - rpcb_userspace_nfsacl_follows_nfs. CONFIG_NFSD_V3_ACL is invisible to the test, so assert only that nfsacl v3 implies nfs v3. - rpcb_userspace_busy and rpcb_userspace_busy_reverse. Ownership cannot change under a live serv, in either direction. - rpcb_userspace_unflagged_teardown and its reverse. Teardown destroys the serv, so it is exempt from that rule in both directions. The follow-on listener_set is what proves the old serv went away rather than merely losing its listeners. - rpcb_userspace_portlist_busy. The legacy portlist add-fd write is refused against a flagged serv and still works against a kernel-owned one. nfsdfs is mounted in the test's netns, so the write cannot reach the host's nfsd. - rpcb_userspace_teardown. Create and destroy a flagged serv three times, then confirm that a kernel-owned serv still reaches rpcbind. An unbalanced rpcb_put_local() would break the last step. - rpcb_userspace_many_listeners. Forty listeners, which is more than GENLMSG_DEFAULT_SIZE would have held. A reply sized from that constant instead of from its contents returns -EMSGSIZE with every listener already up. genl_request_reply() grew an attribute argument, because listener_set now answers with a reply message rather than a bare ack. listener_set_rpcb() checks nlmsg_len against the recv() count before parsing. Forty listeners fit in its buffer, but a reply near NFSD_NL_LISTENER_MAX would not, and the parse loops trust nlmsg_len. Assisted-by: LLM Signed-off-by: Jeff Layton --- .../testing/selftests/nfsd/nfsd_netlink_listener.c | 404 +++++++++++++++++= +++- 1 file changed, 401 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/nfsd/nfsd_netlink_listener.c b/tools/t= esting/selftests/nfsd/nfsd_netlink_listener.c index 106360f87b99..bef7e8b1ee71 100644 --- a/tools/testing/selftests/nfsd/nfsd_netlink_listener.c +++ b/tools/testing/selftests/nfsd/nfsd_netlink_listener.c @@ -20,6 +20,7 @@ */ #define _GNU_SOURCE #include +#include #include #include #include @@ -46,6 +47,9 @@ =20 #include "../kselftest_harness.h" =20 +#define NFS_PROGRAM 100003 +#define NFS_ACL_PROGRAM 100227 + #define NLA_ALIGN4(len) (((len) + 3) & ~3) #define TEST_PORT 20049 #define MAX_LISTENERS 8 @@ -173,15 +177,24 @@ static int genl_request(uint8_t cmd, const char *attr= s, int attrs_len) return ret; } =20 -/* 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) +/* + * Send a command with attributes and return the full reply message; -errno + * on failure. NLM_F_ACK is left off: the kernel reports an error either w= ay, + * so the first message back is the reply whenever there is one. + */ +static int genl_request_reply_attrs(uint8_t cmd, const char *attrs, + int attrs_len, char *rbuf, size_t rlen) { - char buf[256]; + char buf[1 << 20]; struct nlmsghdr *nlh =3D (void *)buf; int fd =3D genl_open(); int off, n, ret; =20 off =3D genl_hdr(buf, nfsd_family, NLM_F_REQUEST, cmd); + if (attrs_len) { + memcpy(buf + off, attrs, attrs_len); + off +=3D attrs_len; + } nlh->nlmsg_len =3D off; =20 if (send(fd, buf, off, 0) < 0) @@ -198,6 +211,11 @@ static int genl_request_reply(uint8_t cmd, char *rbuf,= size_t rlen) return ret; } =20 +static int genl_request_reply(uint8_t cmd, char *rbuf, size_t rlen) +{ + return genl_request_reply_attrs(cmd, NULL, 0, rbuf, rlen); +} + /* Resolve the "nfsd" genl family id; -1 if not registered. */ static int genl_resolve_nfsd(void) { @@ -383,6 +401,119 @@ static int version_set_only(uint32_t major, uint32_t = minor) return genl_request(NFSD_CMD_VERSION_SET, attrs, NLA_ALIGN4(inner)); } =20 +/* ------------------- userspace-rpcbind ------------------- */ + +struct rpcb_ent { + uint32_t program; + uint32_t version; + uint32_t flags; +}; + +/* More listeners than GENLMSG_DEFAULT_SIZE would have held. */ +#define RPCB_MANY_LISTENERS 40 + +struct rpcb_reply { + int acked; /* saw NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND */ + int nprog; + struct rpcb_ent prog[16]; + int naddr; /* every addr nest, not just the stored ones */ + int nlistener; + struct listener_ent listener[MAX_LISTENERS]; +}; + +/* Append the userspace-rpcbind request flag. */ +static int put_userspace_rpcbind(char *buf, int off) +{ + return put_attr(buf, off, NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND, NULL, 0); +} + +static void parse_rpcb_nest(const struct nlattr *na, struct rpcb_ent *e) +{ + const struct nlattr *in =3D (const void *)((const char *)na + NLA_HDRLEN); + int ileft =3D na->nla_len - NLA_HDRLEN; + + memset(e, 0, sizeof(*e)); + while (ileft >=3D (int)NLA_HDRLEN) { + const void *d =3D (const char *)in + NLA_HDRLEN; + + switch (in->nla_type & NLA_TYPE_MASK) { + case NFSD_A_RPCBIND_PROGRAM: + e->program =3D *(const uint32_t *)d; + break; + case NFSD_A_RPCBIND_VERSION: + e->version =3D *(const uint32_t *)d; + break; + case NFSD_A_RPCBIND_FLAGS: + e->flags =3D *(const uint32_t *)d; + break; + } + ileft -=3D NLA_ALIGN4(in->nla_len); + in =3D (const void *)((const char *)in + NLA_ALIGN4(in->nla_len)); + } +} + +/* + * Send a listener_set that asks to own rpcbind, and parse the reply. + * Returns 0 on success or -errno. + */ +static int listener_set_rpcb(char *attrs, int off, struct rpcb_reply *out) +{ + char rbuf[64 * 1024]; + const struct nlmsghdr *nlh =3D (const void *)rbuf; + const struct nlattr *na; + int left, n; + + off =3D put_userspace_rpcbind(attrs, off); + memset(out, 0, sizeof(*out)); + + n =3D genl_request_reply_attrs(NFSD_CMD_LISTENER_SET, attrs, off, + rbuf, sizeof(rbuf)); + if (n < 0) + return n; + + /* + * A reply at NFSD_NL_LISTENER_MAX outgrows rbuf, and the parse loops + * below trust nlmsg_len. Refuse a short read rather than walk off it. + */ + if (n < (int)(NLMSG_HDRLEN + GENL_HDRLEN) || + nlh->nlmsg_len > (unsigned int)n) + return -EMSGSIZE; + + out->nlistener =3D parse_listener_get(rbuf, n, out->listener, + MAX_LISTENERS); + + na =3D (const void *)(rbuf + NLMSG_HDRLEN + GENL_HDRLEN); + left =3D nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; + while (left >=3D (int)NLA_HDRLEN) { + switch (na->nla_type & NLA_TYPE_MASK) { + case NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND: + out->acked =3D 1; + break; + case NFSD_A_SERVER_SOCK_ADDR: + out->naddr++; + break; + case NFSD_A_SERVER_SOCK_RPCBIND: + if (out->nprog < (int)ARRAY_SIZE(out->prog)) + parse_rpcb_nest(na, &out->prog[out->nprog++]); + break; + } + left -=3D NLA_ALIGN4(na->nla_len); + na =3D (const void *)((const char *)na + NLA_ALIGN4(na->nla_len)); + } + return 0; +} + +static struct rpcb_ent *find_rpcb(struct rpcb_reply *r, uint32_t prog, + uint32_t vers) +{ + int i; + + for (i =3D 0; i < r->nprog; i++) + if (r->prog[i].program =3D=3D prog && r->prog[i].version =3D=3D vers) + return &r->prog[i]; + return NULL; +} + /* Fetch the current listeners; returns count (>=3D0) or -errno. */ static int listener_get(struct listener_ent *out, int max) { @@ -1282,6 +1413,273 @@ TEST_F(nfsd_listener, rpcb_unreg_stop_after_failure) EXPECT_LE(three, one); } =20 +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D userspa= ce rpcbind =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D = */ + +/* + * The point of the flag: nfsd must make no rpcbind call at all. svc_bind() + * pings rpcbind at client creation and svc_register() calls it once per + * program and version, so a silent stub is what proves both were skipped. + */ +TEST_F(nfsd_listener, rpcb_userspace_no_traffic) +{ + struct rpcb_reply r; + char attrs[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, rpcb_conns()); + ASSERT_EQ(0, listener_set_rpcb(attrs, off, &r)); + EXPECT_EQ(0, rpcb_conns()); + EXPECT_EQ(0, rpcb_calls()); +} + +/* + * The reply has to tell the caller what to register. Without the program + * list it cannot know whether nfsacl is built in. + */ +TEST_F(nfsd_listener, rpcb_userspace_reply) +{ + struct rpcb_reply r; + char attrs[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, listener_set_rpcb(attrs, off, &r)); + EXPECT_EQ(1, r.acked); + EXPECT_GT(r.nprog, 0); + /* the listener came up and is named, so the caller knows the port */ + ASSERT_EQ(1, r.nlistener); + EXPECT_NE(NULL, find_listener(r.listener, r.nlistener, "tcp", + AF_INET, TEST_PORT)); + /* nfslocalio is hidden and must never be offered for registration */ + EXPECT_EQ(NULL, find_rpcb(&r, 400122, 1)); +} + +/* + * nfsd_nl_validate_listeners() allows far more listeners than the default + * genl buffer holds, so the reply has to be sized from its contents. If it + * is not, the listeners all come up and the caller still sees -EMSGSIZE. + */ +TEST_F(nfsd_listener, rpcb_userspace_many_listeners) +{ + char attrs[RPCB_MANY_LISTENERS * 64]; + struct rpcb_reply r; + int off =3D 0, i; + + for (i =3D 0; i < RPCB_MANY_LISTENERS; i++) + off =3D put_listener(attrs, off, "tcp", TEST_PORT + i); + + ASSERT_EQ(0, listener_set_rpcb(attrs, off, &r)); + EXPECT_EQ(1, r.acked); + EXPECT_GT(r.nprog, 0); + EXPECT_EQ(RPCB_MANY_LISTENERS, r.naddr); +} + +/* + * NFSv4 sets vs_need_cong_ctrl, so the kernel never registered it on UDP. + * The reply cannot filter it out, because the rule depends on the listene= r, + * so it must carry the flag instead. + */ +TEST_F(nfsd_listener, rpcb_userspace_no_v4_udp) +{ + struct rpcb_ent *v4, *v3; + struct rpcb_reply r; + char attrs[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, listener_set_rpcb(attrs, off, &r)); + + /* v3 is always built in and on by default, so it must be offered */ + v3 =3D find_rpcb(&r, NFS_PROGRAM, 3); + ASSERT_NE(NULL, v3); + EXPECT_EQ(0, v3->flags & NFSD_RPCBIND_FLAGS_NO_UDP); + + v4 =3D find_rpcb(&r, NFS_PROGRAM, 4); + if (!v4) + SKIP(return, "NFSv4 is not enabled"); + EXPECT_EQ(NFSD_RPCBIND_FLAGS_NO_UDP, + v4->flags & NFSD_RPCBIND_FLAGS_NO_UDP); +} + +/* + * nfsacl is the value userland cannot derive: CONFIG_NFSD_V3_ACL is not + * visible over netlink. Only assert self-consistency -- if the kernel + * offers nfsacl v3 then it must also offer nfs v3, since both gate on the + * same enabled version. + */ +TEST_F(nfsd_listener, rpcb_userspace_nfsacl_follows_nfs) +{ + struct rpcb_reply r; + char attrs[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, listener_set_rpcb(attrs, off, &r)); + if (find_rpcb(&r, NFS_ACL_PROGRAM, 3)) + EXPECT_NE(NULL, find_rpcb(&r, NFS_PROGRAM, 3)); +} + +/* + * svc_bind() decided whether to take the rpcb_users reference that teardo= wn + * drops, so ownership cannot flip under a live serv. + */ +TEST_F(nfsd_listener, rpcb_userspace_busy) +{ + struct rpcb_reply r; + char attrs[64], plain[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + int poff =3D put_listener(plain, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, listener_set_rpcb(attrs, off, &r)); + + /* same listeners, but now asking the kernel to own rpcbind */ + EXPECT_EQ(-EBUSY, listener_set(plain, poff)); + EXPECT_STRNE("", last_extack); +} + +/* And the same the other way round. */ +TEST_F(nfsd_listener, rpcb_userspace_busy_reverse) +{ + struct rpcb_reply r; + char attrs[64], plain[64]; + int poff =3D put_listener(plain, 0, "tcp", TEST_PORT); + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, listener_set(plain, poff)); + EXPECT_EQ(-EBUSY, listener_set_rpcb(attrs, off, &r)); +} + +#define NFSDFS_DIR "/run/nfsdfs" + +/* nfsdfs binds to the netns it is mounted in, so mount a private one */ +static int mount_nfsdfs(void) +{ + if (mkdir(NFSDFS_DIR, 0700) < 0 && errno !=3D EEXIST) + return -1; + return mount("nfsd", NFSDFS_DIR, "nfsd", 0, NULL); +} + +/* simple_transaction allows one write per open, so open every time */ +static int write_portlist(int sock) +{ + char buf[32]; + int fd, len, ret =3D 0; + + fd =3D open(NFSDFS_DIR "/portlist", O_WRONLY); + if (fd < 0) + return -errno; + len =3D snprintf(buf, sizeof(buf), "%d\n", sock); + if (write(fd, buf, len) < 0) + ret =3D -errno; + close(fd); + return ret; +} + +static int tcp_listener(int port) +{ + struct sockaddr_in sa =3D { + .sin_family =3D AF_INET, + .sin_addr.s_addr =3D htonl(INADDR_LOOPBACK), + .sin_port =3D htons(port), + }; + int fd =3D socket(AF_INET, SOCK_STREAM, 0); + + if (fd < 0) + return -1; + if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0 || + listen(fd, 1) < 0) { + close(fd); + return -1; + } + return fd; +} + +/* + * The legacy portlist interface cannot hand a listener to the rpcbind own= er + * and svc_register() will not register it, so it has to refuse. + */ +TEST_F(nfsd_listener, rpcb_userspace_portlist_busy) +{ + struct rpcb_reply r; + char attrs[64], plain[64]; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + int poff =3D put_listener(plain, 0, "tcp", TEST_PORT); + int sock; + + if (mount_nfsdfs() < 0) + SKIP(return, "cannot mount nfsdfs: %s", strerror(errno)); + + sock =3D tcp_listener(TEST_PORT + 1); + ASSERT_GE(sock, 0); + + ASSERT_EQ(0, listener_set_rpcb(attrs, off, &r)); + EXPECT_EQ(-EBUSY, write_portlist(sock)); + + /* the same write against a kernel-owned serv still works */ + ASSERT_EQ(0, listener_set_rpcb(attrs, 0, &r)); /* destroys the serv */ + ASSERT_EQ(0, listener_set(plain, poff)); + EXPECT_EQ(0, write_portlist(sock)); + + close(sock); +} + +/* + * rpcb_create_local() increments sn->rpcb_users and rpcb_put_local() + * decrements it. A serv that never took the reference must not drop it, or + * the next serv finds the count wrong. Cycle a few times, then confirm a + * kernel-owned serv can still reach rpcbind. + */ +TEST_F(nfsd_listener, rpcb_userspace_teardown) +{ + struct rpcb_reply r; + char attrs[64], plain[64]; + int off, poff, i; + + for (i =3D 0; i < 3; i++) { + off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + ASSERT_EQ(0, listener_set_rpcb(attrs, off, &r)); + /* empty list with no threads destroys the serv */ + ASSERT_EQ(0, listener_set_rpcb(attrs, 0, &r)); + ASSERT_EQ(0, rpcb_conns()); + } + + poff =3D put_listener(plain, 0, "tcp", TEST_PORT); + ASSERT_EQ(0, listener_set(plain, poff)); + EXPECT_GT(rpcb_conns(), 0); +} + +/* + * Teardown destroys the serv, so it is not an ownership change. A caller + * that never learned about the flag has to be able to shut nfsd down. + */ +TEST_F(nfsd_listener, rpcb_userspace_unflagged_teardown) +{ + struct listener_ent got[MAX_LISTENERS]; + char attrs[64], plain[64]; + struct rpcb_reply r; + int off =3D put_listener(attrs, 0, "tcp", TEST_PORT); + int poff =3D put_listener(plain, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, listener_set_rpcb(attrs, off, &r)); + ASSERT_EQ(1, r.nlistener); + + /* no flag, no listeners */ + EXPECT_EQ(0, listener_set(NULL, 0)); + EXPECT_EQ(0, listener_get(got, MAX_LISTENERS)); + + /* the serv is gone, so kernel ownership can be taken */ + EXPECT_EQ(0, listener_set(plain, poff)); +} + +/* And the same the other way round. */ +TEST_F(nfsd_listener, rpcb_userspace_flagged_teardown_of_kernel_serv) +{ + char attrs[64], plain[64]; + struct rpcb_reply r; + int poff =3D put_listener(plain, 0, "tcp", TEST_PORT); + + ASSERT_EQ(0, listener_set(plain, poff)); + EXPECT_EQ(0, listener_set_rpcb(attrs, 0, &r)); + EXPECT_EQ(0, listener_set(plain, poff)); +} + /* =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