Skip to content

Commit dd25793

Browse files
jtlaytonJ. Bruce Fields
authored andcommitted
nfsd: don't return an unhashed lock stateid after taking mutex
nfsd4_lock will take the st_mutex before working with the stateid it gets, but between the time when we drop the cl_lock and take the mutex, the stateid could become unhashed (a'la FREE_STATEID). If that happens the lock stateid returned to the client will be forgotten. Fix this by first moving the st_mutex acquisition into lookup_or_create_lock_state. Then, have it check to see if the lock stateid is still hashed after taking the mutex. If it's not, then put the stateid and try the find/create again. Signed-off-by: Jeff Layton <jlayton@redhat.com> Tested-by: Alexey Kodanev <alexey.kodanev@oracle.com> Cc: stable@vger.kernel.org # feb9dad nfsd: Always lock state exclusively. Cc: stable@vger.kernel.org Signed-off-by: J. Bruce Fields <bfields@redhat.com>
1 parent 4269139 commit dd25793

File tree

1 file changed

+20
-5
lines changed

1 file changed

+20
-5
lines changed

fs/nfsd/nfs4state.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5523,15 +5523,17 @@ static __be32
55235523
lookup_or_create_lock_state(struct nfsd4_compound_state *cstate,
55245524
struct nfs4_ol_stateid *ost,
55255525
struct nfsd4_lock *lock,
5526-
struct nfs4_ol_stateid **lst, bool *new)
5526+
struct nfs4_ol_stateid **plst, bool *new)
55275527
{
55285528
__be32 status;
55295529
struct nfs4_file *fi = ost->st_stid.sc_file;
55305530
struct nfs4_openowner *oo = openowner(ost->st_stateowner);
55315531
struct nfs4_client *cl = oo->oo_owner.so_client;
55325532
struct inode *inode = d_inode(cstate->current_fh.fh_dentry);
55335533
struct nfs4_lockowner *lo;
5534+
struct nfs4_ol_stateid *lst;
55345535
unsigned int strhashval;
5536+
bool hashed;
55355537

55365538
lo = find_lockowner_str(cl, &lock->lk_new_owner);
55375539
if (!lo) {
@@ -5547,12 +5549,27 @@ lookup_or_create_lock_state(struct nfsd4_compound_state *cstate,
55475549
goto out;
55485550
}
55495551

5550-
*lst = find_or_create_lock_stateid(lo, fi, inode, ost, new);
5551-
if (*lst == NULL) {
5552+
retry:
5553+
lst = find_or_create_lock_stateid(lo, fi, inode, ost, new);
5554+
if (lst == NULL) {
55525555
status = nfserr_jukebox;
55535556
goto out;
55545557
}
5558+
5559+
mutex_lock(&lst->st_mutex);
5560+
5561+
/* See if it's still hashed to avoid race with FREE_STATEID */
5562+
spin_lock(&cl->cl_lock);
5563+
hashed = !list_empty(&lst->st_perfile);
5564+
spin_unlock(&cl->cl_lock);
5565+
5566+
if (!hashed) {
5567+
mutex_unlock(&lst->st_mutex);
5568+
nfs4_put_stid(&lst->st_stid);
5569+
goto retry;
5570+
}
55555571
status = nfs_ok;
5572+
*plst = lst;
55565573
out:
55575574
nfs4_put_stateowner(&lo->lo_owner);
55585575
return status;
@@ -5619,8 +5636,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
56195636
goto out;
56205637
status = lookup_or_create_lock_state(cstate, open_stp, lock,
56215638
&lock_stp, &new);
5622-
if (status == nfs_ok)
5623-
mutex_lock(&lock_stp->st_mutex);
56245639
} else {
56255640
status = nfs4_preprocess_seqid_op(cstate,
56265641
lock->lk_old_lock_seqid,

0 commit comments

Comments
 (0)