Skip to content

Commit

Permalink
Merge tag 'nfs-for-5.3-2' of git://git.linux-nfs.org/projects/trondmy…
Browse files Browse the repository at this point in the history
…/linux-nfs

Pull NFS client fixes from Trond Myklebust:
 "Highlights include:

  Stable fixes:

   - NFSv4: Ensure we check the return value of update_open_stateid() so
     we correctly track active open state.

   - NFSv4: Fix for delegation state recovery to ensure we recover all
     open modes that are active.

   - NFSv4: Fix an Oops in nfs4_do_setattr

  Fixes:

   - NFS: Fix regression whereby fscache errors are appearing on 'nofsc'
     mounts

   - NFSv4: Fix a potential sleep while atomic in nfs4_do_reclaim()

   - NFSv4: Fix a credential refcount leak in nfs41_check_delegation_stateid

   - pNFS: Report errors from the call to nfs4_select_rw_stateid()

   - NFSv4: Various other delegation and open stateid recovery fixes

   - NFSv4: Fix state recovery behaviour when server connection times
     out"

* tag 'nfs-for-5.3-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFSv4: Ensure state recovery handles ETIMEDOUT correctly
  NFS: Fix regression whereby fscache errors are appearing on 'nofsc' mounts
  NFSv4: Fix an Oops in nfs4_do_setattr
  NFSv4: Fix a potential sleep while atomic in nfs4_do_reclaim()
  NFSv4: Check the return value of update_open_stateid()
  NFSv4.1: Only reap expired delegations
  NFSv4.1: Fix open stateid recovery
  NFSv4: Report the error from nfs4_select_rw_stateid()
  NFSv4: When recovering state fails with EAGAIN, retry the same recovery
  NFSv4: Print an error in the syslog when state is marked as irrecoverable
  NFSv4: Fix delegation state recovery
  NFSv4: Fix a credential refcount leak in nfs41_check_delegation_stateid
  • Loading branch information
torvalds committed Aug 8, 2019
2 parents 518a1c2 + 67e7b52 commit b678c56
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 75 deletions.
25 changes: 18 additions & 7 deletions fs/nfs/delegation.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ static int nfs_delegation_claim_opens(struct inode *inode,
/* Block nfs4_proc_unlck */
mutex_lock(&sp->so_delegreturn_mutex);
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)
err = nfs_delegation_claim_locks(state, stateid);
if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
Expand Down Expand Up @@ -1046,6 +1046,22 @@ void nfs_test_expired_all_delegations(struct nfs_client *clp)
nfs4_schedule_state_manager(clp);
}

static void
nfs_delegation_test_free_expired(struct inode *inode,
nfs4_stateid *stateid,
const struct cred *cred)
{
struct nfs_server *server = NFS_SERVER(inode);
const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops;
int status;

if (!cred)
return;
status = ops->test_and_free_expired(server, stateid, cred);
if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID)
nfs_remove_bad_delegation(inode, stateid);
}

/**
* nfs_reap_expired_delegations - reap expired delegations
* @clp: nfs_client to process
Expand All @@ -1057,7 +1073,6 @@ void nfs_test_expired_all_delegations(struct nfs_client *clp)
*/
void nfs_reap_expired_delegations(struct nfs_client *clp)
{
const struct nfs4_minor_version_ops *ops = clp->cl_mvops;
struct nfs_delegation *delegation;
struct nfs_server *server;
struct inode *inode;
Expand Down Expand Up @@ -1088,11 +1103,7 @@ void nfs_reap_expired_delegations(struct nfs_client *clp)
nfs4_stateid_copy(&stateid, &delegation->stateid);
clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
rcu_read_unlock();
if (cred != NULL &&
ops->test_and_free_expired(server, &stateid, cred) < 0) {
nfs_revoke_delegation(inode, &stateid);
nfs_inode_find_state_and_recover(inode, &stateid);
}
nfs_delegation_test_free_expired(inode, &stateid, cred);
put_cred(cred);
if (nfs4_server_rebooted(clp)) {
nfs_inode_mark_test_expired_delegation(server,inode);
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/delegation.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ void nfs_reap_expired_delegations(struct nfs_client *clp);

/* NFSv4 delegation-related procedures */
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);
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);
Expand Down
7 changes: 6 additions & 1 deletion fs/nfs/fscache.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int
struct rb_node **p, *parent;
int diff;

