From mboxrd@z Thu Jan 1 00:00:00 1970 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: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit 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_info *info) return 0; } +static size_t nfsd_nl_listener_set_msgsize(struct svc_serv *serv) +{ + size_t size = GENL_HDRLEN + /* genlmsg_iput() */ + nla_total_size(0); /* userspace-rpcbind */ + struct svc_xprt *xprt; + unsigned int p; + + lockdep_assert_held(&nfsd_mutex); + + for (p = 0; p < serv->sv_nprogs; p++) + size += 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 += 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 = genlmsg_new(nfsd_nl_listener_set_msgsize(serv), GFP_KERNEL); + if (!skb) + return ERR_PTR(-ENOMEM); + + hdr = genlmsg_iput(skb, info); + if (!hdr) { + err = -EMSGSIZE; + goto err_free_msg; + } + + if (nla_put_flag(skb, NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND)) { + err = -EMSGSIZE; + goto err_free_msg; + } + + for (p = 0; p < serv->sv_nprogs; p++) { + const struct svc_program *progp = &serv->sv_programs[p]; + + for (i = 0; i < progp->pg_nvers; i++) { + struct nlattr *attr; + u32 flags = 0; + + if (!nfsd_version_registerable(net, progp, i)) + continue; + + if (progp->pg_vers[i]->vs_need_cong_ctrl) + flags |= NFSD_RPCBIND_FLAGS_NO_UDP; + + attr = nla_nest_start(skb, NFSD_A_SERVER_SOCK_RPCBIND); + if (!attr) { + err = -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 = -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 = nla_nest_start(skb, NFSD_A_SERVER_SOCK_ADDR); + if (!attr) { + err = -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 = -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, struct genl_info *info) const struct nlattr *bad_attr = NULL; struct svc_xprt *xprt, *tmp; const char *bad_xprt = NULL; + struct sk_buff *rskb = NULL; unsigned int rpcb_failures; const struct nlattr *attr; bool skipped_rpcb = 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"); } + /* + * 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 = nfsd_nl_listener_set_msg(info, net, serv); + if (IS_ERR(rskb)) { + err = PTR_ERR(rskb); + rskb = NULL; + } + } + if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks)) nfsd_destroy_serv(net); out_unlock_mtx: mutex_unlock(&nfsd_mutex); + /* rskb is only built once err is known to be zero. */ + if (rskb) + return genlmsg_reply(rskb, info); + return err; } 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; 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 = 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[] = { .pg_class = "nfsd", .pg_authenticate = svc_set_client, .pg_init_request = nfsd_acl_init_request, - .pg_rpcbind_set = nfsd_acl_rpcbind_set, + .pg_rpcbind_set = 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; } -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 +/** + * 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 = net_generic(net, nfsd_net_id); + + if (version >= 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 == 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); -- 2.55.0