NFSD updates for LTS 6.1.y
Subsystems (3)
[email protected]
maintainercover NFSD updates for LTS 6.1.y
From: Chuck Lever <[email protected]>
Address an NFSD crasher that was noted here:
https://lore.kernel.org/linux-nfs/[email protected]/
To apply the fix cleanly, backport a few NFSD patches into v6.1.y that have been in the other LTS kernels for a while.
Jeff Layton (1):
nfsd: drop the nfsd_put helper
NeilBrown (5):
nfsd: Simplify code around svc_exit_thread() call in nfsd()
nfsd: separate nfsd_last_thread() from nfsd_put()
NFSD: simplify error paths in nfsd_svc()
nfsd: call nfsd_last_thread() before final nfsd_put()
nfsd: don't call locks_release_private() twice concurrently
Trond Myklebust (1):
nfsd: Fix a regression in nfsd_setattr()
fs/nfsd/nfs4proc.c | 4 ++ fs/nfsd/nfs4state.c | 2 +- fs/nfsd/nfsctl.c | 32 ++++++++------ fs/nfsd/nfsd.h | 3 +- fs/nfsd/nfssvc.c | 85 ++++++++++---------------------------- fs/nfsd/vfs.c | 6 ++- include/linux/sunrpc/svc.h | 13 ------ 7 files changed, 51 insertions(+), 94 deletions(-)
Signature · 1 line
2.45.1
[email protected]
maintainerPATCH 1/7 nfsd: Simplify code around svc_exit_thread() call in nfsd()
From: NeilBrown <[email protected]>
[ Upstream commit 18e4cf915543257eae2925671934937163f5639b ]
Previously a thread could exit asynchronously (due to a signal) so some care was needed to hold nfsd_mutex over the last svc_put() call. Now a thread can only exit when svc_set_num_threads() is called, and this is always called under nfsd_mutex. So no care is needed.
Not only is the mutex held when a thread exits now, but the svc refcount is elevated, so the svc_put() in svc_exit_thread() will never be a final put, so the mutex isn't even needed at this point in the code.
--- fs/nfsd/nfssvc.c | 23 ----------------------- include/linux/sunrpc/svc.h | 13 ------------- 2 files changed, 36 deletions(-)
fs/nfsd/nfssvc.c +0 -23
@@ -983,31 +983,8 @@ nfsd(void *vrqstp)atomic_dec (&nfsd_th_cnt); out: -/* Take an extra ref so that the svc_put in svc_exit_thread() -* doesn't call svc_destroy() -*/ -svc_get (nn->nfsd_serv); -/* Release the thread */ svc_exit_thread (rqstp); - -/* We need to drop a ref, but may not drop the last reference -* without holding nfsd_mutex, and we cannot wait for nfsd_mutex as that -* could deadlock with nfsd_shutdown_threads() waiting for us. -* So three options are: -* - drop a non-final reference, -* - get the mutex without waiting -* - sleep briefly andd try the above again -*/ -while (!svc_put_not_last (nn->nfsd_serv)) { -if (mutex_trylock (&nfsd_mutex)) { -nfsd_put (net); -mutex_unlock (&nfsd_mutex); -break ; - } -msleep (20 ); - } -return 0 ; }
include/linux/sunrpc/svc.h +0 -13
@@ -123,19 +123,6 @@ static inline void svc_put(struct svc_serv *serv)kref_put (&serv->sv_refcnt, svc_destroy); } -/** -* svc_put_not_last - decrement non-final reference count on SUNRPC serv -* @serv: the svc_serv to have count decremented -* -* Returns: %true is refcount was decremented. -* -* If the refcount is 1, it is not decremented and instead failure is reported. -*/ -static inline bool svc_put_not_last (struct svc_serv *serv) -{ -return refcount_dec_not_one (&serv->sv_refcnt.refcount); -} -/* * Maximum payload size supported by a kernel RPC server. * This is use to determine the max number of pages nfsd is
Signature · 1 line
2.45.1
[email protected]
maintainerPATCH 2/7 nfsd: separate nfsd_last_thread() from nfsd_put()
In reply to [email protected]From: NeilBrown <[email protected]>
[ Upstream commit 9f28a971ee9fdf1bf8ce8c88b103f483be610277 ]
Now that the last nfsd thread is stopped by an explicit act of calling svc_set_num_threads() with a count of zero, we only have a limited number of places that can happen, and don't need to call nfsd_last_thread() in nfsd_put()
So separate that out and call it at the two places where the number of threads is set to zero.
Move the clearing of ->nfsd_serv and the call to svc_xprt_destroy_all() into nfsd_last_thread(), as they are really part of the same action.
nfsd_put() is now a thin wrapper around svc_put(), so make it a static inline.
nfsd_put() cannot be called after nfsd_last_thread(), so in a couple of places we have to use svc_put() instead.
--- fs/nfsd/nfsd.h | 7 ++++++- fs/nfsd/nfssvc.c | 52 ++++++++++++++++++------------------------------ 2 files changed, 25 insertions(+), 34 deletions(-)
fs/nfsd/nfsd.h +6 -1
@@ -97,7 +97,12 @@ int nfsd_pool_stats_open(struct inode *, struct file *);int nfsd_pool_stats_release (struct inode *,struct file *);void nfsd_shutdown_threads (struct net *net); -void nfsd_put (struct net *net); +static inline void nfsd_put (struct net *net) +{ +struct nfsd_net *nn =net_generic (net, nfsd_net_id); + +svc_put (nn->nfsd_serv); +}bool i_am_nfsd (void );
fs/nfsd/nfssvc.c +19 -33
114 lines not on this page · raw message
Signature · 1 line
2.45.1
[email protected]
maintainerPATCH 3/7 NFSD: simplify error paths in nfsd_svc()
In reply to [email protected]From: NeilBrown <[email protected]>
[ Upstream commit bf32075256e9dd9c6b736859e2c5813981339908 ]
The error paths in nfsd_svc() are needlessly complex and can result in a final call to svc_put() without nfsd_last_thread() being called. This results in the listening sockets not being closed properly.
The per-netns setup provided by nfsd_startup_new() and removed by nfsd_shutdown_net() is needed precisely when there are running threads. So we don't need nfsd_up_before. We don't need to know if it *was* up. We only need to know if any threads are left. If none are, then we must call nfsd_shutdown_net(). But we don't need to do that explicitly as nfsd_last_thread() does that for us.
So simply call nfsd_last_thread() before the last svc_put() if there are no running threads. That will always do the right thing.
Also discard:
pr_info("nfsd: last server has exited, flushing export cache\n");
It may not be true if an attempt to start the first server failed, and it isn't particularly helpful and it simply reports normal behaviour.
--- fs/nfsd/nfssvc.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-)
fs/nfsd/nfssvc.c +4 -10
@@ -562,7 +562,6 @@ static void nfsd_last_thread(struct net *net)return ;nfsd_shutdown_net (net); -pr_info ("nfsd: last server has exited, flushing export cache\n" );nfsd_export_flush (net); }
@@ -778,7 +777,6 @@ int nfsd_svc(int nrservs, struct net *net, const struct cred *cred) { int error; -bool nfsd_up_before;struct nfsd_net *nn =net_generic (net, nfsd_net_id);struct svc_serv *serv;
@@ -798,8 +796,6 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred) error =nfsd_create_serv (net);if (error) goto out; - - nfsd_up_before = nn->nfsd_net_up; serv = nn->nfsd_serv; error =nfsd_startup_net (net, cred);
@@ -807,17 +803,15 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred) goto out_put; error =svc_set_num_threads (serv,NULL , nrservs);if (error) - goto out_shutdown; + goto out_put; error = serv->sv_nrthreads; -if (error ==0 ) -nfsd_last_thread (net); -out_shutdown: -if (error <0 && !nfsd_up_before) -nfsd_shutdown_net (net); out_put:/* Threads now hold service active */ if (xchg (&nn->keep_active,0 ))svc_put (serv); + +if (serv->sv_nrthreads ==0 ) +nfsd_last_thread (net);svc_put (serv); out:mutex_unlock (&nfsd_mutex);
Signature · 1 line
2.45.1
[email protected]
maintainerPATCH 4/7 nfsd: call nfsd_last_thread() before final nfsd_put()
In reply to [email protected]From: NeilBrown <[email protected]>
[ Upstream commit 2a501f55cd641eb4d3c16a2eab0d678693fac663 ]
If write_ports_addfd or write_ports_addxprt fail, they call nfsd_put() without calling nfsd_last_thread(). This leaves nn->nfsd_serv pointing to a structure that has been freed.
So remove 'static' from nfsd_last_thread() and call it when the nfsd_serv is about to be destroyed.
--- fs/nfsd/nfsctl.c | 9 +++++++-- fs/nfsd/nfsd.h | 1 + fs/nfsd/nfssvc.c | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-)
fs/nfsd/nfsctl.c +7 -2
@@ -720,8 +720,10 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred err =svc_addsock (nn->nfsd_serv, net, fd, buf,SIMPLE_TRANSACTION_LIMIT , cred); -if (err >=0 && - !nn->nfsd_serv->sv_nrthreads && !xchg (&nn->keep_active,1 )) +if (err <0 && !nn->nfsd_serv->sv_nrthreads && !nn->keep_active) +nfsd_last_thread (net); +else if (err >=0 && + !nn->nfsd_serv->sv_nrthreads && !xchg (&nn->keep_active,1 ))svc_get (nn->nfsd_serv);nfsd_put (net);
@@ -771,6 +773,9 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct crsvc_xprt_put (xprt); } out_err: +if (!nn->nfsd_serv->sv_nrthreads && !nn->keep_active) +nfsd_last_thread (net); +nfsd_put (net);return err; }
fs/nfsd/nfsd.h +1 -0
@@ -139,6 +139,7 @@ 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); +void nfsd_last_thread (struct net *net);extern int nfsd_max_blksize;
fs/nfsd/nfssvc.c +1 -1
@@ -532,7 +532,7 @@ static struct notifier_block nfsd_inet6addr_notifier = {/* Only used under nfsd_mutex, so this atomic may be overkill: */ static atomic_t nfsd_notifier_refcount =ATOMIC_INIT (0 ); -static void nfsd_last_thread (struct net *net) +void nfsd_last_thread (struct net *net) {struct nfsd_net *nn =net_generic (net, nfsd_net_id);struct svc_serv *serv = nn->nfsd_serv;
Signature · 1 line
2.45.1
[email protected]
maintainerPATCH 5/7 nfsd: drop the nfsd_put helper
In reply to [email protected]From: Jeff Layton <[email protected]>
[ Upstream commit 64e6304169f1e1f078e7f0798033f80a7fb0ea46 ]
It's not safe to call nfsd_put once nfsd_last_thread has been called, as that function will zero out the nn->nfsd_serv pointer.
Drop the nfsd_put helper altogether and open-code the svc_put in its callers instead. That allows us to not be reliant on the value of that pointer when handling an error.
--- fs/nfsd/nfsctl.c | 31 +++++++++++++++++-------------- fs/nfsd/nfsd.h | 7 ------- 2 files changed, 17 insertions(+), 21 deletions(-)
fs/nfsd/nfsctl.c +17 -14
@@ -709,6 +709,7 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct credchar *mesg = buf;int fd, err;struct nfsd_net *nn =net_generic (net, nfsd_net_id); +struct svc_serv *serv; err =get_int (&mesg, &fd);if (err !=0 || fd <0 )
@@ -718,15 +719,15 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct credif (err !=0 )return err; - err =svc_addsock (nn->nfsd_serv, net, fd, buf,SIMPLE_TRANSACTION_LIMIT , cred); + serv = nn->nfsd_serv; + err =svc_addsock (serv, net, fd, buf,SIMPLE_TRANSACTION_LIMIT , cred); -if (err <0 && !nn->nfsd_serv->sv_nrthreads && !nn->keep_active) +if (err <0 && !serv->sv_nrthreads && !nn->keep_active)nfsd_last_thread (net); -else if (err >=0 && - !nn->nfsd_serv->sv_nrthreads && !xchg (&nn->keep_active,1 )) -svc_get (nn->nfsd_serv); +else if (err >=0 && !serv->sv_nrthreads && !xchg (&nn->keep_active,1 )) +svc_get (serv); -nfsd_put (net); +svc_put (serv);return err; }
@@ -740,6 +741,7 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct crstruct svc_xprt *xprt;int port, err;struct nfsd_net *nn =net_generic (net, nfsd_net_id); +struct svc_serv *serv;if (sscanf (buf,"%15s %5u" , transport, &port) !=2 )return -EINVAL ;
@@ -751,32 +753,33 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct crif (err !=0 )return err; - err =svc_xprt_create (nn->nfsd_serv, transport, net, + serv = nn->nfsd_serv; + err =svc_xprt_create (serv, transport, net,PF_INET , port,SVC_SOCK_ANONYMOUS , cred);if (err <0 ) goto out_err; - err =svc_xprt_create (nn->nfsd_serv, transport, net, + err =svc_xprt_create (serv, transport, net,PF_INET6 , port,SVC_SOCK_ANONYMOUS , cred);if (err <0 && err != -EAFNOSUPPORT ) goto out_close; -if (!nn->nfsd_serv->sv_nrthreads && !xchg (&nn->keep_active,1 )) -svc_get (nn->nfsd_serv); +if (!serv->sv_nrthreads && !xchg (&nn->keep_active,1 )) +svc_get (serv); -nfsd_put (net); +svc_put (serv);return 0 ; out_close: - xprt =svc_find_xprt (nn->nfsd_serv, transport, net,PF_INET , port); + xprt =svc_find_xprt (serv, transport, net,PF_INET , port);if (xprt !=NULL ) {svc_xprt_close (xprt);svc_xprt_put (xprt); } out_err: -if (!nn->nfsd_serv->sv_nrthreads && !nn->keep_active) +if (!serv->sv_nrthreads && !nn->keep_active)nfsd_last_thread (net); -nfsd_put (net); +svc_put (serv);return err; }
fs/nfsd/nfsd.h +0 -7
@@ -97,13 +97,6 @@ int nfsd_pool_stats_open(struct inode *, struct file *);int nfsd_pool_stats_release (struct inode *,struct file *);void nfsd_shutdown_threads (struct net *net); -static inline void nfsd_put (struct net *net) -{ -struct nfsd_net *nn =net_generic (net, nfsd_net_id); - -svc_put (nn->nfsd_serv); -} -bool i_am_nfsd (void );struct nfsdfs_client {
Signature · 1 line
2.45.1
[email protected]
maintainerPATCH 6/7 nfsd: don't call locks_release_private() twice concurrently
In reply to [email protected]From: NeilBrown <[email protected]>
[ Upstream commit 05eda6e75773592760285e10ac86c56d683be17f ]
It is possible for free_blocked_lock() to be called twice concurrently, once from nfsd4_lock() and once from nfsd4_release_lockowner() calling remove_blocked_locks(). This is why a kref was added.
It is perfectly safe for locks_delete_block() and kref_put() to be called in parallel as they use locking or atomicity respectively as protection. However locks_release_private() has no locking. It is safe for it to be called twice sequentially, but not concurrently.
This patch moves that call from free_blocked_lock() where it could race with itself, to free_nbl() where it cannot. This will slightly delay the freeing of private info or release of the owner - but not by much. It is arguably more natural for this freeing to happen in free_nbl() where the structure itself is freed.
This bug was found by code inspection - it has not been seen in practice.
--- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/nfsd/nfs4state.c +1 -1
@@ -318,6 +318,7 @@ free_nbl(struct kref *kref)struct nfsd4_blocked_lock *nbl; nbl = container_of(kref, struct nfsd4_blocked_lock, nbl_kref); +locks_release_private (&nbl->nbl_lock);kfree (nbl); }
@@ -325,7 +326,6 @@ static void free_blocked_lock(struct nfsd4_blocked_lock *nbl) {locks_delete_block (&nbl->nbl_lock); -locks_release_private (&nbl->nbl_lock);kref_put (&nbl->nbl_kref, free_nbl); }
Signature · 1 line
2.45.1
[email protected]
maintainerPATCH 7/7 nfsd: Fix a regression in nfsd_setattr()
In reply to [email protected]From: Trond Myklebust <[email protected]>
[ Upstream commit 6412e44c40aaf8f1d7320b2099c5bdd6cb9126ac ]
Commit bb4d53d66e4b ("NFSD: use (un)lock_inode instead of fh_(un)lock for file operations") broke the NFSv3 pre/post op attributes behaviour when doing a SETATTR rpc call by stripping out the calls to fh_fill_pre_attrs() and fh_fill_post_attrs().
Message-ID: <[email protected]>
--- fs/nfsd/nfs4proc.c | 4 ++++ fs/nfsd/vfs.c | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-)
fs/nfsd/nfs4proc.c +4 -0
@@ -1106,6 +1106,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, };struct inode *inode;__be32 status = nfs_ok; +bool save_no_wcc;int err;if (setattr->sa_iattr.ia_valid &ATTR_SIZE ) {
@@ -1131,8 +1132,11 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,if (status) goto out; + save_no_wcc = cstate->current_fh.fh_no_wcc; + cstate->current_fh.fh_no_wcc = true; status =nfsd_setattr (rqstp, &cstate->current_fh, &attrs,0 , (time64_t )0 ); + cstate->current_fh.fh_no_wcc = save_no_wcc;if (!status) status =nfserrno (attrs.na_labelerr);if (!status)
fs/nfsd/vfs.c +4 -2
@@ -475,7 +475,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,int accmode =NFSD_MAY_SATTR ;umode_t ftype =0 ;__be32 err; -int host_err; +int host_err =0 ;bool get_write_count;bool size_change = (iap->ia_valid &ATTR_SIZE );int retries;
@@ -533,6 +533,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, }inode_lock (inode); +fh_fill_pre_attrs (fhp);for (retries =1 ;;) {struct iattr attrs;
@@ -560,13 +561,14 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, attr->na_aclerr =set_posix_acl (&init_user_ns, inode,ACL_TYPE_DEFAULT , attr->na_dpacl); +fh_fill_post_attrs (fhp);inode_unlock (inode);if (size_change)put_write_access (inode); out:if (!host_err) host_err =commit_metadata (fhp); -return nfserrno (host_err); +return err !=0 ? err :nfserrno (host_err); }#if defined(CONFIG_NFSD_V4 )
Signature · 1 line
2.45.1
Greg KH
maintainerQuoted 8 lines · On Mon, Aug 26, 2024 at 11:06:56AM -0400, [email protected] wrote:
From: Chuck Lever <[email protected]>
Address an NFSD crasher that was noted here:
https://lore.kernel.org/linux-nfs/[email protected]/
To apply the fix cleanly, backport a few NFSD patches into v6.1.y that have been in the other LTS kernels for a while.
Now queued up, thanks.
greg k-h