Commit beeca19c authored by Trond Myklebust's avatar Trond Myklebust Committed by J. Bruce Fields

nfsd: Fix race in lock stateid creation

If we're looking up a new lock state, and the creation fails, then
we want to unhash it, just like we do for OPEN. However in order
to do so, we need to that no other LOCK requests can grab the
mutex until we have unhashed it (and marked it as closed).
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent fd1fd685
...@@ -5722,14 +5722,22 @@ find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp) ...@@ -5722,14 +5722,22 @@ find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp)
return NULL; return NULL;
} }
static void static struct nfs4_ol_stateid *
init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo, init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
struct nfs4_file *fp, struct inode *inode, struct nfs4_file *fp, struct inode *inode,
struct nfs4_ol_stateid *open_stp) struct nfs4_ol_stateid *open_stp)
{ {
struct nfs4_client *clp = lo->lo_owner.so_client; struct nfs4_client *clp = lo->lo_owner.so_client;
struct nfs4_ol_stateid *retstp;
lockdep_assert_held(&clp->cl_lock); mutex_init(&stp->st_mutex);
mutex_lock(&stp->st_mutex);
retry:
spin_lock(&clp->cl_lock);
spin_lock(&fp->fi_lock);
retstp = find_lock_stateid(lo, fp);
if (retstp)
goto out_unlock;
refcount_inc(&stp->st_stid.sc_count); refcount_inc(&stp->st_stid.sc_count);
stp->st_stid.sc_type = NFS4_LOCK_STID; stp->st_stid.sc_type = NFS4_LOCK_STID;
...@@ -5739,12 +5747,22 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo, ...@@ -5739,12 +5747,22 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
stp->st_access_bmap = 0; stp->st_access_bmap = 0;
stp->st_deny_bmap = open_stp->st_deny_bmap; stp->st_deny_bmap = open_stp->st_deny_bmap;
stp->st_openstp = open_stp; stp->st_openstp = open_stp;
mutex_init(&stp->st_mutex);
list_add(&stp->st_locks, &open_stp->st_locks); list_add(&stp->st_locks, &open_stp->st_locks);
list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids);
spin_lock(&fp->fi_lock);
list_add(&stp->st_perfile, &fp->fi_stateids); list_add(&stp->st_perfile, &fp->fi_stateids);
out_unlock:
spin_unlock(&fp->fi_lock); spin_unlock(&fp->fi_lock);
spin_unlock(&clp->cl_lock);
if (retstp) {
if (nfsd4_lock_ol_stateid(retstp) != nfs_ok) {
nfs4_put_stid(&retstp->st_stid);
goto retry;
}
/* To keep mutex tracking happy */
mutex_unlock(&stp->st_mutex);
stp = retstp;
}
return stp;
} }
static struct nfs4_ol_stateid * static struct nfs4_ol_stateid *
...@@ -5757,26 +5775,25 @@ find_or_create_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fi, ...@@ -5757,26 +5775,25 @@ find_or_create_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fi,
struct nfs4_openowner *oo = openowner(ost->st_stateowner); struct nfs4_openowner *oo = openowner(ost->st_stateowner);
struct nfs4_client *clp = oo->oo_owner.so_client; struct nfs4_client *clp = oo->oo_owner.so_client;
*new = false;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
lst = find_lock_stateid(lo, fi); lst = find_lock_stateid(lo, fi);
if (lst == NULL) {
spin_unlock(&clp->cl_lock);
ns = nfs4_alloc_stid(clp, stateid_slab, nfs4_free_lock_stateid);
if (ns == NULL)
return NULL;
spin_lock(&clp->cl_lock);
lst = find_lock_stateid(lo, fi);
if (likely(!lst)) {
lst = openlockstateid(ns);
init_lock_stateid(lst, lo, fi, inode, ost);
ns = NULL;
*new = true;
}
}
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
if (ns) if (lst != NULL) {
if (nfsd4_lock_ol_stateid(lst) == nfs_ok)
goto out;
nfs4_put_stid(&lst->st_stid);
}
ns = nfs4_alloc_stid(clp, stateid_slab, nfs4_free_lock_stateid);
if (ns == NULL)
return NULL;
lst = init_lock_stateid(openlockstateid(ns), lo, fi, inode, ost);
if (lst == openlockstateid(ns))
*new = true;
else
nfs4_put_stid(ns); nfs4_put_stid(ns);
out:
return lst; return lst;
} }
...@@ -5828,17 +5845,12 @@ lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, ...@@ -5828,17 +5845,12 @@ lookup_or_create_lock_state(struct nfsd4_compound_state *cstate,
goto out; goto out;
} }
retry:
lst = find_or_create_lock_stateid(lo, fi, inode, ost, new); lst = find_or_create_lock_stateid(lo, fi, inode, ost, new);
if (lst == NULL) { if (lst == NULL) {
status = nfserr_jukebox; status = nfserr_jukebox;
goto out; goto out;
} }
if (nfsd4_lock_ol_stateid(lst) != nfs_ok) {
nfs4_put_stid(&lst->st_stid);
goto retry;
}
status = nfs_ok; status = nfs_ok;
*plst = lst; *plst = lst;
out: out:
...@@ -6044,14 +6056,16 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -6044,14 +6056,16 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
seqid_mutating_err(ntohl(status))) seqid_mutating_err(ntohl(status)))
lock_sop->lo_owner.so_seqid++; lock_sop->lo_owner.so_seqid++;
mutex_unlock(&lock_stp->st_mutex);
/* /*
* If this is a new, never-before-used stateid, and we are * If this is a new, never-before-used stateid, and we are
* returning an error, then just go ahead and release it. * returning an error, then just go ahead and release it.
*/ */
if (status && new) if (status && new) {
lock_stp->st_stid.sc_type = NFS4_CLOSED_STID;
release_lock_stateid(lock_stp); release_lock_stateid(lock_stp);
}
mutex_unlock(&lock_stp->st_mutex);
nfs4_put_stid(&lock_stp->st_stid); nfs4_put_stid(&lock_stp->st_stid);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment