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. 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 | 23 +++++++++++++++++------ fs/nfsd/nfsd.h | 2 +- fs/nfsd/nfssvc.c | 14 ++++++++++++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 1be8f98a293d..63746334a46f 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -748,7 +748,7 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred return -EINVAL; trace_nfsd_ctl_ports_addfd(net, fd); - err = nfsd_create_serv(net); + err = nfsd_create_serv(net, false); if (err != 0) return err; @@ -780,7 +780,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); - err = nfsd_create_serv(net); + err = nfsd_create_serv(net, false); if (err != 0) return err; @@ -2095,6 +2095,7 @@ 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 = false; + bool userspace_rpcbind; bool bad_rpcb = false; struct svc_serv *serv; LIST_HEAD(permsocks); @@ -2111,15 +2112,25 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info) if (err) return err; + userspace_rpcbind = nla_get_flag(info->attrs[NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND]); + mutex_lock(&nfsd_mutex); - err = nfsd_create_serv(net); + nn = net_generic(net, nfsd_net_id); + + if (nn->nfsd_serv && nn->nfsd_serv->sv_no_rpcbind != userspace_rpcbind) { + NL_SET_ERR_MSG(info->extack, + "cannot change rpcbind ownership while a server exists"); + mutex_unlock(&nfsd_mutex); + return -EBUSY; + } + + err = nfsd_create_serv(net, userspace_rpcbind); if (err) { mutex_unlock(&nfsd_mutex); return err; } - nn = net_generic(net, nfsd_net_id); serv = nn->nfsd_serv; spin_lock_bh(&serv->sv_lock); @@ -2213,12 +2224,12 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info) continue; } - flags = skipped_rpcb ? SVC_SOCK_ANONYMOUS : 0; + flags = (userspace_rpcbind || skipped_rpcb) ? SVC_SOCK_ANONYMOUS : 0; ret = svc_xprt_create_from_sa(serv, xcl_name, net, sa, flags, current_cred()); hit_rpcb = false; - if (!skipped_rpcb && + if (!userspace_rpcbind && !skipped_rpcb && svc_rpcb_failure_count(serv) != rpcb_failures) { skipped_rpcb = true; hit_rpcb = 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_AVAIL }; 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); #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; } -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 = net_generic(net, nfsd_net_id); @@ -635,6 +642,9 @@ int nfsd_create_serv(struct net *net) return -ENOMEM; } + /* svc_bind() reads this, so set it first. */ + serv->sv_no_rpcbind = no_rpcbind; + error = svc_bind(serv, net); if (error < 0) { svc_destroy(&serv); @@ -775,7 +785,7 @@ nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const c strscpy(nn->nfsd_name, scope ? scope : utsname()->nodename, sizeof(nn->nfsd_name)); - error = nfsd_create_serv(net); + error = nfsd_create_serv(net, false); if (error) goto out; serv = nn->nfsd_serv; -- 2.55.0