aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorJeff Layton <jlayton@poochiereds.net>2015-09-17 07:47:08 -0400
committerJ. Bruce Fields <bfields@redhat.com>2015-10-12 17:31:03 -0400
commit35a92fe8770ce54c5eb275cd76128645bea2d200 (patch)
tree3c844e8991d12036688ece6073cd6f2d4cd17b13 /fs/nfsd
parent3be7f32878e742cf3c17b435c90e198862457706 (diff)
nfsd: serialize state seqid morphing operations
Andrew was seeing a race occur when an OPEN and OPEN_DOWNGRADE were running in parallel. The server would receive the OPEN_DOWNGRADE first and check its seqid, but then an OPEN would race in and bump it. The OPEN_DOWNGRADE would then complete and bump the seqid again. The result was that the OPEN_DOWNGRADE would be applied after the OPEN, even though it should have been rejected since the seqid changed. The only recourse we have here I think is to serialize operations that bump the seqid in a stateid, particularly when we're given a seqid in the call. To address this, we add a new rw_semaphore to the nfs4_ol_stateid struct. We do a down_write prior to checking the seqid after looking up the stateid to ensure that nothing else is going to bump it while we're operating on it. In the case of OPEN, we do a down_read, as the call doesn't contain a seqid. Those can run in parallel -- we just need to serialize them when there is a concurrent OPEN_DOWNGRADE or CLOSE. LOCK and LOCKU however always take the write lock as there is no opportunity for parallelizing those. Reported-and-Tested-by: Andrew W Elble <aweits@rit.edu> Signed-off-by: Jeff Layton <jeff.layton@primarydata.com> Cc: stable@vger.kernel.org Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4state.c33
-rw-r--r--fs/nfsd/state.h19
2 files changed, 38 insertions, 14 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 0f1d5691b795..1b39edf10b67 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3360,6 +3360,7 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
3360 stp->st_access_bmap = 0; 3360 stp->st_access_bmap = 0;
3361 stp->st_deny_bmap = 0; 3361 stp->st_deny_bmap = 0;
3362 stp->st_openstp = NULL; 3362 stp->st_openstp = NULL;
3363 init_rwsem(&stp->st_rwsem);
3363 spin_lock(&oo->oo_owner.so_client->cl_lock); 3364 spin_lock(&oo->oo_owner.so_client->cl_lock);
3364 list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); 3365 list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids);
3365 spin_lock(&fp->fi_lock); 3366 spin_lock(&fp->fi_lock);
@@ -4187,15 +4188,20 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4187 */ 4188 */
4188 if (stp) { 4189 if (stp) {
4189 /* Stateid was found, this is an OPEN upgrade */ 4190 /* Stateid was found, this is an OPEN upgrade */
4191 down_read(&stp->st_rwsem);
4190 status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open); 4192 status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open);
4191 if (status) 4193 if (status) {
4194 up_read(&stp->st_rwsem);
4192 goto out; 4195 goto out;
4196 }
4193 } else { 4197 } else {
4194 stp = open->op_stp; 4198 stp = open->op_stp;
4195 open->op_stp = NULL; 4199 open->op_stp = NULL;
4196 init_open_stateid(stp, fp, open); 4200 init_open_stateid(stp, fp, open);
4201 down_read(&stp->st_rwsem);
4197 status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open); 4202 status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open);
4198 if (status) { 4203 if (status) {
4204 up_read(&stp->st_rwsem);
4199 release_open_stateid(stp); 4205 release_open_stateid(stp);
4200 goto out; 4206 goto out;
4201 } 4207 }
@@ -4207,6 +4213,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4207 } 4213 }
4208 update_stateid(&stp->st_stid.sc_stateid); 4214 update_stateid(&stp->st_stid.sc_stateid);
4209 memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); 4215 memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
4216 up_read(&stp->st_rwsem);
4210 4217
4211 if (nfsd4_has_session(&resp->cstate)) { 4218 if (nfsd4_has_session(&resp->cstate)) {
4212 if (open->op_deleg_want & NFS4_SHARE_WANT_NO_DELEG) { 4219 if (open->op_deleg_want & NFS4_SHARE_WANT_NO_DELEG) {
@@ -4819,10 +4826,13 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_
4819 * revoked delegations are kept only for free_stateid. 4826 * revoked delegations are kept only for free_stateid.
4820 */ 4827 */
4821 return nfserr_bad_stateid; 4828 return nfserr_bad_stateid;
4829 down_write(&stp->st_rwsem);
4822 status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate)); 4830 status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate));
4823 if (status) 4831 if (status == nfs_ok)
4824 return status; 4832 status = nfs4_check_fh(current_fh, &stp->st_stid);
4825 return nfs4_check_fh(current_fh, &stp->st_stid); 4833 if (status != nfs_ok)
4834 up_write(&stp->st_rwsem);
4835 return status;
4826} 4836}
4827 4837
4828/* 4838/*
@@ -4869,6 +4879,7 @@ static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cs
4869 return status; 4879 return status;
4870 oo = openowner(stp->st_stateowner); 4880 oo = openowner(stp->st_stateowner);
4871 if (!(oo->oo_flags & NFS4_OO_CONFIRMED)) { 4881 if (!(oo->oo_flags & NFS4_OO_CONFIRMED)) {
4882 up_write(&stp->st_rwsem);
4872 nfs4_put_stid(&stp->st_stid); 4883 nfs4_put_stid(&stp->st_stid);
4873 return nfserr_bad_stateid; 4884 return nfserr_bad_stateid;
4874 } 4885 }
@@ -4899,11 +4910,14 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
4899 goto out; 4910 goto out;
4900 oo = openowner(stp->st_stateowner); 4911 oo = openowner(stp->st_stateowner);
4901 status = nfserr_bad_stateid; 4912 status = nfserr_bad_stateid;
4902 if (oo->oo_flags & NFS4_OO_CONFIRMED) 4913 if (oo->oo_flags & NFS4_OO_CONFIRMED) {
4914 up_write(&stp->st_rwsem);
4903 goto put_stateid; 4915 goto put_stateid;
4916 }
4904 oo->oo_flags |= NFS4_OO_CONFIRMED; 4917 oo->oo_flags |= NFS4_OO_CONFIRMED;
4905 update_stateid(&stp->st_stid.sc_stateid); 4918 update_stateid(&stp->st_stid.sc_stateid);
4906 memcpy(&oc->oc_resp_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); 4919 memcpy(&oc->oc_resp_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
4920 up_write(&stp->st_rwsem);
4907 dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n", 4921 dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n",
4908 __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stid.sc_stateid)); 4922 __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stid.sc_stateid));
4909 4923
@@ -4982,6 +4996,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
4982 memcpy(&od->od_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); 4996 memcpy(&od->od_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
4983 status = nfs_ok; 4997 status = nfs_ok;
4984put_stateid: 4998put_stateid:
4999 up_write(&stp->st_rwsem);
4985 nfs4_put_stid(&stp->st_stid); 5000 nfs4_put_stid(&stp->st_stid);
4986out: 5001out:
4987 nfsd4_bump_seqid(cstate, status); 5002 nfsd4_bump_seqid(cstate, status);
@@ -5035,6 +5050,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
5035 goto out; 5050 goto out;
5036 update_stateid(&stp->st_stid.sc_stateid); 5051 update_stateid(&stp->st_stid.sc_stateid);
5037 memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); 5052 memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
5053 up_write(&stp->st_rwsem);
5038 5054
5039 nfsd4_close_open_stateid(stp); 5055 nfsd4_close_open_stateid(stp);
5040 5056
@@ -5260,6 +5276,7 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
5260 stp->st_access_bmap = 0; 5276 stp->st_access_bmap = 0;
5261 stp->st_deny_bmap = open_stp->st_deny_bmap; 5277 stp->st_deny_bmap = open_stp->st_deny_bmap;
5262 stp->st_openstp = open_stp; 5278 stp->st_openstp = open_stp;
5279 init_rwsem(&stp->st_rwsem);
5263 list_add(&stp->st_locks, &open_stp->st_locks); 5280 list_add(&stp->st_locks, &open_stp->st_locks);
5264 list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); 5281 list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids);
5265 spin_lock(&fp->fi_lock); 5282 spin_lock(&fp->fi_lock);
@@ -5428,6 +5445,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
5428 &open_stp, nn); 5445 &open_stp, nn);
5429 if (status) 5446 if (status)
5430 goto out; 5447 goto out;
5448 up_write(&open_stp->st_rwsem);
5431 open_sop = openowner(open_stp->st_stateowner); 5449 open_sop = openowner(open_stp->st_stateowner);
5432 status = nfserr_bad_stateid; 5450 status = nfserr_bad_stateid;
5433 if (!same_clid(&open_sop->oo_owner.so_client->cl_clientid, 5451 if (!same_clid(&open_sop->oo_owner.so_client->cl_clientid,
@@ -5435,6 +5453,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
5435 goto out; 5453 goto out;
5436 status = lookup_or_create_lock_state(cstate, open_stp, lock, 5454 status = lookup_or_create_lock_state(cstate, open_stp, lock,
5437 &lock_stp, &new); 5455 &lock_stp, &new);
5456 if (status == nfs_ok)
5457 down_write(&lock_stp->st_rwsem);
5438 } else { 5458 } else {
5439 status = nfs4_preprocess_seqid_op(cstate, 5459 status = nfs4_preprocess_seqid_op(cstate,
5440 lock->lk_old_lock_seqid, 5460 lock->lk_old_lock_seqid,
@@ -5540,6 +5560,8 @@ out:
5540 seqid_mutating_err(ntohl(status))) 5560 seqid_mutating_err(ntohl(status)))
5541 lock_sop->lo_owner.so_seqid++; 5561 lock_sop->lo_owner.so_seqid++;
5542 5562
5563 up_write(&lock_stp->st_rwsem);
5564
5543 /* 5565 /*
5544 * If this is a new, never-before-used stateid, and we are 5566 * If this is a new, never-before-used stateid, and we are
5545 * returning an error, then just go ahead and release it. 5567 * returning an error, then just go ahead and release it.
@@ -5709,6 +5731,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
5709fput: 5731fput:
5710 fput(filp); 5732 fput(filp);
5711put_stateid: 5733put_stateid:
5734 up_write(&stp->st_rwsem);
5712 nfs4_put_stid(&stp->st_stid); 5735 nfs4_put_stid(&stp->st_stid);
5713out: 5736out:
5714 nfsd4_bump_seqid(cstate, status); 5737 nfsd4_bump_seqid(cstate, status);
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 583ffc13cae2..31bde12feefe 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -534,15 +534,16 @@ struct nfs4_file {
534 * Better suggestions welcome. 534 * Better suggestions welcome.
535 */ 535 */
536struct nfs4_ol_stateid { 536struct nfs4_ol_stateid {
537 struct nfs4_stid st_stid; /* must be first field */ 537 struct nfs4_stid st_stid;
538 struct list_head st_perfile; 538 struct list_head st_perfile;
539 struct list_head st_perstateowner; 539 struct list_head st_perstateowner;
540 struct list_head st_locks; 540 struct list_head st_locks;
541 struct nfs4_stateowner * st_stateowner; 541 struct nfs4_stateowner *st_stateowner;
542 struct nfs4_clnt_odstate * st_clnt_odstate; 542 struct nfs4_clnt_odstate *st_clnt_odstate;
543 unsigned char st_access_bmap; 543 unsigned char st_access_bmap;
544 unsigned char st_deny_bmap; 544 unsigned char st_deny_bmap;
545 struct nfs4_ol_stateid * st_openstp; 545 struct nfs4_ol_stateid *st_openstp;
546 struct rw_semaphore st_rwsem;
546}; 547};
547 548
548static inline struct nfs4_ol_stateid *openlockstateid(struct nfs4_stid *s) 549static inline struct nfs4_ol_stateid *openlockstateid(struct nfs4_stid *s)