aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorJeff Layton <jlayton@poochiereds.net>2015-10-01 09:05:50 -0400
committerJ. Bruce Fields <bfields@redhat.com>2015-10-23 15:57:33 -0400
commit9767feb2c64b29775f1ea683130b44f95f67d169 (patch)
treecfde0f75348706306314749a9ac8164d0237a0d4 /fs/nfsd
parentcc8a55320b5f1196bee5bd14e4bb2ebd3b983317 (diff)
nfsd: ensure that seqid morphing operations are atomic wrt to copies
Bruce points out that the increment of the seqid in stateids is not serialized in any way, so it's possible for racing calls to bump it twice and end up sending the same stateid. While we don't have any reports of this problem it _is_ theoretically possible, and could lead to spurious state recovery by the client. In the current code, update_stateid is always followed by a memcpy of that stateid, so we can combine the two operations. For better atomicity, we add a spinlock to the nfs4_stid and hold that when bumping the seqid and copying the stateid. Signed-off-by: Jeff Layton <jeff.layton@primarydata.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4layouts.c13
-rw-r--r--fs/nfsd/nfs4state.c34
-rw-r--r--fs/nfsd/state.h23
3 files changed, 31 insertions, 39 deletions
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index 4a68ab901b4b..9ffef06b30d5 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -409,8 +409,7 @@ nfsd4_insert_layout(struct nfsd4_layoutget *lgp, struct nfs4_layout_stateid *ls)
409 list_add_tail(&new->lo_perstate, &ls->ls_layouts); 409 list_add_tail(&new->lo_perstate, &ls->ls_layouts);
410 new = NULL; 410 new = NULL;
411done: 411done:
412 update_stateid(&ls->ls_stid.sc_stateid); 412 nfs4_inc_and_copy_stateid(&lgp->lg_sid, &ls->ls_stid);
413 memcpy(&lgp->lg_sid, &ls->ls_stid.sc_stateid, sizeof(stateid_t));
414 spin_unlock(&ls->ls_lock); 413 spin_unlock(&ls->ls_lock);
415out: 414out:
416 spin_unlock(&fp->fi_lock); 415 spin_unlock(&fp->fi_lock);
@@ -484,11 +483,8 @@ nfsd4_return_file_layouts(struct svc_rqst *rqstp,
484 } 483 }
485 } 484 }
486 if (!list_empty(&ls->ls_layouts)) { 485 if (!list_empty(&ls->ls_layouts)) {
487 if (found) { 486 if (found)
488 update_stateid(&ls->ls_stid.sc_stateid); 487 nfs4_inc_and_copy_stateid(&lrp->lr_sid, &ls->ls_stid);
489 memcpy(&lrp->lr_sid, &ls->ls_stid.sc_stateid,
490 sizeof(stateid_t));
491 }
492 lrp->lrs_present = 1; 488 lrp->lrs_present = 1;
493 } else { 489 } else {
494 trace_layoutstate_unhash(&ls->ls_stid.sc_stateid); 490 trace_layoutstate_unhash(&ls->ls_stid.sc_stateid);
@@ -619,8 +615,7 @@ nfsd4_cb_layout_prepare(struct nfsd4_callback *cb)
619 container_of(cb, struct nfs4_layout_stateid, ls_recall); 615 container_of(cb, struct nfs4_layout_stateid, ls_recall);
620 616
621 mutex_lock(&ls->ls_mutex); 617 mutex_lock(&ls->ls_mutex);
622 update_stateid(&ls->ls_stid.sc_stateid); 618 nfs4_inc_and_copy_stateid(&ls->ls_recall_sid, &ls->ls_stid);
623 memcpy(&ls->ls_recall_sid, &ls->ls_stid.sc_stateid, sizeof(stateid_t));
624} 619}
625 620
626static int 621static int
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f2ea343086b8..0a697158a4ca 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -575,6 +575,7 @@ struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl,
575 stid->sc_stateid.si_opaque.so_clid = cl->cl_clientid; 575 stid->sc_stateid.si_opaque.so_clid = cl->cl_clientid;
576 /* Will be incremented before return to client: */ 576 /* Will be incremented before return to client: */
577 atomic_set(&stid->sc_count, 1); 577 atomic_set(&stid->sc_count, 1);
578 spin_lock_init(&stid->sc_lock);
578 579
579 /* 580 /*
580 * It shouldn't be a problem to reuse an opaque stateid value. 581 * It shouldn't be a problem to reuse an opaque stateid value.
@@ -745,6 +746,18 @@ nfs4_put_stid(struct nfs4_stid *s)
745 put_nfs4_file(fp); 746 put_nfs4_file(fp);
746} 747}
747 748
749void
750nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid)
751{
752 stateid_t *src = &stid->sc_stateid;
753
754 spin_lock(&stid->sc_lock);
755 if (unlikely(++src->si_generation == 0))
756 src->si_generation = 1;
757 memcpy(dst, src, sizeof(*dst));
758 spin_unlock(&stid->sc_lock);
759}
760
748static void nfs4_put_deleg_lease(struct nfs4_file *fp) 761static void nfs4_put_deleg_lease(struct nfs4_file *fp)
749{ 762{
750 struct file *filp = NULL; 763 struct file *filp = NULL;
@@ -4221,8 +4234,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4221 if (stp->st_clnt_odstate == open->op_odstate) 4234 if (stp->st_clnt_odstate == open->op_odstate)
4222 open->op_odstate = NULL; 4235 open->op_odstate = NULL;
4223 } 4236 }
4224 update_stateid(&stp->st_stid.sc_stateid); 4237 nfs4_inc_and_copy_stateid(&open->op_stateid, &stp->st_stid);
4225 memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
4226 up_read(&stp->st_rwsem); 4238 up_read(&stp->st_rwsem);
4227 4239
4228 if (nfsd4_has_session(&resp->cstate)) { 4240 if (nfsd4_has_session(&resp->cstate)) {
@@ -4925,8 +4937,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
4925 goto put_stateid; 4937 goto put_stateid;
4926 } 4938 }
4927 oo->oo_flags |= NFS4_OO_CONFIRMED; 4939 oo->oo_flags |= NFS4_OO_CONFIRMED;
4928 update_stateid(&stp->st_stid.sc_stateid); 4940 nfs4_inc_and_copy_stateid(&oc->oc_resp_stateid, &stp->st_stid);
4929 memcpy(&oc->oc_resp_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
4930 up_write(&stp->st_rwsem); 4941 up_write(&stp->st_rwsem);
4931 dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n", 4942 dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n",
4932 __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stid.sc_stateid)); 4943 __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stid.sc_stateid));
@@ -4999,11 +5010,8 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
4999 goto put_stateid; 5010 goto put_stateid;
5000 } 5011 }
5001 nfs4_stateid_downgrade(stp, od->od_share_access); 5012 nfs4_stateid_downgrade(stp, od->od_share_access);
5002
5003 reset_union_bmap_deny(od->od_share_deny, stp); 5013 reset_union_bmap_deny(od->od_share_deny, stp);
5004 5014 nfs4_inc_and_copy_stateid(&od->od_stateid, &stp->st_stid);
5005 update_stateid(&stp->st_stid.sc_stateid);
5006 memcpy(&od->od_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
5007 status = nfs_ok; 5015 status = nfs_ok;
5008put_stateid: 5016put_stateid:
5009 up_write(&stp->st_rwsem); 5017 up_write(&stp->st_rwsem);
@@ -5058,8 +5066,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
5058 nfsd4_bump_seqid(cstate, status); 5066 nfsd4_bump_seqid(cstate, status);
5059 if (status) 5067 if (status)
5060 goto out; 5068 goto out;
5061 update_stateid(&stp->st_stid.sc_stateid); 5069 nfs4_inc_and_copy_stateid(&close->cl_stateid, &stp->st_stid);
5062 memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
5063 up_write(&stp->st_rwsem); 5070 up_write(&stp->st_rwsem);
5064 5071
5065 nfsd4_close_open_stateid(stp); 5072 nfsd4_close_open_stateid(stp);
@@ -5542,9 +5549,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
5542 err = vfs_lock_file(filp, F_SETLK, file_lock, conflock); 5549 err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
5543 switch (-err) { 5550 switch (-err) {
5544 case 0: /* success! */ 5551 case 0: /* success! */
5545 update_stateid(&lock_stp->st_stid.sc_stateid); 5552 nfs4_inc_and_copy_stateid(&lock->lk_resp_stateid, &lock_stp->st_stid);
5546 memcpy(&lock->lk_resp_stateid, &lock_stp->st_stid.sc_stateid,
5547 sizeof(stateid_t));
5548 status = 0; 5553 status = 0;
5549 break; 5554 break;
5550 case (EAGAIN): /* conflock holds conflicting lock */ 5555 case (EAGAIN): /* conflock holds conflicting lock */
@@ -5736,8 +5741,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
5736 dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n"); 5741 dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n");
5737 goto out_nfserr; 5742 goto out_nfserr;
5738 } 5743 }
5739 update_stateid(&stp->st_stid.sc_stateid); 5744 nfs4_inc_and_copy_stateid(&locku->lu_stateid, &stp->st_stid);
5740 memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
5741fput: 5745fput:
5742 fput(filp); 5746 fput(filp);
5743put_stateid: 5747put_stateid:
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 1fa0f3848d4e..77fdf4de91ba 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -84,7 +84,7 @@ struct nfsd4_callback_ops {
84 * fields that are of general use to any stateid. 84 * fields that are of general use to any stateid.
85 */ 85 */
86struct nfs4_stid { 86struct nfs4_stid {
87 atomic_t sc_count; 87 atomic_t sc_count;
88#define NFS4_OPEN_STID 1 88#define NFS4_OPEN_STID 1
89#define NFS4_LOCK_STID 2 89#define NFS4_LOCK_STID 2
90#define NFS4_DELEG_STID 4 90#define NFS4_DELEG_STID 4
@@ -94,11 +94,12 @@ struct nfs4_stid {
94#define NFS4_REVOKED_DELEG_STID 16 94#define NFS4_REVOKED_DELEG_STID 16
95#define NFS4_CLOSED_DELEG_STID 32 95#define NFS4_CLOSED_DELEG_STID 32
96#define NFS4_LAYOUT_STID 64 96#define NFS4_LAYOUT_STID 64
97 unsigned char sc_type; 97 unsigned char sc_type;
98 stateid_t sc_stateid; 98 stateid_t sc_stateid;
99 struct nfs4_client *sc_client; 99 spinlock_t sc_lock;
100 struct nfs4_file *sc_file; 100 struct nfs4_client *sc_client;
101 void (*sc_free)(struct nfs4_stid *); 101 struct nfs4_file *sc_file;
102 void (*sc_free)(struct nfs4_stid *);
102}; 103};
103 104
104/* 105/*
@@ -364,15 +365,6 @@ struct nfs4_client_reclaim {
364 char cr_recdir[HEXDIR_LEN]; /* recover dir */ 365 char cr_recdir[HEXDIR_LEN]; /* recover dir */
365}; 366};
366 367
367static inline void
368update_stateid(stateid_t *stateid)
369{
370 stateid->si_generation++;
371 /* Wraparound recommendation from 3530bis-13 9.1.3.2: */
372 if (stateid->si_generation == 0)
373 stateid->si_generation = 1;
374}
375
376/* A reasonable value for REPLAY_ISIZE was estimated as follows: 368/* A reasonable value for REPLAY_ISIZE was estimated as follows:
377 * The OPEN response, typically the largest, requires 369 * The OPEN response, typically the largest, requires
378 * 4(status) + 8(stateid) + 20(changeinfo) + 4(rflags) + 8(verifier) + 370 * 4(status) + 8(stateid) + 20(changeinfo) + 4(rflags) + 8(verifier) +
@@ -595,6 +587,7 @@ struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl,
595 struct kmem_cache *slab); 587 struct kmem_cache *slab);
596void nfs4_unhash_stid(struct nfs4_stid *s); 588void nfs4_unhash_stid(struct nfs4_stid *s);
597void nfs4_put_stid(struct nfs4_stid *s); 589void nfs4_put_stid(struct nfs4_stid *s);
590void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid);
598void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *); 591void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *);
599extern void nfs4_release_reclaim(struct nfsd_net *); 592extern void nfs4_release_reclaim(struct nfsd_net *);
600extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir, 593extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir,