From nobody Fri Sep 25 09:20:33 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 0601D490BE9; Mon, 14 Sep 2026 15:21: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=1789399271; cv=none; b=OvqND3WIr9isPAHy39+QQc1+YGAL3h23IoTotfC4+3H4HdFJ+RQ/KzET/mwsOVqqpI+Kd22vDGETm33VxDmvji4GpkfV/IPLJ8ky5wLIi4HQYGbzGK77EXQ1G+DqvwoLSWgfWC/2bPPXbo5XWUYPiu4zzVCBIf8BBKGtcgKQ57A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789399271; c=relaxed/simple; bh=UoWQHbkr2M9okG7sxUiZYcmC9RgSw9SeU8siqAcbPag=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=AwSHS3vLqf/a/E4yHcIHZoVuWw2BNy3HxkvxI4LQopmriqscBrP6b/izS0IWtKJnxHWIS43Y9MIm6bTSm9HzhIee0DikKxO3pNzfNUdmVIcp30zCOX9tvtD2HGMGw5I3wPiI15FKIHRSG+qWX3vSn+JwN/FdXBMvhxzX/3R8tek= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lnNrWhZ2; 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="lnNrWhZ2" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D8AE91F0089F; Mon, 14 Sep 2026 15:21:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789399268; bh=OeeQ+ZrmuUedsYcXYwL4tkTD/pZRXUuRAVVdzvyPLKE=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=lnNrWhZ28CuylTJyN5bYvqCwqo/0a0ufkmdTmGdyKzJPg1WhDP6Wuyboed9SDok39 SMKV79uy78e/SqgJrUosQF0r/m0ojQQGijB2JXNJ0Yx240MgvtuOY2UuFlCjEKFQmA xJPS2nsHr+rW2fQKTpRghlGEnxaIiFCBkHydSB4Br7/vh/O1fhVXg4eMwwTJDocpsC vhEVtpcOEeBkhnChmyF4wpTkJS33jiI+pd5BFCw77N39emNsOpxKElUZF7BICv+yHR iYmrzyTXDjIjbyPFxPpphe2slGtXEJCkzVAIszKG/QLr/EkicWQkQDf8Hs4Y89w+BZ aFuZqPvXwT7Zg== From: Jeff Layton Date: Mon, 14 Sep 2026 11:20:38 -0400 Subject: [PATCH v2 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: <20260914-nfsd-norpcb-v2-1-38263c6f59b2@kernel.org> References: <20260914-nfsd-norpcb-v2-0-38263c6f59b2@kernel.org> In-Reply-To: <20260914-nfsd-norpcb-v2-0-38263c6f59b2@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/ZANAwAKAQAOaEEZVoIVAcsmYgBqqBDgCdE5t0AWiC+tq/DyK1aJ5REgQTN6jRD2C NjM5M7n4RqJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaqgQ4AAKCRAADmhBGVaC FaBjD/44+zsXz4dbKODxBQbZ2FWifPWxGWn65dwbJTJfjeW1rxt0qCA0J/rbF5w/VoGcW5p1BSz ww9P9L4w2uDXp95VDuAYy6S22iixCkx2bK+6q4TSsmZsH/2Edn2Jlz4hJ2LooNtS9R2W5SgORqt tVlJFF2pK9OAbKYco6OkXyzihphqFP6j0nlO+uysqqBMgAoflBmXTACpOfdKjBl1kbcuJQ9XVsD LuslB+yursfZpHetF/M9q8ZTEtGt7OKP8cww8+vlrJgP5SWNvietEq3cbti5T07mlRfW3nE8Mur 88sJhCW7/e0kroegjcBtk7+G/oRBL8UskrjMXoMZCnuGbCvfmudMs2GFTYM5I2FR4JvAy8xpaZZ ZaHbNfv5iU1GblFsC+gEac3esA6prPDCNNK/sEB1WCPMXfZ1dlfH7AMhVYeGmBK0Fg/KiEgieCW txn+wUlS7AmDHvXV+0dmX019vzTgzhIjYa/vkbTCQqOAFWrp38wBEAq1sN9Rvsz7l8poK+ps2l/ vNEcKFnUTXjro1OtTP5d8MExtnTBXx1BEbyu47vt6eA5EHRfE/UY/OwWimXiSUwFN98ZaOvz63F twe6PPMV55LZz49A8ILpUHc5vjjBIkqwipJZ/8qJKRgdHmklnx8E7B0l7MudZvS5xOA8SK/caMM sZVagqTcLf5zRQA== 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 09:20:33 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 E5E1549C4AD; Mon, 14 Sep 2026 15:21: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=1789399273; cv=none; b=A+ieZS+im2mvzl1gwG2RGBGPA6szUpe8+aWnPZrhaH+8wsdiSLfaVsLz1+EeumkpMkOt3Tf1vn1Gj4444yZ490g7H+rHak3jeGOFqjXx1/N60G6nSq53XhFM/huposBLxY3I2BXGLGp8YPgCZUWqRtDg9KyJHazWmzP9DH4DVvc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789399273; c=relaxed/simple; bh=dYm5b7B6dTvNKjVYzuF7/FkF8IDWphASM2cJV/5trG4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=BMYYKI0ffrhN0areAZmPRUnE59KJOJnHrc36oXPKzB88xDsx0f6R/q54X9D5I6FXwywUbfdwJeGFoElZFAP07AJL9wTxS3AOM5Vv6dk7VKsFZwaZef8hl+uKuYE2gy7PeNutrj5GuU3E3IeTJBs+oaVSS8SEvE7JI71tYnblb0k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WZULdT1Z; 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="WZULdT1Z" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7AD641F008A0; Mon, 14 Sep 2026 15:21:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789399269; bh=QH3G09fgNYNq57/vr1pk3tneJpQFCpXfha0cp9uQYVE=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=WZULdT1ZFMIxjMQVyXq2Ppc0Hc+mXve0GEqQ4lG57+ogcFAof/O6c1Op+2SWlw/YT p0wbgGx/Mf43Xm10kRceJ8DoFdPbyLmw1MzprqbtFN8tkwpOMPLy72pEYXexIhJOTL ZXuXNlCnWquJdHXKeNvvlfcylhjs1QzjBKhgaqzi+xOr3KYwGfaJ9G/Xc+MwE5FLyB 01R3LATGI0njFWWLOXCo2kpCjLe1DUAbrquIN3P0KNmyIZzhQEbEKck690aWmyboYw bA8XzL/ujyDgkPn+XpC9n0EB1Pitr9A67vkPCDqi7wqE+DJGVa0OInD6UuI0pevzAl iaaH6i7UfAEfg== From: Jeff Layton Date: Mon, 14 Sep 2026 11:20:39 -0400 Subject: [PATCH v2 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: <20260914-nfsd-norpcb-v2-2-38263c6f59b2@kernel.org> References: <20260914-nfsd-norpcb-v2-0-38263c6f59b2@kernel.org> In-Reply-To: <20260914-nfsd-norpcb-v2-0-38263c6f59b2@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/ZANAwAKAQAOaEEZVoIVAcsmYgBqqBDgM9UnOJtjs+kmrTDh8zNwvUige3pJq4Oqd ZsKfiFIXyOJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaqgQ4AAKCRAADmhBGVaC FYNJEADAlpItC3535KsCrdoevvITcjyiwz1zQCVli0gAjR8t8pbpAykgE0B/RTqhz0PRerbDWCC /sZZvRkPrjmVtHsIi3YRF6j9jQjGwCI9YVXbMfRuDQ7ufqMtZ8cXrH9LISvveduEj5LXYSxaPeF XPyETfggB5rwm7j1B6jq/DQSWWgzr5IqNMIs+lV6Tdk3PFxapyeJ50A8eBtFMdNC+asKXqqmkKw uHQ9tPMPPxnAWv3FhA5HYQ1gbKPHAbBmX/38vxn5BSrUqduW/lu2MzxybdY30S2WcEDANNbogtA jSNPFQaGsCiA6L6M2JTpSysgq35SYY3e4pbiwL7qoN7zP/5/4TS/XAiaLX1XREbDM8gPonbMs9J IwKOf9FpeMD4yGhwf8qZ/qyvA0UmaYskAkPmQyMqIZpxUBD5FxaFqYRXmKyE88P14W0fIuF+IyJ aGWDT4O6DGGw1NGsGY2BoiYVSD1FeqAKJw7I2b6fYum+GYYo41CL1YLEXNLO0JFOs/Fuxu0Xvep uIRdLn6lgmu4+6zrFthR94Ayf3MfuyT+Brd1RcY24gGq3aThIhYw2z4tJQCI4UpQe2nCFLFoWpP emZeFJOo83s8LQa3S/HWvoUuWLWyQdfe+bSFzdpkWbpTDOn8HANVPv9BPLD9FR17jth4ig3/Dt2 nr8839tjnWRmSWw== 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 09:20:33 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 3C13D47F791; Mon, 14 Sep 2026 15:21:12 +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=1789399274; cv=none; b=i9eKRow7z8Y7BgR0j6QQD8QcvHEZbfTcSivhElYQ4cnm6cB4pPDCu9ORBazKkgCRcg5QwtFokMM3B8d7cjuyWRaseYtMB7lGm1NDZ4pV0S2dTepxRvwupckmky2aUA90zRyqHB6r2Cy1ZXdG1j9iX7xQeU9wXVIdo57KD0Sn3os= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789399274; c=relaxed/simple; bh=C3xajPKbFSHFqX0Wtv9izib5A53bSMe1dMWdnMH5wSM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oc5YFLFJqzDQs+XMrKOUwPAKAJoKDwN+R8aVjkgO4X6eNP7YWLpeBOZpVUqWmkx4xXwOaEGRrLONvoBL/aDW7k12ShWeRYsdyB4dC+zVVPPQCmHserwERi3Vdhp9Y3KnDWk3+ct/V5E+PtT19G2QGXH6o24A1pgYZUjMWwGgbzQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nrLc6nQo; 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="nrLc6nQo" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1C4A61F00899; Mon, 14 Sep 2026 15:21:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789399271; bh=VRH/H/+N8Z/Vfxv9jNxARCyZVqpjG1luBuWidiJcDi8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=nrLc6nQoC18zeNhjb/yn7dGI8EjwLR1G2/cdp53Fn4bpi6ZmZjCLDbZdV2pCO6tzg FMnDHIIdWhrIzIgwYxkyETW8vBh8kHop2UC8AMmOfbU8z/Hto8U2nH4CC6rTerzYYz bW/e3dlJMzdSqdb2jExGAQPuoX82w3KKKlNOdLI6PcVdAq5xYbfJQSJSKOA/N4Gmug wSYhWszT3dJLLdTMyRUiQTrQa8VRzt/8uqrmSZFKYeFe7kp0G54y5GdxwYQOQeFzKN 2/aDcHEytlGGqe2V/9XtFlMkrGjt4boJ3+vidS6TsrJuMngpvxYlPTG5zJyUp7U3H2 MgwJHBwi2uXDw== From: Jeff Layton Date: Mon, 14 Sep 2026 11:20:40 -0400 Subject: [PATCH v2 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: <20260914-nfsd-norpcb-v2-3-38263c6f59b2@kernel.org> References: <20260914-nfsd-norpcb-v2-0-38263c6f59b2@kernel.org> In-Reply-To: <20260914-nfsd-norpcb-v2-0-38263c6f59b2@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=5333; i=jlayton@kernel.org; h=from:subject:message-id; bh=C3xajPKbFSHFqX0Wtv9izib5A53bSMe1dMWdnMH5wSM=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqqBDg7RdvDBI+ET24j/jZku3r+TJNeYS1JTYzK QYG/FZNmnKJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaqgQ4AAKCRAADmhBGVaC FSVLD/0TwpxojxIrLEjzGxYZ9zNCaljn7UBiyvs0jDXCmnK0JnGgPXRJAQLeCANi6OzyW1dxzIT NyBAsTfkx4fK2fs+i0qkwZ3bVTn0LsGdokJHMqZJoRElRHx8BX4jlHbOgAUpQHXnFkZLJpWK+sR kVtc3N4Mtf1Xx7O51MIKS7/bpTDnrmhFqu2i+jLzFZat5ScLu+CFPlz39eI6f6whFFqLCLlmgye kbTZhNQQROiqY87YeNfQtVxhQ/rJ5LmflVKoJtViYjDPtVRjR/MYoW2PBBm9qHTsMHDnv+MY3+c EymtDR7D8TejIPu+qGqXylTvlkWXNIbr00wqMEJBSe2rPASoDDJZylHRfl00sURKJV4N9FxADfB 1WqlgvbGQMarVw1iqbiq0SzgniOIKmRlAvQiXBqyby+aN2T7sm06/X8jJWLGSbR26kSd4jIcZGY rjiHKQl35wwJAtVexy1BMSJtdThJltnQWZm44JrRbOaQPHsA0pidGm1e+ViPhy7V/V7E1QBV/FI 08GIBDow96D/+TkDMUMQZTaFX2N3lF1jEbGlLaiYN0jLxuHFqeu1FIEBwzpeNR0XBRXw9I/l3v9 KbmREU7ByzLIOA3pOMHttMbleGKlZzKE2gzCfOkec2YHus2eZnE44rduy+ZDxWIURA8gOtVMqC3 bu9761WAQ3I7mZw== 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. 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 | 30 ++++++++++++++++++++++++------ fs/nfsd/nfsd.h | 2 +- fs/nfsd/nfssvc.c | 14 ++++++++++++-- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 1be8f98a293d..207a1bed3928 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 @@ -2095,6 +2102,7 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, st= ruct 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); @@ -2111,15 +2119,25 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, = struct genl_info *info) if (err) return err; =20 + userspace_rpcbind =3D nla_get_flag(info->attrs[NFSD_A_SERVER_SOCK_USERSPA= CE_RPCBIND]); + mutex_lock(&nfsd_mutex); =20 - err =3D nfsd_create_serv(net); + nn =3D net_generic(net, nfsd_net_id); + + if (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 +2231,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 09:20:33 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 D03DC49F12C; Mon, 14 Sep 2026 15:21: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=1789399276; cv=none; b=Rw9mCZgppkYbIv8W6erekJ95cdi4yjSaMWFuDHKRsrLWTwEwVoktFgeAM5303+9KLsdegtxBraHvceyt+ea/FEtOma/2xKI4qGUehlsSgj2e7dIwX2ZrvAO5FXvc4wdxRXBXz6b6v4mGp33CaiJmCdMXQKlZM7zkVG7UzHhUG8w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789399276; c=relaxed/simple; bh=RQ8w37NXo6F8DQ9ATeocKHRDTpN6zbWjY1zPMWT6VNU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HC2FPF7qvObQ0vkGKUp4HhTcRKpz/W9fDqzFCU8GHvCln2LxB+lGbp4QC1cMRlZzWdQm41jrUCrHviAjscQiJJB2aL1VUVTFnImPBGi8Any9ZANg/VNjlpToo0miXAj44Qv39BWM/g+Uj3sPF6oJYnHdFaS7gx2DkpdtQGX+40Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Udx5O5m5; 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="Udx5O5m5" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B2C3F1F0089A; Mon, 14 Sep 2026 15:21:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789399273; bh=JcHrfNRy0FCOPdZ8/DfjR5IHyK2uId9GxDpiWccxoB8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Udx5O5m5iLGGhh0brl6e+gBNoblmUMe6tKMSSvBna0Yf0vFDP2XTwSIjloXSp0BLK s4shz7r03DnoTlDpuIoamL5E6jFIfByMAuN4oPhUlHM2U9MY4zt4jTdoeEkGIcxQ/2 zNJY7EgDKW1Gwfua1JQWYTVas2o+aPKukxk0CbPAwxrJQ5YxQn/JxEO2KIg41DEaVR hHNSqZGRG1m+HUnvsr/XgazhNdzyrZr2EMxBm3Gfd5eOCsBxdxfTK78aYI0u2SLLvc EtSLrHk3rQJfm9lBs6mBfrRdzb9UxuJOMxtbnnUnF1xpUQDq9i7CD6zyyqLZKrm9WY ftt1bn/qMVGKQ== From: Jeff Layton Date: Mon, 14 Sep 2026 11:20:41 -0400 Subject: [PATCH v2 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: <20260914-nfsd-norpcb-v2-4-38263c6f59b2@kernel.org> References: <20260914-nfsd-norpcb-v2-0-38263c6f59b2@kernel.org> In-Reply-To: <20260914-nfsd-norpcb-v2-0-38263c6f59b2@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=9145; i=jlayton@kernel.org; h=from:subject:message-id; bh=RQ8w37NXo6F8DQ9ATeocKHRDTpN6zbWjY1zPMWT6VNU=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqqBDg6F3/asIR3F0d1ICNQ2kpvnxgct3JcCjTB cCmyYFonpWJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaqgQ4AAKCRAADmhBGVaC Fd7oEACMolsvvmf0kGQw3rReJt9q2h3YIvu2J0gUevJCTqm9MJTPayIb5vkwvFI1U0SPs6xxVzd R84KiL1WIDRCYPs49QsBfPw07igYN4ZU8OLYTzGIXEGl+dOL+NNIYT6AyKU5RDoKRroEHYzzwiU o/O6OvDYqPqnTTjJHQ+18G74TJfSi939QqOB6rqh8gJCEShoap8OMQOHnEauHNvPd6ZT0acvQAn ReI2g7aOo3KlS/AFSxAcCPhNprHmWBYn7h0+8lXIFoC4Nroc9PHtkN3oXwyL2h4xgu4TanRIvf9 iR5u8XIKdv9JZ19RN5tmfcyv729H07h3OU9RDLdv5mVhHHrAc8JncD60tkeUwVnHfnENzpJeHV5 fvbxPbsXZILpXgbQZkaPzV5j2t0cbZ6ocXgH6GyAJy5boaRBRKABDKXDOOapSCmKynLyJLy5jOz 524EOSnKZ8PvA1Z5ixyaviC76IXwDoz+HDcFn/crQYFgEztlny/6ZK5JMspvPPk1JaoaZ0hQbIK 8di8/UZCH6W1IOnima1lE8gFG++v7xKkbuTBmgUUFk5WcFyQ/Y4B55/z0LGpfcLhYNLEny62/wZ jzu0qUIgbi8ThVvUZI2rugxRPwu4LB7Xo5elEqFCnBiPYoitNSk2BS8vQeVDtbCmGrzxcOW5j3k WWcgvMgVV9Ayl8Q== 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_support_acl_version() reads nfsd_acl_version[], which CONFIG_NFSD_V2_ACL and CONFIG_NFSD_V3_ACL build, and no netlink command reports that. 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. Assisted-by: LLM Signed-off-by: Jeff Layton --- fs/nfsd/nfsctl.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++= ++++ fs/nfsd/nfsd.h | 2 + fs/nfsd/nfssvc.c | 53 +++++++++++++-------- 3 files changed, 175 insertions(+), 19 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 207a1bed3928..bb09d89e860e 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2086,6 +2086,128 @@ static int nfsd_nl_validate_listeners(struct genl_i= nfo *info) return 0; } =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 @@ -2099,6 +2221,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; @@ -2280,12 +2403,28 @@ 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; + } + } + 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..890458c08f2e 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,44 @@ 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); + + 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; + +#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) + if (progp->pg_prog =3D=3D NFS_ACL_PROGRAM && + !nfsd_support_acl_version(version)) + return false; +#endif + + 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 09:20:33 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 CB0834A13BC; Mon, 14 Sep 2026 15:21:14 +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=1789399277; cv=none; b=m1cFqoK3Elaby1tZSYPKSSqJ7vna2r/LGzX9hGPkTztGvFLLaxZdiDVvf8q0erzFNLKv0Jpummwm+DlVkAIcEnAtCr7B3R2khBsDzMDxLLbVSYZyQQN6B01tg6MyZDtClRGsrUIzxDGuAiHYB0ER2oM1HyDWs6sH0lzh7HXglk8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789399277; c=relaxed/simple; bh=QIVMN7rgjkwwjFY+BrTCHo+3ef705rRpehg/zQzaK8A=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jsdMt8V+zY5zm1tqLcxjM5RKKQp9PpHwcceeGA0wLD/EmlXB5b/t4nPUs+C56mO433EKk/7/PAjZ9/mPQfsjkndXAbbfjgzyDxF8iYa0RHYOmT5TspNZFkuwwaj2vB0EHgYauVtGcIJYdmgxDA29trSenDtm3OZkczQ7xttF/P0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=VFrw/XDe; 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="VFrw/XDe" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 559561F00893; Mon, 14 Sep 2026 15:21:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789399274; bh=sfa/3+38oAWMK/z078GqcbtKSpzYJGZ3t0VL9DkNIU0=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=VFrw/XDeLs2rq4xtLimXzgG0sDcWVTXxCEuxYUS1rzlu5xaJlr5xkdhEPnnaGjM9n mXdDMfpbiV7PiCh1gElWtzoahZBmBppEFREs398mH0k1THK0yHvNLgPb8ElsQPmdam c+YhA5NOygVZyOeeFXslWC4H8z2E86vC2oIgHEBzR59I8mZj338I2ch/dLog4veVmA /rN8p2frGTFLhg8xmjfXLgZaDk3W3ilrjHdlomzrJZN34Fnp2sDNjSchMLVhwv5r+/ +PcOg/HUU0FI22lfHL5vqqNmBBqCQP7GMKbMnnXAA+Ds6PVYu8y4gmMzvAdPGwVyYP TweDzvV3vZrmg== From: Jeff Layton Date: Mon, 14 Sep 2026 11:20:42 -0400 Subject: [PATCH v2 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: <20260914-nfsd-norpcb-v2-5-38263c6f59b2@kernel.org> References: <20260914-nfsd-norpcb-v2-0-38263c6f59b2@kernel.org> In-Reply-To: <20260914-nfsd-norpcb-v2-0-38263c6f59b2@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=14588; i=jlayton@kernel.org; h=from:subject:message-id; bh=QIVMN7rgjkwwjFY+BrTCHo+3ef705rRpehg/zQzaK8A=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBqqBDgiKmHvjRho5CxkGytc6NSUdOsknHJOPikq GWm/2Xwu4GJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaqgQ4AAKCRAADmhBGVaC FSVrD/9zoXMrHESJ64iWJMX1yAZ5NKvkC/c+P5Mi70QG8Mx9/gYJcFL3H7ZKGpCbBCRLqPBhX8C gXrRumfm+Ji8XEymLJ79wRU0mXdkjmJqem74iENCAA7ARhLIX5TaYwYShmJD25NYHoh9djmYIsL PsVbYK5R9x28eTaTz6gSqmbW2u8vdR5t2pvqSdGKssd0cWXYWIfYWum/YmDKvEBp5qwdB9EUcgJ YxzkRygL+AZSqtGtHuJkEcDNIx6O/31Rbb3bJeo5vfvIw9I+Iv1d2+W//3tMVqvt22eqo7i7JOz NZLReS5wCsJRCz8+h6mSSK5vCPfs4XQ+FRkWi6ib+rVEqH9N3CWqJehCE5hkfx5HilSBK7cI5uO rYiyYa2ywy6oKMv5jRbaJ0bxIRMuAwQViPchpn5ubglvBbnNY5uTNmO2x4b0PLdftSg83XMrxgJ zDQ0fH6lsAPwFE1lUJIxmsYGOHB+ddrV+ZE7qAb2Ycy+l/wyXPBX9BzW+AsqBEpuu1AUylR85ft r4wYYpgfSaUJI4vKMNFXBc1C2BU8bCANIEoMLH+Fe+dwI28/sn7HcVi5+s0OT+xFO4ATpEHQ7n0 5SvmWo9liE6NvsA3xNJCYv6oN5R+2koVyncVhuOAxc0K4TcLI/QX3tO0YMhaq7OxW6lLBuRIRKq oIKp7C6o5QzdG0A== 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_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. Assisted-by: LLM Signed-off-by: Jeff Layton --- .../testing/selftests/nfsd/nfsd_netlink_listener.c | 361 +++++++++++++++++= +++- 1 file changed, 358 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..c01941ee262f 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,111 @@ 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; + + 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 +1405,238 @@ 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); +} + /* =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