aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfs4state.c
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2011-09-16 20:12:38 -0400
committerJ. Bruce Fields <bfields@redhat.com>2011-09-19 08:39:34 -0400
commitf7a4d872078a5e143d88adb561627f637046b05a (patch)
tree9a61699ca2705a48a46e5c276f587f1825dd27a0 /fs/nfsd/nfs4state.c
parentd3b313a463c64c54d57c6af09c4a5d20106c1d1c (diff)
nfsd4: hash closed stateid's like any other
Look up closed stateid's in the stateid hash like any other stateid rather than searching the close lru. This is simpler, and fixes a bug: currently we handle only the case of a close that is the last close for a given stateowner, but not the case of a close for a stateowner that still has active opens on other files. Thus in a case like: open(owner, file1) open(owner, file2) close(owner, file2) close(owner, file2) the final close won't be recognized as a retransmission. Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd/nfs4state.c')
-rw-r--r--fs/nfsd/nfs4state.c101
1 files changed, 53 insertions, 48 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 922f47dd0d74..e5cba833613f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -406,7 +406,6 @@ static int nfs4_access_to_omode(u32 access)
406 406
407static void unhash_generic_stateid(struct nfs4_ol_stateid *stp) 407static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
408{ 408{
409 list_del(&stp->st_stid.sc_hash);
410 list_del(&stp->st_perfile); 409 list_del(&stp->st_perfile);
411 list_del(&stp->st_perstateowner); 410 list_del(&stp->st_perstateowner);
412} 411}
@@ -437,6 +436,7 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp)
437 struct file *file; 436 struct file *file;
438 437
439 unhash_generic_stateid(stp); 438 unhash_generic_stateid(stp);
439 list_del(&stp->st_stid.sc_hash);
440 file = find_any_file(stp->st_file); 440 file = find_any_file(stp->st_file);
441 if (file) 441 if (file)
442 locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner)); 442 locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner));
@@ -485,6 +485,7 @@ static void unhash_open_stateid(struct nfs4_ol_stateid *stp)
485static void release_open_stateid(struct nfs4_ol_stateid *stp) 485static void release_open_stateid(struct nfs4_ol_stateid *stp)
486{ 486{
487 unhash_open_stateid(stp); 487 unhash_open_stateid(stp);
488 list_del(&stp->st_stid.sc_hash);
488 free_generic_stateid(stp); 489 free_generic_stateid(stp);
489} 490}
490 491
@@ -501,12 +502,22 @@ static void unhash_openowner(struct nfs4_openowner *oo)
501 } 502 }
502} 503}
503 504
505static void release_last_closed_stateid(struct nfs4_openowner *oo)
506{
507 struct nfs4_ol_stateid *s = oo->oo_last_closed_stid;
508
509 if (s) {
510 list_del_init(&s->st_stid.sc_hash);
511 free_generic_stateid(s);
512 oo->oo_last_closed_stid = NULL;
513 }
514}
515
504static void release_openowner(struct nfs4_openowner *oo) 516static void release_openowner(struct nfs4_openowner *oo)
505{ 517{
506 unhash_openowner(oo); 518 unhash_openowner(oo);
507 list_del(&oo->oo_close_lru); 519 list_del(&oo->oo_close_lru);
508 if (oo->oo_last_closed_stid) 520 release_last_closed_stateid(oo);
509 free_generic_stateid(oo->oo_last_closed_stid);
510 nfs4_free_openowner(oo); 521 nfs4_free_openowner(oo);
511} 522}
512 523
@@ -3099,23 +3110,11 @@ laundromat_main(struct work_struct *not_used)
3099 queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); 3110 queue_delayed_work(laundry_wq, &laundromat_work, t*HZ);
3100} 3111}
3101 3112
3102static struct nfs4_openowner * search_close_lru(stateid_t *s) 3113static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp)
3103{
3104 struct nfs4_openowner *local;
3105 struct nfs4_ol_stateid *os;
3106
3107 list_for_each_entry(local, &close_lru, oo_close_lru) {
3108 os = local->oo_last_closed_stid;
3109 if (same_stateid(&os->st_stid.sc_stateid, s))
3110 return local;
3111 }
3112 return NULL;
3113}
3114
3115static inline int
3116nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp)
3117{ 3114{
3118 return fhp->fh_dentry->d_inode != stp->st_file->fi_inode; 3115 if (fhp->fh_dentry->d_inode != stp->st_file->fi_inode)
3116 return nfserr_bad_stateid;
3117 return nfs_ok;
3119} 3118}
3120 3119
3121static int 3120static int
@@ -3283,7 +3282,8 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
3283 status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); 3282 status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
3284 if (status) 3283 if (status)
3285 goto out; 3284 goto out;
3286 if (s->sc_type == NFS4_DELEG_STID) { 3285 switch (s->sc_type) {
3286 case NFS4_DELEG_STID:
3287 dp = delegstateid(s); 3287 dp = delegstateid(s);
3288 status = nfs4_check_delegmode(dp, flags); 3288 status = nfs4_check_delegmode(dp, flags);
3289 if (status) 3289 if (status)
@@ -3293,10 +3293,12 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
3293 *filpp = dp->dl_file->fi_deleg_file; 3293 *filpp = dp->dl_file->fi_deleg_file;
3294 BUG_ON(!*filpp); 3294 BUG_ON(!*filpp);
3295 } 3295 }
3296 } else { /* open or lock stateid */ 3296 break;
3297 case NFS4_OPEN_STID:
3298 case NFS4_LOCK_STID:
3297 stp = openlockstateid(s); 3299 stp = openlockstateid(s);
3298 status = nfserr_bad_stateid; 3300 status = nfs4_check_fh(current_fh, stp);
3299 if (nfs4_check_fh(current_fh, stp)) 3301 if (status)
3300 goto out; 3302 goto out;
3301 if (stp->st_stateowner->so_is_open_owner 3303 if (stp->st_stateowner->so_is_open_owner
3302 && !(openowner(stp->st_stateowner)->oo_flags & NFS4_OO_CONFIRMED)) 3304 && !(openowner(stp->st_stateowner)->oo_flags & NFS4_OO_CONFIRMED))
@@ -3311,6 +3313,9 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
3311 else 3313 else
3312 *filpp = find_writeable_file(stp->st_file); 3314 *filpp = find_writeable_file(stp->st_file);
3313 } 3315 }
3316 break;
3317 default:
3318 return nfserr_bad_stateid;
3314 } 3319 }
3315 status = nfs_ok; 3320 status = nfs_ok;
3316out: 3321out:
@@ -3362,6 +3367,9 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
3362 ret = nfsd4_free_lock_stateid(openlockstateid(s)); 3367 ret = nfsd4_free_lock_stateid(openlockstateid(s));
3363 else 3368 else
3364 ret = nfserr_locks_held; 3369 ret = nfserr_locks_held;
3370 break;
3371 default:
3372 ret = nfserr_bad_stateid;
3365 } 3373 }
3366out: 3374out:
3367 nfs4_unlock_state(); 3375 nfs4_unlock_state();
@@ -3390,12 +3398,19 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_
3390 struct nfs4_stateowner *sop = stp->st_stateowner; 3398 struct nfs4_stateowner *sop = stp->st_stateowner;
3391 __be32 status; 3399 __be32 status;
3392 3400
3393 if (nfs4_check_fh(current_fh, stp))
3394 return nfserr_bad_stateid;
3395 status = nfsd4_check_seqid(cstate, sop, seqid); 3401 status = nfsd4_check_seqid(cstate, sop, seqid);
3396 if (status) 3402 if (status)
3397 return status; 3403 return status;
3398 return check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate)); 3404 if (stp->st_stid.sc_type == NFS4_CLOSED_STID)
3405 /*
3406 * "Closed" stateid's exist *only* to return
3407 * nfserr_replay_me from the previous step.
3408 */
3409 return nfserr_bad_stateid;
3410 status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate));
3411 if (status)
3412 return status;
3413 return nfs4_check_fh(current_fh, stp);
3399} 3414}
3400 3415
3401/* 3416/*
@@ -3564,8 +3579,13 @@ void nfsd4_purge_closed_stateid(struct nfs4_stateowner *so)
3564 return; 3579 return;
3565 } 3580 }
3566 oo->oo_flags &= ~NFS4_OO_PURGE_CLOSE; 3581 oo->oo_flags &= ~NFS4_OO_PURGE_CLOSE;
3567 free_generic_stateid(oo->oo_last_closed_stid); 3582 release_last_closed_stateid(oo);
3568 oo->oo_last_closed_stid = NULL; 3583}
3584
3585static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
3586{
3587 unhash_open_stateid(s);
3588 s->st_stid.sc_type = NFS4_CLOSED_STID;
3569} 3589}
3570 3590
3571/* 3591/*
@@ -3584,24 +3604,10 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
3584 cstate->current_fh.fh_dentry->d_name.name); 3604 cstate->current_fh.fh_dentry->d_name.name);
3585 3605
3586 nfs4_lock_state(); 3606 nfs4_lock_state();
3587 /* check close_lru for replay */ 3607 status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid,
3588 status = nfs4_preprocess_confirmed_seqid_op(cstate, close->cl_seqid, 3608 &close->cl_stateid,
3589 &close->cl_stateid, &stp); 3609 NFS4_OPEN_STID|NFS4_CLOSED_STID,
3590 if (stp == NULL && status == nfserr_expired) { 3610 &stp);
3591 /*
3592 * Also, we should make sure this isn't just the result of
3593 * a replayed close:
3594 */
3595 oo = search_close_lru(&close->cl_stateid);
3596 /* It's not stale; let's assume it's expired: */
3597 if (oo == NULL)
3598 goto out;
3599 cstate->replay_owner = &oo->oo_owner;
3600 status = nfsd4_check_seqid(cstate, &oo->oo_owner, close->cl_seqid);
3601 if (status)
3602 goto out;
3603 status = nfserr_bad_seqid;
3604 }
3605 if (status) 3611 if (status)
3606 goto out; 3612 goto out;
3607 oo = openowner(stp->st_stateowner); 3613 oo = openowner(stp->st_stateowner);
@@ -3609,9 +3615,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
3609 update_stateid(&stp->st_stid.sc_stateid); 3615 update_stateid(&stp->st_stid.sc_stateid);
3610 memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); 3616 memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
3611 3617
3612 /* unhash_open_stateid() calls nfsd_close() if needed */ 3618 nfsd4_close_open_stateid(stp);
3613 oo->oo_last_closed_stid = stp; 3619 oo->oo_last_closed_stid = stp;
3614 unhash_open_stateid(stp);
3615 3620
3616 /* place unused nfs4_stateowners on so_close_lru list to be 3621 /* place unused nfs4_stateowners on so_close_lru list to be
3617 * released by the laundromat service after the lease period 3622 * released by the laundromat service after the lease period