Currently, both destroy_revoked_delegation and revoke_delegation manipulate the cl_revoked list without any locking aside from the client_mutex. Ensure that the clp->cl_lock is held when manipulating it, except for the list walking in destroy_client. At that point, the client should no longer be in use, and so it should be safe to walk the list without any locking. That also means that we don't need to do the list_splice_init there either. Also, the fact that destroy_revoked_delegation and revoke_delegation delete dl_recall_lru without any locking makes it difficult to know whether they're doing so safely in all cases. Move the list_del_init calls into the callers, and add WARN_ONs in the event that these calls are passed a delegation that has a non-empty list_head. Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxxxxxxx> --- fs/nfsd/nfs4state.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 4e42c3823a8a..ca8b4d0e712b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -669,7 +669,7 @@ unhash_delegation_locked(struct nfs4_delegation *dp) static void destroy_revoked_delegation(struct nfs4_delegation *dp) { - list_del_init(&dp->dl_recall_lru); + WARN_ON(!list_empty(&dp->dl_recall_lru)); nfs4_put_delegation(dp); } @@ -685,11 +685,15 @@ static void revoke_delegation(struct nfs4_delegation *dp) { struct nfs4_client *clp = dp->dl_stid.sc_client; + WARN_ON(!list_empty(&dp->dl_recall_lru)); + if (clp->cl_minorversion == 0) destroy_revoked_delegation(dp); else { dp->dl_stid.sc_type = NFS4_REVOKED_DELEG_STID; - list_move(&dp->dl_recall_lru, &clp->cl_revoked); + spin_lock(&clp->cl_lock); + list_add(&dp->dl_recall_lru, &clp->cl_revoked); + spin_unlock(&clp->cl_lock); } } @@ -1458,9 +1462,9 @@ destroy_client(struct nfs4_client *clp) list_del_init(&dp->dl_recall_lru); nfs4_put_delegation(dp); } - list_splice_init(&clp->cl_revoked, &reaplist); - while (!list_empty(&reaplist)) { + while (!list_empty(&clp->cl_revoked)) { dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); + list_del_init(&dp->dl_recall_lru); destroy_revoked_delegation(dp); } while (!list_empty(&clp->cl_openowners)) { @@ -3883,8 +3887,10 @@ nfs4_laundromat(struct nfsd_net *nn) list_add(&dp->dl_recall_lru, &reaplist); } spin_unlock(&state_lock); - list_for_each_safe(pos, next, &reaplist) { - dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); + while (!list_empty(&reaplist)) { + dp = list_first_entry(&reaplist, struct nfs4_delegation, + dl_recall_lru); + list_del_init(&dp->dl_recall_lru); revoke_delegation(dp); } list_for_each_safe(pos, next, &nn->close_lru) { @@ -4228,6 +4234,9 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, break; case NFS4_REVOKED_DELEG_STID: dp = delegstateid(s); + spin_lock(&cl->cl_lock); + list_del_init(&dp->dl_recall_lru); + spin_unlock(&cl->cl_lock); destroy_revoked_delegation(dp); ret = nfs_ok; break; -- 1.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html