diff options
author | J. Bruce Fields <bfields@redhat.com> | 2011-09-16 20:12:38 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2011-09-19 08:39:34 -0400 |
commit | f7a4d872078a5e143d88adb561627f637046b05a (patch) | |
tree | 9a61699ca2705a48a46e5c276f587f1825dd27a0 /fs/nfsd/nfs4state.c | |
parent | d3b313a463c64c54d57c6af09c4a5d20106c1d1c (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.c | 101 |
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 | ||
407 | static void unhash_generic_stateid(struct nfs4_ol_stateid *stp) | 407 | static 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) | |||
485 | static void release_open_stateid(struct nfs4_ol_stateid *stp) | 485 | static 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 | ||
505 | static 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 | |||
504 | static void release_openowner(struct nfs4_openowner *oo) | 516 | static 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 | ||
3102 | static struct nfs4_openowner * search_close_lru(stateid_t *s) | 3113 | static 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 | |||
3115 | static inline int | ||
3116 | nfs4_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 | ||
3121 | static int | 3120 | static 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; |
3316 | out: | 3321 | out: |
@@ -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 | } |
3366 | out: | 3374 | out: |
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 | |||
3585 | static 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 |