NFSv4: Fix delegation state recovery

Once we clear the NFS_DELEGATED_STATE flag, we're telling
nfs_delegation_claim_opens() that we're done recovering all open state
for that stateid, so we really need to ensure that we test for all
open modes that are currently cached and recover them before exiting
nfs4_open_delegation_recall().

Fixes: 24311f8841 ("NFSv4: Recovery of recalled read delegations...")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Cc: stable@vger.kernel.org # v4.3+
This commit is contained in:
Trond Myklebust 2019-07-19 14:08:37 -04:00
parent 8c39a39e28
commit 5eb8d18ca0
3 changed files with 15 additions and 16 deletions

View File

@ -153,7 +153,7 @@ again:
/* Block nfs4_proc_unlck */ /* Block nfs4_proc_unlck */
mutex_lock(&sp->so_delegreturn_mutex); mutex_lock(&sp->so_delegreturn_mutex);
seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
err = nfs4_open_delegation_recall(ctx, state, stateid, type); err = nfs4_open_delegation_recall(ctx, state, stateid);
if (!err) if (!err)
err = nfs_delegation_claim_locks(state, stateid); err = nfs_delegation_claim_locks(state, stateid);
if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))

View File

@ -63,7 +63,7 @@ void nfs_reap_expired_delegations(struct nfs_client *clp);
/* NFSv4 delegation-related procedures */ /* NFSv4 delegation-related procedures */
int nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred, const nfs4_stateid *stateid, int issync); int nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred, const nfs4_stateid *stateid, int issync);
int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid, fmode_t type); int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid);
int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid); int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid);
bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, nfs4_stateid *dst, const struct cred **cred); bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, nfs4_stateid *dst, const struct cred **cred);
bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode); bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode);

View File

@ -2177,12 +2177,10 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
case -NFS4ERR_BAD_HIGH_SLOT: case -NFS4ERR_BAD_HIGH_SLOT:
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
case -NFS4ERR_DEADSESSION: case -NFS4ERR_DEADSESSION:
set_bit(NFS_DELEGATED_STATE, &state->flags);
nfs4_schedule_session_recovery(server->nfs_client->cl_session, err); nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
return -EAGAIN; return -EAGAIN;
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
set_bit(NFS_DELEGATED_STATE, &state->flags);
/* Don't recall a delegation if it was lost */ /* Don't recall a delegation if it was lost */
nfs4_schedule_lease_recovery(server->nfs_client); nfs4_schedule_lease_recovery(server->nfs_client);
return -EAGAIN; return -EAGAIN;
@ -2203,7 +2201,6 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
return -EAGAIN; return -EAGAIN;
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
case -NFS4ERR_GRACE: case -NFS4ERR_GRACE:
set_bit(NFS_DELEGATED_STATE, &state->flags);
ssleep(1); ssleep(1);
return -EAGAIN; return -EAGAIN;
case -ENOMEM: case -ENOMEM:
@ -2219,8 +2216,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
} }
int nfs4_open_delegation_recall(struct nfs_open_context *ctx, int nfs4_open_delegation_recall(struct nfs_open_context *ctx,
struct nfs4_state *state, const nfs4_stateid *stateid, struct nfs4_state *state, const nfs4_stateid *stateid)
fmode_t type)
{ {
struct nfs_server *server = NFS_SERVER(state->inode); struct nfs_server *server = NFS_SERVER(state->inode);
struct nfs4_opendata *opendata; struct nfs4_opendata *opendata;
@ -2231,20 +2227,23 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx,
if (IS_ERR(opendata)) if (IS_ERR(opendata))
return PTR_ERR(opendata); return PTR_ERR(opendata);
nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid); nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
nfs_state_clear_delegation(state); if (!test_bit(NFS_O_RDWR_STATE, &state->flags)) {
switch (type & (FMODE_READ|FMODE_WRITE)) {
case FMODE_READ|FMODE_WRITE:
case FMODE_WRITE:
err = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE); err = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE);
if (err) if (err)
break; goto out;
}
if (!test_bit(NFS_O_WRONLY_STATE, &state->flags)) {
err = nfs4_open_recover_helper(opendata, FMODE_WRITE); err = nfs4_open_recover_helper(opendata, FMODE_WRITE);
if (err) if (err)
break; goto out;
/* Fall through */
case FMODE_READ:
err = nfs4_open_recover_helper(opendata, FMODE_READ);
} }
if (!test_bit(NFS_O_RDONLY_STATE, &state->flags)) {
err = nfs4_open_recover_helper(opendata, FMODE_READ);
if (err)
goto out;
}
nfs_state_clear_delegation(state);
out:
nfs4_opendata_put(opendata); nfs4_opendata_put(opendata);
return nfs4_handle_delegation_recall_error(server, state, stateid, NULL, err); return nfs4_handle_delegation_recall_error(server, state, stateid, NULL, err);
} }