| From a1d0b5eebc4fd6e0edb02688b35f17f67f42aea5 Mon Sep 17 00:00:00 2001 |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Date: Mon, 5 Mar 2012 19:56:44 -0500 |
| Subject: NFS: Properly handle the case where the delegation is revoked |
| |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| |
| commit a1d0b5eebc4fd6e0edb02688b35f17f67f42aea5 upstream. |
| |
| If we know that the delegation stateid is bad or revoked, we need to |
| remove that delegation as soon as possible, and then mark all the |
| stateids that relied on that delegation for recovery. We cannot use |
| the delegation as part of the recovery process. |
| |
| Also note that NFSv4.1 uses a different error code (NFS4ERR_DELEG_REVOKED) |
| to indicate that the delegation was revoked. |
| |
| Finally, ensure that setlk() and setattr() can both recover safely from |
| a revoked delegation. |
| |
| Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/nfs/delegation.c | 11 +++++++++++ |
| fs/nfs/delegation.h | 1 + |
| fs/nfs/nfs4_fs.h | 2 ++ |
| fs/nfs/nfs4proc.c | 18 ++++++++++++++++-- |
| fs/nfs/nfs4state.c | 29 +++++++++++++++++++++++++++-- |
| 5 files changed, 57 insertions(+), 4 deletions(-) |
| |
| --- a/fs/nfs/delegation.c |
| +++ b/fs/nfs/delegation.c |
| @@ -466,6 +466,17 @@ static void nfs_delegation_run_state_man |
| nfs4_schedule_state_manager(clp); |
| } |
| |
| +void nfs_remove_bad_delegation(struct inode *inode) |
| +{ |
| + struct nfs_delegation *delegation; |
| + |
| + delegation = nfs_detach_delegation(NFS_I(inode), NFS_SERVER(inode)); |
| + if (delegation) { |
| + nfs_inode_find_state_and_recover(inode, &delegation->stateid); |
| + nfs_free_delegation(delegation); |
| + } |
| +} |
| + |
| /** |
| * nfs_expire_all_delegation_types |
| * @clp: client to process |
| --- a/fs/nfs/delegation.h |
| +++ b/fs/nfs/delegation.h |
| @@ -45,6 +45,7 @@ void nfs_expire_unreferenced_delegations |
| void nfs_handle_cb_pathdown(struct nfs_client *clp); |
| int nfs_client_return_marked_delegations(struct nfs_client *clp); |
| int nfs_delegations_present(struct nfs_client *clp); |
| +void nfs_remove_bad_delegation(struct inode *inode); |
| |
| void nfs_delegation_mark_reclaim(struct nfs_client *clp); |
| void nfs_delegation_reap_unclaimed(struct nfs_client *clp); |
| --- a/fs/nfs/nfs4_fs.h |
| +++ b/fs/nfs/nfs4_fs.h |
| @@ -324,6 +324,8 @@ extern void nfs4_put_open_state(struct n |
| extern void nfs4_close_state(struct nfs4_state *, fmode_t); |
| extern void nfs4_close_sync(struct nfs4_state *, fmode_t); |
| extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t); |
| +extern void nfs_inode_find_state_and_recover(struct inode *inode, |
| + const nfs4_stateid *stateid); |
| extern void nfs4_schedule_lease_recovery(struct nfs_client *); |
| extern void nfs4_schedule_state_manager(struct nfs_client *); |
| extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp); |
| --- a/fs/nfs/nfs4proc.c |
| +++ b/fs/nfs/nfs4proc.c |
| @@ -263,8 +263,11 @@ static int nfs4_handle_exception(struct |
| switch(errorcode) { |
| case 0: |
| return 0; |
| + case -NFS4ERR_DELEG_REVOKED: |
| case -NFS4ERR_ADMIN_REVOKED: |
| case -NFS4ERR_BAD_STATEID: |
| + if (state != NULL) |
| + nfs_remove_bad_delegation(state->inode); |
| case -NFS4ERR_OPENMODE: |
| if (state == NULL) |
| break; |
| @@ -1316,8 +1319,11 @@ int nfs4_open_delegation_recall(struct n |
| * The show must go on: exit, but mark the |
| * stateid as needing recovery. |
| */ |
| + case -NFS4ERR_DELEG_REVOKED: |
| case -NFS4ERR_ADMIN_REVOKED: |
| case -NFS4ERR_BAD_STATEID: |
| + nfs_inode_find_state_and_recover(state->inode, |
| + stateid); |
| nfs4_schedule_stateid_recovery(server, state); |
| case -EKEYEXPIRED: |
| /* |
| @@ -1893,7 +1899,9 @@ static int nfs4_do_setattr(struct inode |
| struct nfs4_state *state) |
| { |
| struct nfs_server *server = NFS_SERVER(inode); |
| - struct nfs4_exception exception = { }; |
| + struct nfs4_exception exception = { |
| + .state = state, |
| + }; |
| int err; |
| do { |
| err = nfs4_handle_exception(server, |
| @@ -3707,8 +3715,11 @@ nfs4_async_handle_error(struct rpc_task |
| if (task->tk_status >= 0) |
| return 0; |
| switch(task->tk_status) { |
| + case -NFS4ERR_DELEG_REVOKED: |
| case -NFS4ERR_ADMIN_REVOKED: |
| case -NFS4ERR_BAD_STATEID: |
| + if (state != NULL) |
| + nfs_remove_bad_delegation(state->inode); |
| case -NFS4ERR_OPENMODE: |
| if (state == NULL) |
| break; |
| @@ -4526,7 +4537,9 @@ out: |
| |
| static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) |
| { |
| - struct nfs4_exception exception = { }; |
| + struct nfs4_exception exception = { |
| + .state = state, |
| + }; |
| int err; |
| |
| do { |
| @@ -4619,6 +4632,7 @@ int nfs4_lock_delegation_recall(struct n |
| * The show must go on: exit, but mark the |
| * stateid as needing recovery. |
| */ |
| + case -NFS4ERR_DELEG_REVOKED: |
| case -NFS4ERR_ADMIN_REVOKED: |
| case -NFS4ERR_BAD_STATEID: |
| case -NFS4ERR_OPENMODE: |
| --- a/fs/nfs/nfs4state.c |
| +++ b/fs/nfs/nfs4state.c |
| @@ -1071,12 +1071,37 @@ void nfs4_schedule_stateid_recovery(cons |
| { |
| struct nfs_client *clp = server->nfs_client; |
| |
| - if (test_and_clear_bit(NFS_DELEGATED_STATE, &state->flags)) |
| - nfs_async_inode_return_delegation(state->inode, &state->stateid); |
| nfs4_state_mark_reclaim_nograce(clp, state); |
| nfs4_schedule_state_manager(clp); |
| } |
| |
| +void nfs_inode_find_state_and_recover(struct inode *inode, |
| + const nfs4_stateid *stateid) |
| +{ |
| + struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; |
| + struct nfs_inode *nfsi = NFS_I(inode); |
| + struct nfs_open_context *ctx; |
| + struct nfs4_state *state; |
| + bool found = false; |
| + |
| + spin_lock(&inode->i_lock); |
| + list_for_each_entry(ctx, &nfsi->open_files, list) { |
| + state = ctx->state; |
| + if (state == NULL) |
| + continue; |
| + if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) |
| + continue; |
| + if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0) |
| + continue; |
| + nfs4_state_mark_reclaim_nograce(clp, state); |
| + found = true; |
| + } |
| + spin_unlock(&inode->i_lock); |
| + if (found) |
| + nfs4_schedule_state_manager(clp); |
| +} |
| + |
| + |
| static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops) |
| { |
| struct inode *inode = state->inode; |