|
@@ -3512,7 +3512,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;
|
|
|
atomic_inc(&ret->st_stid.sc_count);
|
|
|
break;
|
|
@@ -3521,6 +3523,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)
|
|
@@ -3565,6 +3613,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);
|
|
|
|
|
@@ -3589,7 +3638,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;
|
|
@@ -4399,6 +4452,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
|
|
|
struct nfs4_ol_stateid *stp = NULL;
|
|
|
struct nfs4_delegation *dp = NULL;
|
|
|
__be32 status;
|
|
|
+ bool new_stp = false;
|
|
|
|
|
|
/*
|
|
|
* Lookup file; if found, lookup stateid and check open request,
|
|
@@ -4410,9 +4464,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;
|
|
@@ -4420,35 +4472,31 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
+ if (!stp) {
|
|
|
+ stp = init_open_stateid(fp, open);
|
|
|
+ if (!open->op_stp)
|
|
|
+ new_stp = true;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* OPEN the file, or upgrade an existing OPEN.
|
|
|
* If truncate fails, the OPEN fails.
|
|
|
+ *
|
|
|
+ * stp is already locked.
|
|
|
*/
|
|
|
- if (stp) {
|
|
|
+ if (!new_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);
|
|
|
goto out;
|
|
|
}
|
|
|
} else {
|
|
|
- /* stp is returned locked. */
|
|
|
- stp = init_open_stateid(fp, open);
|
|
|
- /* See if we lost the race to some other thread */
|
|
|
- if (stp->st_access_bmap != 0) {
|
|
|
- status = nfs4_upgrade_open(rqstp, fp, current_fh,
|
|
|
- stp, open);
|
|
|
- if (status) {
|
|
|
- mutex_unlock(&stp->st_mutex);
|
|
|
- goto out;
|
|
|
- }
|
|
|
- goto upgrade_out;
|
|
|
- }
|
|
|
status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open);
|
|
|
if (status) {
|
|
|
- mutex_unlock(&stp->st_mutex);
|
|
|
+ stp->st_stid.sc_type = NFS4_CLOSED_STID;
|
|
|
release_open_stateid(stp);
|
|
|
+ mutex_unlock(&stp->st_mutex);
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
@@ -4457,7 +4505,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
|
|
|
if (stp->st_clnt_odstate == open->op_odstate)
|
|
|
open->op_odstate = NULL;
|
|
|
}
|
|
|
-upgrade_out:
|
|
|
+
|
|
|
nfs4_inc_and_copy_stateid(&open->op_stateid, &stp->st_stid);
|
|
|
mutex_unlock(&stp->st_mutex);
|
|
|
|
|
@@ -4684,7 +4732,7 @@ nfs4_laundromat(struct nfsd_net *nn)
|
|
|
spin_unlock(&nn->blocked_locks_lock);
|
|
|
|
|
|
while (!list_empty(&reaplist)) {
|
|
|
- nbl = list_first_entry(&nn->blocked_locks_lru,
|
|
|
+ nbl = list_first_entry(&reaplist,
|
|
|
struct nfsd4_blocked_lock, nbl_lru);
|
|
|
list_del_init(&nbl->nbl_lru);
|
|
|
posix_unblock_lock(&nbl->nbl_lock);
|
|
@@ -5317,7 +5365,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);
|
|
|
|
|
@@ -5357,10 +5404,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);
|
|
@@ -7103,7 +7152,7 @@ nfs4_state_shutdown_net(struct net *net)
|
|
|
spin_unlock(&nn->blocked_locks_lock);
|
|
|
|
|
|
while (!list_empty(&reaplist)) {
|
|
|
- nbl = list_first_entry(&nn->blocked_locks_lru,
|
|
|
+ nbl = list_first_entry(&reaplist,
|
|
|
struct nfsd4_blocked_lock, nbl_lru);
|
|
|
list_del_init(&nbl->nbl_lru);
|
|
|
posix_unblock_lock(&nbl->nbl_lock);
|