|
@@ -3562,7 +3562,9 @@ nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
|
|
|
/* ignore lock owners */
|
|
|
if (local->st_stateowner->so_is_open_owner == 0)
|
|
|
continue;
|
|
|
- if (local->st_stateowner == &oo->oo_owner) {
|
|
|
+ if (local->st_stateowner != &oo->oo_owner)
|
|
|
+ continue;
|
|
|
+ if (local->st_stid.sc_type == NFS4_OPEN_STID) {
|
|
|
ret = local;
|
|
|
refcount_inc(&ret->st_stid.sc_count);
|
|
|
break;
|
|
@@ -3571,6 +3573,52 @@ nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static __be32
|
|
|
+nfsd4_verify_open_stid(struct nfs4_stid *s)
|
|
|
+{
|
|
|
+ __be32 ret = nfs_ok;
|
|
|
+
|
|
|
+ switch (s->sc_type) {
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ case NFS4_CLOSED_STID:
|
|
|
+ case NFS4_CLOSED_DELEG_STID:
|
|
|
+ ret = nfserr_bad_stateid;
|
|
|
+ break;
|
|
|
+ case NFS4_REVOKED_DELEG_STID:
|
|
|
+ ret = nfserr_deleg_revoked;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/* Lock the stateid st_mutex, and deal with races with CLOSE */
|
|
|
+static __be32
|
|
|
+nfsd4_lock_ol_stateid(struct nfs4_ol_stateid *stp)
|
|
|
+{
|
|
|
+ __be32 ret;
|
|
|
+
|
|
|
+ mutex_lock(&stp->st_mutex);
|
|
|
+ ret = nfsd4_verify_open_stid(&stp->st_stid);
|
|
|
+ if (ret != nfs_ok)
|
|
|
+ mutex_unlock(&stp->st_mutex);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static struct nfs4_ol_stateid *
|
|
|
+nfsd4_find_and_lock_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
|
|
|
+{
|
|
|
+ struct nfs4_ol_stateid *stp;
|
|
|
+ for (;;) {
|
|
|
+ spin_lock(&fp->fi_lock);
|
|
|
+ stp = nfsd4_find_existing_open(fp, open);
|
|
|
+ spin_unlock(&fp->fi_lock);
|
|
|
+ if (!stp || nfsd4_lock_ol_stateid(stp) == nfs_ok)
|
|
|
+ break;
|
|
|
+ nfs4_put_stid(&stp->st_stid);
|
|
|
+ }
|
|
|
+ return stp;
|
|
|
+}
|
|
|
+
|
|
|
static struct nfs4_openowner *
|
|
|
alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
|
|
|
struct nfsd4_compound_state *cstate)
|
|
@@ -3615,6 +3663,7 @@ init_open_stateid(struct nfs4_file *fp, struct nfsd4_open *open)
|
|
|
mutex_init(&stp->st_mutex);
|
|
|
mutex_lock(&stp->st_mutex);
|
|
|
|
|
|
+retry:
|
|
|
spin_lock(&oo->oo_owner.so_client->cl_lock);
|
|
|
spin_lock(&fp->fi_lock);
|
|
|
|
|
@@ -3639,7 +3688,11 @@ out_unlock:
|
|
|
spin_unlock(&fp->fi_lock);
|
|
|
spin_unlock(&oo->oo_owner.so_client->cl_lock);
|
|
|
if (retstp) {
|
|
|
- mutex_lock(&retstp->st_mutex);
|
|
|
+ /* Handle races with CLOSE */
|
|
|
+ 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;
|
|
@@ -4460,9 +4513,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
|
|
|
status = nfs4_check_deleg(cl, open, &dp);
|
|
|
if (status)
|
|
|
goto out;
|
|
|
- spin_lock(&fp->fi_lock);
|
|
|
- stp = nfsd4_find_existing_open(fp, open);
|
|
|
- spin_unlock(&fp->fi_lock);
|
|
|
+ stp = nfsd4_find_and_lock_existing_open(fp, open);
|
|
|
} else {
|
|
|
open->op_file = NULL;
|
|
|
status = nfserr_bad_stateid;
|
|
@@ -4476,7 +4527,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
|
|
|
*/
|
|
|
if (stp) {
|
|
|
/* Stateid was found, this is an OPEN upgrade */
|
|
|
- mutex_lock(&stp->st_mutex);
|
|
|
status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open);
|
|
|
if (status) {
|
|
|
mutex_unlock(&stp->st_mutex);
|
|
@@ -5367,7 +5417,6 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
|
|
|
bool unhashed;
|
|
|
LIST_HEAD(reaplist);
|
|
|
|
|
|
- s->st_stid.sc_type = NFS4_CLOSED_STID;
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
unhashed = unhash_open_stateid(s, &reaplist);
|
|
|
|
|
@@ -5407,10 +5456,12 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
nfsd4_bump_seqid(cstate, status);
|
|
|
if (status)
|
|
|
goto out;
|
|
|
+
|
|
|
+ stp->st_stid.sc_type = NFS4_CLOSED_STID;
|
|
|
nfs4_inc_and_copy_stateid(&close->cl_stateid, &stp->st_stid);
|
|
|
- mutex_unlock(&stp->st_mutex);
|
|
|
|
|
|
nfsd4_close_open_stateid(stp);
|
|
|
+ mutex_unlock(&stp->st_mutex);
|
|
|
|
|
|
/* put reference from nfs4_preprocess_seqid_op */
|
|
|
nfs4_put_stid(&stp->st_stid);
|