|
@@ -63,12 +63,16 @@ static const stateid_t zero_stateid = {
|
|
|
static const stateid_t currentstateid = {
|
|
|
.si_generation = 1,
|
|
|
};
|
|
|
+static const stateid_t close_stateid = {
|
|
|
+ .si_generation = 0xffffffffU,
|
|
|
+};
|
|
|
|
|
|
static u64 current_sessionid = 1;
|
|
|
|
|
|
#define ZERO_STATEID(stateid) (!memcmp((stateid), &zero_stateid, sizeof(stateid_t)))
|
|
|
#define ONE_STATEID(stateid) (!memcmp((stateid), &one_stateid, sizeof(stateid_t)))
|
|
|
#define CURRENT_STATEID(stateid) (!memcmp((stateid), ¤tstateid, sizeof(stateid_t)))
|
|
|
+#define CLOSE_STATEID(stateid) (!memcmp((stateid), &close_stateid, sizeof(stateid_t)))
|
|
|
|
|
|
/* forward declarations */
|
|
|
static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner);
|
|
@@ -83,6 +87,11 @@ static void nfs4_free_ol_stateid(struct nfs4_stid *stid);
|
|
|
*/
|
|
|
static DEFINE_SPINLOCK(state_lock);
|
|
|
|
|
|
+enum nfsd4_st_mutex_lock_subclass {
|
|
|
+ OPEN_STATEID_MUTEX = 0,
|
|
|
+ LOCK_STATEID_MUTEX = 1,
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* A waitqueue for all in-progress 4.0 CLOSE operations that are waiting for
|
|
|
* the refcount on the open stateid to drop.
|
|
@@ -3562,7 +3571,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 +3582,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_nested(&stp->st_mutex, LOCK_STATEID_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)
|
|
@@ -3613,8 +3670,9 @@ init_open_stateid(struct nfs4_file *fp, struct nfsd4_open *open)
|
|
|
stp = open->op_stp;
|
|
|
/* We are moving these outside of the spinlocks to avoid the warnings */
|
|
|
mutex_init(&stp->st_mutex);
|
|
|
- mutex_lock(&stp->st_mutex);
|
|
|
+ mutex_lock_nested(&stp->st_mutex, OPEN_STATEID_MUTEX);
|
|
|
|
|
|
+retry:
|
|
|
spin_lock(&oo->oo_owner.so_client->cl_lock);
|
|
|
spin_lock(&fp->fi_lock);
|
|
|
|
|
@@ -3639,7 +3697,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;
|
|
@@ -4449,6 +4511,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,
|
|
@@ -4460,9 +4523,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;
|
|
@@ -4470,35 +4531,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;
|
|
|
}
|
|
|
|
|
@@ -4507,7 +4564,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);
|
|
|
|
|
@@ -4734,7 +4791,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);
|
|
@@ -4855,6 +4912,18 @@ static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_s
|
|
|
return nfserr_old_stateid;
|
|
|
}
|
|
|
|
|
|
+static __be32 nfsd4_stid_check_stateid_generation(stateid_t *in, struct nfs4_stid *s, bool has_session)
|
|
|
+{
|
|
|
+ __be32 ret;
|
|
|
+
|
|
|
+ spin_lock(&s->sc_lock);
|
|
|
+ ret = nfsd4_verify_open_stid(s);
|
|
|
+ if (ret == nfs_ok)
|
|
|
+ ret = check_stateid_generation(in, &s->sc_stateid, has_session);
|
|
|
+ spin_unlock(&s->sc_lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static __be32 nfsd4_check_openowner_confirmed(struct nfs4_ol_stateid *ols)
|
|
|
{
|
|
|
if (ols->st_stateowner->so_is_open_owner &&
|
|
@@ -4868,7 +4937,8 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
|
|
|
struct nfs4_stid *s;
|
|
|
__be32 status = nfserr_bad_stateid;
|
|
|
|
|
|
- if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
|
|
|
+ if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) ||
|
|
|
+ CLOSE_STATEID(stateid))
|
|
|
return status;
|
|
|
/* Client debugging aid. */
|
|
|
if (!same_clid(&stateid->si_opaque.so_clid, &cl->cl_clientid)) {
|
|
@@ -4883,7 +4953,7 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
|
|
|
s = find_stateid_locked(cl, stateid);
|
|
|
if (!s)
|
|
|
goto out_unlock;
|
|
|
- status = check_stateid_generation(stateid, &s->sc_stateid, 1);
|
|
|
+ status = nfsd4_stid_check_stateid_generation(stateid, s, 1);
|
|
|
if (status)
|
|
|
goto out_unlock;
|
|
|
switch (s->sc_type) {
|
|
@@ -4926,7 +4996,8 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
|
|
|
else if (typemask & NFS4_DELEG_STID)
|
|
|
typemask |= NFS4_REVOKED_DELEG_STID;
|
|
|
|
|
|
- if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
|
|
|
+ if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) ||
|
|
|
+ CLOSE_STATEID(stateid))
|
|
|
return nfserr_bad_stateid;
|
|
|
status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn);
|
|
|
if (status == nfserr_stale_clientid) {
|
|
@@ -5044,7 +5115,7 @@ nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
|
|
|
&s, nn);
|
|
|
if (status)
|
|
|
return status;
|
|
|
- status = check_stateid_generation(stateid, &s->sc_stateid,
|
|
|
+ status = nfsd4_stid_check_stateid_generation(stateid, s,
|
|
|
nfsd4_has_session(cstate));
|
|
|
if (status)
|
|
|
goto out;
|
|
@@ -5098,7 +5169,9 @@ nfsd4_free_lock_stateid(stateid_t *stateid, struct nfs4_stid *s)
|
|
|
struct nfs4_ol_stateid *stp = openlockstateid(s);
|
|
|
__be32 ret;
|
|
|
|
|
|
- mutex_lock(&stp->st_mutex);
|
|
|
+ ret = nfsd4_lock_ol_stateid(stp);
|
|
|
+ if (ret)
|
|
|
+ goto out_put_stid;
|
|
|
|
|
|
ret = check_stateid_generation(stateid, &s->sc_stateid, 1);
|
|
|
if (ret)
|
|
@@ -5109,11 +5182,13 @@ nfsd4_free_lock_stateid(stateid_t *stateid, struct nfs4_stid *s)
|
|
|
lockowner(stp->st_stateowner)))
|
|
|
goto out;
|
|
|
|
|
|
+ stp->st_stid.sc_type = NFS4_CLOSED_STID;
|
|
|
release_lock_stateid(stp);
|
|
|
ret = nfs_ok;
|
|
|
|
|
|
out:
|
|
|
mutex_unlock(&stp->st_mutex);
|
|
|
+out_put_stid:
|
|
|
nfs4_put_stid(s);
|
|
|
return ret;
|
|
|
}
|
|
@@ -5133,6 +5208,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
s = find_stateid_locked(cl, stateid);
|
|
|
if (!s)
|
|
|
goto out_unlock;
|
|
|
+ spin_lock(&s->sc_lock);
|
|
|
switch (s->sc_type) {
|
|
|
case NFS4_DELEG_STID:
|
|
|
ret = nfserr_locks_held;
|
|
@@ -5144,11 +5220,13 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
ret = nfserr_locks_held;
|
|
|
break;
|
|
|
case NFS4_LOCK_STID:
|
|
|
+ spin_unlock(&s->sc_lock);
|
|
|
refcount_inc(&s->sc_count);
|
|
|
spin_unlock(&cl->cl_lock);
|
|
|
ret = nfsd4_free_lock_stateid(stateid, s);
|
|
|
goto out;
|
|
|
case NFS4_REVOKED_DELEG_STID:
|
|
|
+ spin_unlock(&s->sc_lock);
|
|
|
dp = delegstateid(s);
|
|
|
list_del_init(&dp->dl_recall_lru);
|
|
|
spin_unlock(&cl->cl_lock);
|
|
@@ -5157,6 +5235,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
goto out;
|
|
|
/* Default falls through and returns nfserr_bad_stateid */
|
|
|
}
|
|
|
+ spin_unlock(&s->sc_lock);
|
|
|
out_unlock:
|
|
|
spin_unlock(&cl->cl_lock);
|
|
|
out:
|
|
@@ -5179,15 +5258,9 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_
|
|
|
status = nfsd4_check_seqid(cstate, sop, seqid);
|
|
|
if (status)
|
|
|
return status;
|
|
|
- if (stp->st_stid.sc_type == NFS4_CLOSED_STID
|
|
|
- || stp->st_stid.sc_type == NFS4_REVOKED_DELEG_STID)
|
|
|
- /*
|
|
|
- * "Closed" stateid's exist *only* to return
|
|
|
- * nfserr_replay_me from the previous step, and
|
|
|
- * revoked delegations are kept only for free_stateid.
|
|
|
- */
|
|
|
- return nfserr_bad_stateid;
|
|
|
- mutex_lock(&stp->st_mutex);
|
|
|
+ status = nfsd4_lock_ol_stateid(stp);
|
|
|
+ if (status != nfs_ok)
|
|
|
+ return status;
|
|
|
status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate));
|
|
|
if (status == nfs_ok)
|
|
|
status = nfs4_check_fh(current_fh, &stp->st_stid);
|
|
@@ -5367,7 +5440,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 +5479,17 @@ 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);
|
|
|
+
|
|
|
+ /* See RFC5661 sectionm 18.2.4 */
|
|
|
+ if (stp->st_stid.sc_client->cl_minorversion)
|
|
|
+ memcpy(&close->cl_stateid, &close_stateid,
|
|
|
+ sizeof(close->cl_stateid));
|
|
|
|
|
|
/* put reference from nfs4_preprocess_seqid_op */
|
|
|
nfs4_put_stid(&stp->st_stid);
|
|
@@ -5436,7 +5515,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
if (status)
|
|
|
goto out;
|
|
|
dp = delegstateid(s);
|
|
|
- status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate));
|
|
|
+ status = nfsd4_stid_check_stateid_generation(stateid, &dp->dl_stid, nfsd4_has_session(cstate));
|
|
|
if (status)
|
|
|
goto put_stateid;
|
|
|
|
|
@@ -5642,14 +5721,41 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
+static struct nfs4_ol_stateid *
|
|
|
+find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp)
|
|
|
+{
|
|
|
+ struct nfs4_ol_stateid *lst;
|
|
|
+ struct nfs4_client *clp = lo->lo_owner.so_client;
|
|
|
+
|
|
|
+ lockdep_assert_held(&clp->cl_lock);
|
|
|
+
|
|
|
+ list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
|
|
|
+ if (lst->st_stid.sc_type != NFS4_LOCK_STID)
|
|
|
+ continue;
|
|
|
+ if (lst->st_stid.sc_file == fp) {
|
|
|
+ refcount_inc(&lst->st_stid.sc_count);
|
|
|
+ return lst;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static struct nfs4_ol_stateid *
|
|
|
init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
|
|
|
struct nfs4_file *fp, struct inode *inode,
|
|
|
struct nfs4_ol_stateid *open_stp)
|
|
|
{
|
|
|
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_nested(&stp->st_mutex, OPEN_STATEID_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);
|
|
|
stp->st_stid.sc_type = NFS4_LOCK_STID;
|
|
@@ -5659,29 +5765,22 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
|
|
|
stp->st_access_bmap = 0;
|
|
|
stp->st_deny_bmap = open_stp->st_deny_bmap;
|
|
|
stp->st_openstp = open_stp;
|
|
|
- mutex_init(&stp->st_mutex);
|
|
|
list_add(&stp->st_locks, &open_stp->st_locks);
|
|
|
list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids);
|
|
|
- spin_lock(&fp->fi_lock);
|
|
|
list_add(&stp->st_perfile, &fp->fi_stateids);
|
|
|
+out_unlock:
|
|
|
spin_unlock(&fp->fi_lock);
|
|
|
-}
|
|
|
-
|
|
|
-static struct nfs4_ol_stateid *
|
|
|
-find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp)
|
|
|
-{
|
|
|
- struct nfs4_ol_stateid *lst;
|
|
|
- struct nfs4_client *clp = lo->lo_owner.so_client;
|
|
|
-
|
|
|
- lockdep_assert_held(&clp->cl_lock);
|
|
|
-
|
|
|
- list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
|
|
|
- if (lst->st_stid.sc_file == fp) {
|
|
|
- refcount_inc(&lst->st_stid.sc_count);
|
|
|
- return lst;
|
|
|
+ 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 NULL;
|
|
|
+ return stp;
|
|
|
}
|
|
|
|
|
|
static struct nfs4_ol_stateid *
|
|
@@ -5694,26 +5793,25 @@ find_or_create_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fi,
|
|
|
struct nfs4_openowner *oo = openowner(ost->st_stateowner);
|
|
|
struct nfs4_client *clp = oo->oo_owner.so_client;
|
|
|
|
|
|
+ *new = false;
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
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);
|
|
|
- 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);
|
|
|
+out:
|
|
|
return lst;
|
|
|
}
|
|
|
|
|
@@ -5750,7 +5848,6 @@ lookup_or_create_lock_state(struct nfsd4_compound_state *cstate,
|
|
|
struct nfs4_lockowner *lo;
|
|
|
struct nfs4_ol_stateid *lst;
|
|
|
unsigned int strhashval;
|
|
|
- bool hashed;
|
|
|
|
|
|
lo = find_lockowner_str(cl, &lock->lk_new_owner);
|
|
|
if (!lo) {
|
|
@@ -5766,25 +5863,12 @@ lookup_or_create_lock_state(struct nfsd4_compound_state *cstate,
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
-retry:
|
|
|
lst = find_or_create_lock_stateid(lo, fi, inode, ost, new);
|
|
|
if (lst == NULL) {
|
|
|
status = nfserr_jukebox;
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- mutex_lock(&lst->st_mutex);
|
|
|
-
|
|
|
- /* See if it's still hashed to avoid race with FREE_STATEID */
|
|
|
- spin_lock(&cl->cl_lock);
|
|
|
- hashed = !list_empty(&lst->st_perfile);
|
|
|
- spin_unlock(&cl->cl_lock);
|
|
|
-
|
|
|
- if (!hashed) {
|
|
|
- mutex_unlock(&lst->st_mutex);
|
|
|
- nfs4_put_stid(&lst->st_stid);
|
|
|
- goto retry;
|
|
|
- }
|
|
|
status = nfs_ok;
|
|
|
*plst = lst;
|
|
|
out:
|
|
@@ -5990,14 +6074,16 @@ out:
|
|
|
seqid_mutating_err(ntohl(status)))
|
|
|
lock_sop->lo_owner.so_seqid++;
|
|
|
|
|
|
- mutex_unlock(&lock_stp->st_mutex);
|
|
|
-
|
|
|
/*
|
|
|
* If this is a new, never-before-used stateid, and we are
|
|
|
* 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&lock_stp->st_mutex);
|
|
|
|
|
|
nfs4_put_stid(&lock_stp->st_stid);
|
|
|
}
|
|
@@ -7017,6 +7103,10 @@ static int nfs4_state_create_net(struct net *net)
|
|
|
INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]);
|
|
|
nn->conf_name_tree = RB_ROOT;
|
|
|
nn->unconf_name_tree = RB_ROOT;
|
|
|
+ nn->boot_time = get_seconds();
|
|
|
+ nn->grace_ended = false;
|
|
|
+ nn->nfsd4_manager.block_opens = true;
|
|
|
+ INIT_LIST_HEAD(&nn->nfsd4_manager.list);
|
|
|
INIT_LIST_HEAD(&nn->client_lru);
|
|
|
INIT_LIST_HEAD(&nn->close_lru);
|
|
|
INIT_LIST_HEAD(&nn->del_recall_lru);
|
|
@@ -7074,9 +7164,6 @@ nfs4_state_start_net(struct net *net)
|
|
|
ret = nfs4_state_create_net(net);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
- nn->boot_time = get_seconds();
|
|
|
- nn->grace_ended = false;
|
|
|
- nn->nfsd4_manager.block_opens = true;
|
|
|
locks_start_grace(net, &nn->nfsd4_manager);
|
|
|
nfsd4_client_tracking_init(net);
|
|
|
printk(KERN_INFO "NFSD: starting %ld-second grace period (net %x)\n",
|
|
@@ -7153,7 +7240,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);
|