Message ID | 20240304044304.3657-5-neilb@suse.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | nfsd: fix dadlock in move_to_close_lru() | expand |
On Mon, 2024-03-04 at 15:40 +1100, NeilBrown wrote: > move_to_close_lru() is currently called with ->st_mutex held. > This can lead to a deadlock as move_to_close_lru() waits for sc_count to > drop to 2, and some threads holding a reference might be waiting for the > mutex. These references will never be dropped so sc_count will never > reach 2. > > There can be no harm in dropping ->st_mutex before > move_to_close_lru() because the only place that takes the mutex is > nfsd4_lock_ol_stateid(), and it quickly aborts if sc_type is > NFS4_CLOSED_STID, which it will be before move_to_close_lru() is called. > > See also > https://lore.kernel.org/lkml/4dd1fe21e11344e5969bb112e954affb@jd.com/T/ > where this problem was raised but not successfully resolved. > > Signed-off-by: NeilBrown <neilb@suse.de> > Reviewed-by: Jeff Layton <jlayton@kernel.org> > Signed-off-by: Chuck Lever <chuck.lever@oracle.com> > --- > fs/nfsd/nfs4state.c | 11 +++++++---- > 1 file changed, 7 insertions(+), 4 deletions(-) > > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c > index 47e879d5d68a..05181b4a3ce8 100644 > --- a/fs/nfsd/nfs4state.c > +++ b/fs/nfsd/nfs4state.c > @@ -6998,7 +6998,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, > return status; > } > > -static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s) > +static bool nfsd4_close_open_stateid(struct nfs4_ol_stateid *s) > { > struct nfs4_client *clp = s->st_stid.sc_client; > bool unhashed; > @@ -7015,11 +7015,11 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s) > list_for_each_entry(stp, &reaplist, st_locks) > nfs4_free_cpntf_statelist(clp->net, &stp->st_stid); > free_ol_stateid_reaplist(&reaplist); > + return false; > } else { > spin_unlock(&clp->cl_lock); > free_ol_stateid_reaplist(&reaplist); > - if (unhashed) > - move_to_close_lru(s, clp->net); > + return unhashed; > } > } > > @@ -7035,6 +7035,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, > struct nfs4_ol_stateid *stp; > struct net *net = SVC_NET(rqstp); > struct nfsd_net *nn = net_generic(net, nfsd_net_id); > + bool need_move_to_close_list; > > dprintk("NFSD: nfsd4_close on file %pd\n", > cstate->current_fh.fh_dentry); > @@ -7057,8 +7058,10 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, > */ > nfs4_inc_and_copy_stateid(&close->cl_stateid, &stp->st_stid); > > - nfsd4_close_open_stateid(stp); > + need_move_to_close_list = nfsd4_close_open_stateid(stp); > mutex_unlock(&stp->st_mutex); > + if (need_move_to_close_list) > + move_to_close_lru(stp, net); > > /* v4.1+ suggests that we send a special stateid in here, since the > * clients should just ignore this anyway. Since this is not useful Reviewed-by: Jeff Layton <jlayton@kernel.org>
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 47e879d5d68a..05181b4a3ce8 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -6998,7 +6998,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, return status; } -static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s) +static bool nfsd4_close_open_stateid(struct nfs4_ol_stateid *s) { struct nfs4_client *clp = s->st_stid.sc_client; bool unhashed; @@ -7015,11 +7015,11 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s) list_for_each_entry(stp, &reaplist, st_locks) nfs4_free_cpntf_statelist(clp->net, &stp->st_stid); free_ol_stateid_reaplist(&reaplist); + return false; } else { spin_unlock(&clp->cl_lock); free_ol_stateid_reaplist(&reaplist); - if (unhashed) - move_to_close_lru(s, clp->net); + return unhashed; } } @@ -7035,6 +7035,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfs4_ol_stateid *stp; struct net *net = SVC_NET(rqstp); struct nfsd_net *nn = net_generic(net, nfsd_net_id); + bool need_move_to_close_list; dprintk("NFSD: nfsd4_close on file %pd\n", cstate->current_fh.fh_dentry); @@ -7057,8 +7058,10 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, */ nfs4_inc_and_copy_stateid(&close->cl_stateid, &stp->st_stid); - nfsd4_close_open_stateid(stp); + need_move_to_close_list = nfsd4_close_open_stateid(stp); mutex_unlock(&stp->st_mutex); + if (need_move_to_close_list) + move_to_close_lru(stp, net); /* v4.1+ suggests that we send a special stateid in here, since the * clients should just ignore this anyway. Since this is not useful