diff options
author | J. Bruce Fields <bfields@redhat.com> | 2013-04-09 17:02:51 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2013-04-16 10:59:30 -0400 |
commit | 3bd64a5ba1719c2bb6cba4493dfd3e23a7653e54 (patch) | |
tree | c968f3f51d6082d3c7f0a03ec3d9fdaa031e6593 /fs/nfsd | |
parent | 23340032e64d70ce76817a88e8193c8040b095cf (diff) |
nfsd4: implement SEQ4_STATUS_RECALLABLE_STATE_REVOKED
A 4.1 server must notify a client that has had any state revoked using
the SEQ4_STATUS_RECALLABLE_STATE_REVOKED flag. The client can figure
out exactly which state is the problem using CHECK_STATEID and then free
it using FREE_STATEID. The status flag will be unset once all such
revoked stateids are freed.
Our server's only recallable state is delegations. So we keep with each
4.1 client a list of delegations that have timed out and been recalled,
but haven't yet been freed by FREE_STATEID.
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/nfs4state.c | 55 | ||||
-rw-r--r-- | fs/nfsd/state.h | 3 |
2 files changed, 50 insertions, 8 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index add9721ab059..3b84700d1bd7 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -445,7 +445,6 @@ static void unhash_stid(struct nfs4_stid *s) | |||
445 | static void | 445 | static void |
446 | unhash_delegation(struct nfs4_delegation *dp) | 446 | unhash_delegation(struct nfs4_delegation *dp) |
447 | { | 447 | { |
448 | unhash_stid(&dp->dl_stid); | ||
449 | list_del_init(&dp->dl_perclnt); | 448 | list_del_init(&dp->dl_perclnt); |
450 | spin_lock(&recall_lock); | 449 | spin_lock(&recall_lock); |
451 | list_del_init(&dp->dl_perfile); | 450 | list_del_init(&dp->dl_perfile); |
@@ -454,10 +453,37 @@ unhash_delegation(struct nfs4_delegation *dp) | |||
454 | nfs4_put_deleg_lease(dp->dl_file); | 453 | nfs4_put_deleg_lease(dp->dl_file); |
455 | put_nfs4_file(dp->dl_file); | 454 | put_nfs4_file(dp->dl_file); |
456 | dp->dl_file = NULL; | 455 | dp->dl_file = NULL; |
456 | } | ||
457 | |||
458 | |||
459 | |||
460 | static void destroy_revoked_delegation(struct nfs4_delegation *dp) | ||
461 | { | ||
462 | list_del_init(&dp->dl_recall_lru); | ||
457 | remove_stid(&dp->dl_stid); | 463 | remove_stid(&dp->dl_stid); |
458 | nfs4_put_delegation(dp); | 464 | nfs4_put_delegation(dp); |
459 | } | 465 | } |
460 | 466 | ||
467 | static void destroy_delegation(struct nfs4_delegation *dp) | ||
468 | { | ||
469 | unhash_delegation(dp); | ||
470 | remove_stid(&dp->dl_stid); | ||
471 | nfs4_put_delegation(dp); | ||
472 | } | ||
473 | |||
474 | static void revoke_delegation(struct nfs4_delegation *dp) | ||
475 | { | ||
476 | struct nfs4_client *clp = dp->dl_stid.sc_client; | ||
477 | |||
478 | if (clp->cl_minorversion == 0) | ||
479 | destroy_delegation(dp); | ||
480 | else { | ||
481 | unhash_delegation(dp); | ||
482 | dp->dl_stid.sc_type = NFS4_REVOKED_DELEG_STID; | ||
483 | list_add(&dp->dl_recall_lru, &clp->cl_revoked); | ||
484 | } | ||
485 | } | ||
486 | |||
461 | /* | 487 | /* |
462 | * SETCLIENTID state | 488 | * SETCLIENTID state |
463 | */ | 489 | */ |
@@ -1114,7 +1140,7 @@ destroy_client(struct nfs4_client *clp) | |||
1114 | spin_unlock(&recall_lock); | 1140 | spin_unlock(&recall_lock); |
1115 | while (!list_empty(&reaplist)) { | 1141 | while (!list_empty(&reaplist)) { |
1116 | dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); | 1142 | dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); |
1117 | unhash_delegation(dp); | 1143 | destroy_delegation(dp); |
1118 | } | 1144 | } |
1119 | while (!list_empty(&clp->cl_openowners)) { | 1145 | while (!list_empty(&clp->cl_openowners)) { |
1120 | oo = list_entry(clp->cl_openowners.next, struct nfs4_openowner, oo_perclient); | 1146 | oo = list_entry(clp->cl_openowners.next, struct nfs4_openowner, oo_perclient); |
@@ -1310,6 +1336,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, | |||
1310 | INIT_LIST_HEAD(&clp->cl_delegations); | 1336 | INIT_LIST_HEAD(&clp->cl_delegations); |
1311 | INIT_LIST_HEAD(&clp->cl_lru); | 1337 | INIT_LIST_HEAD(&clp->cl_lru); |
1312 | INIT_LIST_HEAD(&clp->cl_callbacks); | 1338 | INIT_LIST_HEAD(&clp->cl_callbacks); |
1339 | INIT_LIST_HEAD(&clp->cl_revoked); | ||
1313 | spin_lock_init(&clp->cl_lock); | 1340 | spin_lock_init(&clp->cl_lock); |
1314 | nfsd4_init_callback(&clp->cl_cb_null); | 1341 | nfsd4_init_callback(&clp->cl_cb_null); |
1315 | clp->cl_time = get_seconds(); | 1342 | clp->cl_time = get_seconds(); |
@@ -2171,6 +2198,8 @@ out: | |||
2171 | default: | 2198 | default: |
2172 | seq->status_flags = 0; | 2199 | seq->status_flags = 0; |
2173 | } | 2200 | } |
2201 | if (!list_empty(&clp->cl_revoked)) | ||
2202 | seq->status_flags |= SEQ4_STATUS_RECALLABLE_STATE_REVOKED; | ||
2174 | out_no_session: | 2203 | out_no_session: |
2175 | kfree(conn); | 2204 | kfree(conn); |
2176 | spin_unlock(&nn->client_lock); | 2205 | spin_unlock(&nn->client_lock); |
@@ -3297,7 +3326,7 @@ nfs4_laundromat(struct nfsd_net *nn) | |||
3297 | spin_unlock(&recall_lock); | 3326 | spin_unlock(&recall_lock); |
3298 | list_for_each_safe(pos, next, &reaplist) { | 3327 | list_for_each_safe(pos, next, &reaplist) { |
3299 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | 3328 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); |
3300 | unhash_delegation(dp); | 3329 | revoke_delegation(dp); |
3301 | } | 3330 | } |
3302 | test_val = nn->nfsd4_lease; | 3331 | test_val = nn->nfsd4_lease; |
3303 | list_for_each_safe(pos, next, &nn->close_lru) { | 3332 | list_for_each_safe(pos, next, &nn->close_lru) { |
@@ -3459,6 +3488,8 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) | |||
3459 | switch (s->sc_type) { | 3488 | switch (s->sc_type) { |
3460 | case NFS4_DELEG_STID: | 3489 | case NFS4_DELEG_STID: |
3461 | return nfs_ok; | 3490 | return nfs_ok; |
3491 | case NFS4_REVOKED_DELEG_STID: | ||
3492 | return nfserr_deleg_revoked; | ||
3462 | case NFS4_OPEN_STID: | 3493 | case NFS4_OPEN_STID: |
3463 | case NFS4_LOCK_STID: | 3494 | case NFS4_LOCK_STID: |
3464 | ols = openlockstateid(s); | 3495 | ols = openlockstateid(s); |
@@ -3602,6 +3633,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3602 | { | 3633 | { |
3603 | stateid_t *stateid = &free_stateid->fr_stateid; | 3634 | stateid_t *stateid = &free_stateid->fr_stateid; |
3604 | struct nfs4_stid *s; | 3635 | struct nfs4_stid *s; |
3636 | struct nfs4_delegation *dp; | ||
3605 | struct nfs4_client *cl = cstate->session->se_client; | 3637 | struct nfs4_client *cl = cstate->session->se_client; |
3606 | __be32 ret = nfserr_bad_stateid; | 3638 | __be32 ret = nfserr_bad_stateid; |
3607 | 3639 | ||
@@ -3623,6 +3655,11 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3623 | else | 3655 | else |
3624 | ret = nfserr_locks_held; | 3656 | ret = nfserr_locks_held; |
3625 | break; | 3657 | break; |
3658 | case NFS4_REVOKED_DELEG_STID: | ||
3659 | dp = delegstateid(s); | ||
3660 | destroy_revoked_delegation(dp); | ||
3661 | ret = nfs_ok; | ||
3662 | break; | ||
3626 | default: | 3663 | default: |
3627 | ret = nfserr_bad_stateid; | 3664 | ret = nfserr_bad_stateid; |
3628 | } | 3665 | } |
@@ -3647,10 +3684,12 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_ | |||
3647 | status = nfsd4_check_seqid(cstate, sop, seqid); | 3684 | status = nfsd4_check_seqid(cstate, sop, seqid); |
3648 | if (status) | 3685 | if (status) |
3649 | return status; | 3686 | return status; |
3650 | if (stp->st_stid.sc_type == NFS4_CLOSED_STID) | 3687 | if (stp->st_stid.sc_type == NFS4_CLOSED_STID |
3688 | || stp->st_stid.sc_type == NFS4_REVOKED_DELEG_STID) | ||
3651 | /* | 3689 | /* |
3652 | * "Closed" stateid's exist *only* to return | 3690 | * "Closed" stateid's exist *only* to return |
3653 | * nfserr_replay_me from the previous step. | 3691 | * nfserr_replay_me from the previous step, and |
3692 | * revoked delegations are kept only for free_stateid. | ||
3654 | */ | 3693 | */ |
3655 | return nfserr_bad_stateid; | 3694 | return nfserr_bad_stateid; |
3656 | status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate)); | 3695 | status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate)); |
@@ -3913,7 +3952,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3913 | if (status) | 3952 | if (status) |
3914 | goto out; | 3953 | goto out; |
3915 | 3954 | ||
3916 | unhash_delegation(dp); | 3955 | destroy_delegation(dp); |
3917 | out: | 3956 | out: |
3918 | nfs4_unlock_state(); | 3957 | nfs4_unlock_state(); |
3919 | 3958 | ||
@@ -4763,7 +4802,7 @@ u64 nfsd_forget_client_delegations(struct nfs4_client *clp, u64 max) | |||
4763 | spin_unlock(&recall_lock); | 4802 | spin_unlock(&recall_lock); |
4764 | 4803 | ||
4765 | list_for_each_entry_safe(dp, next, &victims, dl_recall_lru) | 4804 | list_for_each_entry_safe(dp, next, &victims, dl_recall_lru) |
4766 | unhash_delegation(dp); | 4805 | revoke_delegation(dp); |
4767 | 4806 | ||
4768 | return count; | 4807 | return count; |
4769 | } | 4808 | } |
@@ -5018,7 +5057,7 @@ nfs4_state_shutdown_net(struct net *net) | |||
5018 | spin_unlock(&recall_lock); | 5057 | spin_unlock(&recall_lock); |
5019 | list_for_each_safe(pos, next, &reaplist) { | 5058 | list_for_each_safe(pos, next, &reaplist) { |
5020 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | 5059 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); |
5021 | unhash_delegation(dp); | 5060 | destroy_delegation(dp); |
5022 | } | 5061 | } |
5023 | 5062 | ||
5024 | nfsd4_client_tracking_exit(net); | 5063 | nfsd4_client_tracking_exit(net); |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 13ec4853e9af..274e2a114e05 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -79,6 +79,8 @@ struct nfs4_stid { | |||
79 | #define NFS4_DELEG_STID 4 | 79 | #define NFS4_DELEG_STID 4 |
80 | /* For an open stateid kept around *only* to process close replays: */ | 80 | /* For an open stateid kept around *only* to process close replays: */ |
81 | #define NFS4_CLOSED_STID 8 | 81 | #define NFS4_CLOSED_STID 8 |
82 | /* For a deleg stateid kept around only to process free_stateid's: */ | ||
83 | #define NFS4_REVOKED_DELEG_STID 16 | ||
82 | unsigned char sc_type; | 84 | unsigned char sc_type; |
83 | stateid_t sc_stateid; | 85 | stateid_t sc_stateid; |
84 | struct nfs4_client *sc_client; | 86 | struct nfs4_client *sc_client; |
@@ -238,6 +240,7 @@ struct nfs4_client { | |||
238 | struct list_head cl_openowners; | 240 | struct list_head cl_openowners; |
239 | struct idr cl_stateids; /* stateid lookup */ | 241 | struct idr cl_stateids; /* stateid lookup */ |
240 | struct list_head cl_delegations; | 242 | struct list_head cl_delegations; |
243 | struct list_head cl_revoked; /* unacknowledged, revoked 4.1 state */ | ||
241 | struct list_head cl_lru; /* tail queue */ | 244 | struct list_head cl_lru; /* tail queue */ |
242 | struct xdr_netobj cl_name; /* id generated by client */ | 245 | struct xdr_netobj cl_name; /* id generated by client */ |
243 | nfs4_verifier cl_verifier; /* generated by client */ | 246 | nfs4_verifier cl_verifier; /* generated by client */ |