nfss->fscache_key = NULL;
nfss->fscache = NULL;
if (!(nfss->options & NFS_OPTION_FSCACHE))
return;
if (!uniq) {
uniq = "";
ulen = 1;
Expand Down Expand Up @@ -226,10 +230,11 @@ void nfs_fscache_release_super_cookie(struct super_block *sb)
void nfs_fscache_init_inode(struct inode *inode)
{
struct nfs_fscache_inode_auxdata auxdata;
struct nfs_server *nfss = NFS_SERVER(inode);
struct nfs_inode *nfsi = NFS_I(inode);

nfsi->fscache = NULL;
if (!S_ISREG(inode->i_mode))
if (!(nfss->fscache && S_ISREG(inode->i_mode)))
return;

memset(&auxdata, 0, sizeof(auxdata));
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/fscache.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ static inline void nfs_fscache_wait_on_invalidate(struct inode *inode)
*/
static inline const char *nfs_server_fscache_state(struct nfs_server *server)
{
if (server->fscache && (server->options & NFS_OPTION_FSCACHE))
if (server->fscache)
return "yes";
return "no ";
}
Expand Down
3 changes: 2 additions & 1 deletion fs/nfs/nfs4_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,8 @@ static inline void nfs4_schedule_session_recovery(struct nfs4_session *session,

extern struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *, const struct cred *, gfp_t);
extern void nfs4_put_state_owner(struct nfs4_state_owner *);
extern void nfs4_purge_state_owners(struct nfs_server *);
extern void nfs4_purge_state_owners(struct nfs_server *, struct list_head *);
extern void nfs4_free_state_owners(struct list_head *head);
extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
extern void nfs4_put_open_state(struct nfs4_state *);
extern void nfs4_close_state(struct nfs4_state *, fmode_t);
Expand Down
5 changes: 4 additions & 1 deletion fs/nfs/nfs4client.c
Original file line number Diff line number Diff line change
Expand Up @@ -758,9 +758,12 @@ int nfs41_walk_client_list(struct nfs_client *new,

static void nfs4_destroy_server(struct nfs_server *server)
{
LIST_HEAD(freeme);

nfs_server_return_all_delegations(server);
unset_pnfs_layoutdriver(server);
nfs4_purge_state_owners(server);
nfs4_purge_state_owners(server, &freeme);
nfs4_free_state_owners(&freeme);
}

/*
Expand Down
109 changes: 62 additions & 47 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 @@ -1907,8 +1915,9 @@ _nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data)
if (data->o_res.delegation_type != 0)
nfs4_opendata_check_deleg(data, state);
update:
update_open_stateid(state, &data->o_res.stateid, NULL,
data->o_arg.fmode);
if (!update_open_stateid(state, &data->o_res.stateid,
NULL, data->o_arg.fmode))
return ERR_PTR(-EAGAIN);
refcount_inc(&state->count);

return state;
Expand Down Expand Up @@ -1973,8 +1982,11 @@ _nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)

if (data->o_res.delegation_type != 0)
nfs4_opendata_check_deleg(data, state);
update_open_stateid(state, &data->o_res.stateid, NULL,
data->o_arg.fmode);
if (!update_open_stateid(state, &data->o_res.stateid,
NULL, data->o_arg.fmode)) {
nfs4_put_open_state(state);
state = ERR_PTR(-EAGAIN);
}
out:
nfs_release_seqid(data->o_arg.seqid);
return state;
Expand Down Expand Up @@ -2074,13 +2086,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 +2162,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 All @@ -2171,18 +2179,17 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
case -ENOENT:
case -EAGAIN:
case -ESTALE:
case -ETIMEDOUT:
break;
case -NFS4ERR_BADSESSION:
case -NFS4ERR_BADSLOT:
case -NFS4ERR_BAD_HIGH_SLOT:
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
case -NFS4ERR_DEADSESSION:
set_bit(NFS_DELEGATED_STATE, &state->flags);
nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
return -EAGAIN;
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
set_bit(NFS_DELEGATED_STATE, &state->flags);
/* Don't recall a delegation if it was lost */
nfs4_schedule_lease_recovery(server->nfs_client);
return -EAGAIN;
Expand All @@ -2203,7 +2210,6 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
return -EAGAIN;
case -NFS4ERR_DELAY:
case -NFS4ERR_GRACE:
set_bit(NFS_DELEGATED_STATE, &state->flags);
ssleep(1);
return -EAGAIN;
case -ENOMEM:
Expand All @@ -2219,8 +2225,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
}

int nfs4_open_delegation_recall(struct nfs_open_context *ctx,
struct nfs4_state *state, const nfs4_stateid *stateid,
fmode_t type)
struct nfs4_state *state, const nfs4_stateid *stateid)
{
struct nfs_server *server = NFS_SERVER(state->inode);
struct nfs4_opendata *opendata;
Expand All @@ -2231,20 +2236,23 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx,
if (IS_ERR(opendata))
return PTR_ERR(opendata);
nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
nfs_state_clear_delegation(state);
switch (type & (FMODE_READ|FMODE_WRITE)) {
case FMODE_READ|FMODE_WRITE:
case FMODE_WRITE:
if (!test_bit(NFS_O_RDWR_STATE, &state->flags)) {
err = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE);
if (err)
break;
goto out;
}
if (!test_bit(NFS_O_WRONLY_STATE, &state->flags)) {
err = nfs4_open_recover_helper(opendata, FMODE_WRITE);
if (err)
break;
/* Fall through */
case FMODE_READ:
goto out;
}
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);
return nfs4_handle_delegation_recall_error(server, state, stateid, NULL, err);
}
Expand Down Expand Up @@ -2492,6 +2500,7 @@ static int nfs4_run_open_task(struct nfs4_opendata *data,
if (!ctx) {
nfs4_init_sequence(&o_arg->seq_args, &o_res->seq_res, 1, 1);
data->is_recover = true;
task_setup_data.flags |= RPC_TASK_TIMEOUT;
} else {
nfs4_init_sequence(&o_arg->seq_args, &o_res->seq_res, 1, 0);
pnfs_lgopen_prepare(data, ctx);
Expand Down Expand Up @@ -2698,6 +2707,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 @@ -2740,34 +2750,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 @@ -2777,9 +2782,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;

if (delegation->cred)
put_cred(cred);
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 @@ -2849,21 +2869,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 @@ -2876,7 +2887,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 Expand Up @@ -3201,7 +3216,7 @@ static int _nfs4_do_setattr(struct inode *inode,

if (nfs4_copy_delegation_stateid(inode, FMODE_WRITE, &arg->stateid, &delegation_cred)) {
/* Use that stateid */
} else if (ctx != NULL) {
} else if (ctx != NULL && ctx->state) {
struct nfs_lock_context *l_ctx;
if (!nfs4_valid_open_stateid(ctx->state))
return -EBADF;
Expand Down
Loading

0 comments on commit b678c56

Please sign in to comment.