Skip to content

Commit

Permalink
NFSv4.1: Fix open stateid recovery
Browse files Browse the repository at this point in the history
The logic for checking in nfs41_check_open_stateid() whether the state
is supported by a delegation is inverted. In addition, it makes more
sense to perform that check before we check for expired locks.

Fixes: 8a64c4e ("NFSv4.1: Even if the stateid is OK,...")
Signed-off-by: Trond Myklebust <[email protected]>
  • Loading branch information
Trond Myklebust committed Aug 5, 2019
1 parent 731c74d commit 27a30cf
Showing 1 changed file with 38 additions and 27 deletions.
65 changes: 38 additions & 27 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1683,6 +1683,14 @@ static void nfs_state_set_open_stateid(struct nfs4_state *state,
write_sequnlock(&state->seqlock);
}

static void nfs_state_clear_open_state_flags(struct nfs4_state *state)
{
clear_bit(NFS_O_RDWR_STATE, &state->flags);
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
clear_bit(NFS_OPEN_STATE, &state->flags);
}

static void nfs_state_set_delegation(struct nfs4_state *state,
const nfs4_stateid *deleg_stateid,
fmode_t fmode)
Expand Down Expand Up @@ -2074,13 +2082,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
{
int ret;

/* Don't trigger recovery in nfs_test_and_clear_all_open_stateid */
clear_bit(NFS_O_RDWR_STATE, &state->flags);
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
/* memory barrier prior to reading state->n_* */
clear_bit(NFS_DELEGATED_STATE, &state->flags);
clear_bit(NFS_OPEN_STATE, &state->flags);
smp_rmb();
ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE);
if (ret != 0)
Expand Down Expand Up @@ -2156,6 +2158,8 @@ static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *sta
ctx = nfs4_state_find_open_context(state);
if (IS_ERR(ctx))
return -EAGAIN;
clear_bit(NFS_DELEGATED_STATE, &state->flags);
nfs_state_clear_open_state_flags(state);
ret = nfs4_do_open_reclaim(ctx, state);
put_nfs_open_context(ctx);
return ret;
Expand Down Expand Up @@ -2697,6 +2701,7 @@ static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
{
/* NFSv4.0 doesn't allow for delegation recovery on open expire */
nfs40_clear_delegation_stateid(state);
nfs_state_clear_open_state_flags(state);
return nfs4_open_expired(sp, state);
}

Expand Down Expand Up @@ -2739,34 +2744,29 @@ static int nfs41_test_and_free_expired_stateid(struct nfs_server *server,
return -NFS4ERR_EXPIRED;
}

static void nfs41_check_delegation_stateid(struct nfs4_state *state)
static int nfs41_check_delegation_stateid(struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(state->inode);
nfs4_stateid stateid;
struct nfs_delegation *delegation;
const struct cred *cred = NULL;
int status;
int status, ret = NFS_OK;

/* Get the delegation credential for use by test/free_stateid */
rcu_read_lock();
delegation = rcu_dereference(NFS_I(state->inode)->delegation);
if (delegation == NULL) {
rcu_read_unlock();
nfs_state_clear_delegation(state);
return;
return NFS_OK;
}

nfs4_stateid_copy(&stateid, &delegation->stateid);
if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
rcu_read_unlock();
nfs_state_clear_delegation(state);
return;
}

if (!test_and_clear_bit(NFS_DELEGATION_TEST_EXPIRED,
&delegation->flags)) {
rcu_read_unlock();
return;
return NFS_OK;
}

if (delegation->cred)
Expand All @@ -2776,8 +2776,24 @@ static void nfs41_check_delegation_stateid(struct nfs4_state *state)
trace_nfs4_test_delegation_stateid(state, NULL, status);
if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID)
nfs_finish_clear_delegation_stateid(state, &stateid);
else
ret = status;

put_cred(cred);
return ret;
}

static void nfs41_delegation_recover_stateid(struct nfs4_state *state)
{
nfs4_stateid tmp;

if (test_bit(NFS_DELEGATED_STATE, &state->flags) &&
nfs4_copy_delegation_stateid(state->inode, state->state,
&tmp, NULL) &&
nfs4_stateid_match_other(&state->stateid, &tmp))
nfs_state_set_delegation(state, &tmp, state->state);
else
nfs_state_clear_delegation(state);
}

/**
Expand Down Expand Up @@ -2847,21 +2863,12 @@ static int nfs41_check_open_stateid(struct nfs4_state *state)
const struct cred *cred = state->owner->so_cred;
int status;

if (test_bit(NFS_OPEN_STATE, &state->flags) == 0) {
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0) {
if (nfs4_have_delegation(state->inode, state->state))
return NFS_OK;
return -NFS4ERR_OPENMODE;
}
if (test_bit(NFS_OPEN_STATE, &state->flags) == 0)
return -NFS4ERR_BAD_STATEID;
}
status = nfs41_test_and_free_expired_stateid(server, stateid, cred);
trace_nfs4_test_open_stateid(state, NULL, status);
if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) {
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
clear_bit(NFS_O_RDWR_STATE, &state->flags);
clear_bit(NFS_OPEN_STATE, &state->flags);
nfs_state_clear_open_state_flags(state);
stateid->type = NFS4_INVALID_STATEID_TYPE;
return status;
}
Expand All @@ -2874,7 +2881,11 @@ static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
{
int status;

nfs41_check_delegation_stateid(state);
status = nfs41_check_delegation_stateid(state);
if (status != NFS_OK)
return status;
nfs41_delegation_recover_stateid(state);

status = nfs41_check_expired_locks(state);
if (status != NFS_OK)
return status;
Expand Down

0 comments on commit 27a30cf

Please sign in to comment.