Commit a1d0b5ee authored by Trond Myklebust's avatar Trond Myklebust

NFS: Properly handle the case where the delegation is revoked

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: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Cc: stable@vger.kernel.org
parent fa68a1ba
...@@ -466,6 +466,17 @@ static void nfs_delegation_run_state_manager(struct nfs_client *clp) ...@@ -466,6 +466,17 @@ static void nfs_delegation_run_state_manager(struct nfs_client *clp)
nfs4_schedule_state_manager(clp); 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 * nfs_expire_all_delegation_types
* @clp: client to process * @clp: client to process
......
...@@ -45,6 +45,7 @@ void nfs_expire_unreferenced_delegations(struct nfs_client *clp); ...@@ -45,6 +45,7 @@ void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
void nfs_handle_cb_pathdown(struct nfs_client *clp); void nfs_handle_cb_pathdown(struct nfs_client *clp);
int nfs_client_return_marked_delegations(struct nfs_client *clp); int nfs_client_return_marked_delegations(struct nfs_client *clp);
int nfs_delegations_present(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_mark_reclaim(struct nfs_client *clp);
void nfs_delegation_reap_unclaimed(struct nfs_client *clp); void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
......
...@@ -317,6 +317,8 @@ extern void nfs4_put_open_state(struct nfs4_state *); ...@@ -317,6 +317,8 @@ extern void nfs4_put_open_state(struct nfs4_state *);
extern void nfs4_close_state(struct nfs4_state *, fmode_t); extern void nfs4_close_state(struct nfs4_state *, fmode_t);
extern void nfs4_close_sync(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 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_lease_recovery(struct nfs_client *);
extern void nfs4_schedule_state_manager(struct nfs_client *); extern void nfs4_schedule_state_manager(struct nfs_client *);
extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp); extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp);
......
...@@ -268,8 +268,11 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc ...@@ -268,8 +268,11 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
switch(errorcode) { switch(errorcode) {
case 0: case 0:
return 0; return 0;
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_ADMIN_REVOKED: case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
if (state != NULL)
nfs_remove_bad_delegation(state->inode);
case -NFS4ERR_OPENMODE: case -NFS4ERR_OPENMODE:
if (state == NULL) if (state == NULL)
break; break;
...@@ -1331,8 +1334,11 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state ...@@ -1331,8 +1334,11 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
* The show must go on: exit, but mark the * The show must go on: exit, but mark the
* stateid as needing recovery. * stateid as needing recovery.
*/ */
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_ADMIN_REVOKED: case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
nfs_inode_find_state_and_recover(state->inode,
stateid);
nfs4_schedule_stateid_recovery(server, state); nfs4_schedule_stateid_recovery(server, state);
case -EKEYEXPIRED: case -EKEYEXPIRED:
/* /*
...@@ -1931,7 +1937,9 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, ...@@ -1931,7 +1937,9 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
struct nfs4_state *state) struct nfs4_state *state)
{ {
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_exception exception = { }; struct nfs4_exception exception = {
.state = state,
};
int err; int err;
do { do {
err = nfs4_handle_exception(server, err = nfs4_handle_exception(server,
...@@ -3760,8 +3768,11 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, ...@@ -3760,8 +3768,11 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
if (task->tk_status >= 0) if (task->tk_status >= 0)
return 0; return 0;
switch(task->tk_status) { switch(task->tk_status) {
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_ADMIN_REVOKED: case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
if (state != NULL)
nfs_remove_bad_delegation(state->inode);
case -NFS4ERR_OPENMODE: case -NFS4ERR_OPENMODE:
if (state == NULL) if (state == NULL)
break; break;
...@@ -4604,7 +4615,9 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock ...@@ -4604,7 +4615,9 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) 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; int err;
do { do {
...@@ -4697,6 +4710,7 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) ...@@ -4697,6 +4710,7 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
* The show must go on: exit, but mark the * The show must go on: exit, but mark the
* stateid as needing recovery. * stateid as needing recovery.
*/ */
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_ADMIN_REVOKED: case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_OPENMODE: case -NFS4ERR_OPENMODE:
......
...@@ -1106,12 +1106,37 @@ void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4 ...@@ -1106,12 +1106,37 @@ void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4
{ {
struct nfs_client *clp = server->nfs_client; 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_state_mark_reclaim_nograce(clp, state);
nfs4_schedule_state_manager(clp); 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) static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
{ {
struct inode *inode = state->inode; struct inode *inode = state->inode;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment