aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2017-11-03 08:00:13 -0400
committerJ. Bruce Fields <bfields@redhat.com>2017-11-27 16:45:10 -0500
commit659aefb68eca28ba9aa482a9fc64de107332e256 (patch)
treea8183d2a3c27c9f74a21051015362b91a54e2b28
parentfb500a7cfee7f2f447d2bbf30cb59629feab6ac1 (diff)
nfsd: Ensure we don't recognise lock stateids after freeing them
In order to deal with lookup races, nfsd4_free_lock_stateid() needs to be able to signal to other stateful functions that the lock stateid is no longer valid. Right now, nfsd_lock() will check whether or not an existing stateid is still hashed, but only in the "new lock" path. To ensure the stateid invalidation is also recognised by the "existing lock" path, and also by a second call to nfsd4_free_lock_stateid() itself, we can change the type to NFS4_CLOSED_STID under the stp->st_mutex. Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs4state.c19
1 files changed, 8 insertions, 11 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index afc04b9784a0..6542b57ecc86 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5149,7 +5149,9 @@ nfsd4_free_lock_stateid(stateid_t *stateid, struct nfs4_stid *s)
5149 struct nfs4_ol_stateid *stp = openlockstateid(s); 5149 struct nfs4_ol_stateid *stp = openlockstateid(s);
5150 __be32 ret; 5150 __be32 ret;
5151 5151
5152 mutex_lock(&stp->st_mutex); 5152 ret = nfsd4_lock_ol_stateid(stp);
5153 if (ret)
5154 goto out_put_stid;
5153 5155
5154 ret = check_stateid_generation(stateid, &s->sc_stateid, 1); 5156 ret = check_stateid_generation(stateid, &s->sc_stateid, 1);
5155 if (ret) 5157 if (ret)
@@ -5160,11 +5162,13 @@ nfsd4_free_lock_stateid(stateid_t *stateid, struct nfs4_stid *s)
5160 lockowner(stp->st_stateowner))) 5162 lockowner(stp->st_stateowner)))
5161 goto out; 5163 goto out;
5162 5164
5165 stp->st_stid.sc_type = NFS4_CLOSED_STID;
5163 release_lock_stateid(stp); 5166 release_lock_stateid(stp);
5164 ret = nfs_ok; 5167 ret = nfs_ok;
5165 5168
5166out: 5169out:
5167 mutex_unlock(&stp->st_mutex); 5170 mutex_unlock(&stp->st_mutex);
5171out_put_stid:
5168 nfs4_put_stid(s); 5172 nfs4_put_stid(s);
5169 return ret; 5173 return ret;
5170} 5174}
@@ -5733,6 +5737,8 @@ find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp)
5733 lockdep_assert_held(&clp->cl_lock); 5737 lockdep_assert_held(&clp->cl_lock);
5734 5738
5735 list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) { 5739 list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
5740 if (lst->st_stid.sc_type != NFS4_LOCK_STID)
5741 continue;
5736 if (lst->st_stid.sc_file == fp) { 5742 if (lst->st_stid.sc_file == fp) {
5737 refcount_inc(&lst->st_stid.sc_count); 5743 refcount_inc(&lst->st_stid.sc_count);
5738 return lst; 5744 return lst;
@@ -5807,7 +5813,6 @@ lookup_or_create_lock_state(struct nfsd4_compound_state *cstate,
5807 struct nfs4_lockowner *lo; 5813 struct nfs4_lockowner *lo;
5808 struct nfs4_ol_stateid *lst; 5814 struct nfs4_ol_stateid *lst;
5809 unsigned int strhashval; 5815 unsigned int strhashval;
5810 bool hashed;
5811 5816
5812 lo = find_lockowner_str(cl, &lock->lk_new_owner); 5817 lo = find_lockowner_str(cl, &lock->lk_new_owner);
5813 if (!lo) { 5818 if (!lo) {
@@ -5830,15 +5835,7 @@ retry:
5830 goto out; 5835 goto out;
5831 } 5836 }
5832 5837
5833 mutex_lock(&lst->st_mutex); 5838 if (nfsd4_lock_ol_stateid(lst) != nfs_ok) {
5834
5835 /* See if it's still hashed to avoid race with FREE_STATEID */
5836 spin_lock(&cl->cl_lock);
5837 hashed = !list_empty(&lst->st_perfile);
5838 spin_unlock(&cl->cl_lock);
5839
5840 if (!hashed) {
5841 mutex_unlock(&lst->st_mutex);
5842 nfs4_put_stid(&lst->st_stid); 5839 nfs4_put_stid(&lst->st_stid);
5843 goto retry; 5840 goto retry;
5844 } 5841 }