diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index ce3bb5f7a60a..9492d6bc1d1a 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -1653,6 +1653,15 @@ static void ff_layout_io_track_ds_error(struct pnfs_layout_segment *lseg, dprintk("%s: err %d op %d status %u\n", __func__, err, opnum, status); } +static bool ff_layout_io_state_lost(const struct nfs_pgio_header *hdr) +{ + struct nfs_open_context *ctx = hdr->args.context; + + return test_bit(NFS_CONTEXT_BAD, &ctx->flags) || + !nfs4_valid_open_stateid(ctx->state) || + nfs4_lock_context_lost(ctx->state, hdr->args.lock_context); +} + /* NFS_PROTO call done callback routines */ static int ff_layout_read_done_cb(struct rpc_task *task, struct nfs_pgio_header *hdr) @@ -1664,6 +1673,12 @@ static int ff_layout_read_done_cb(struct rpc_task *task, hdr->args.offset); int err; + if (task->tk_status < 0 && !RPC_WAS_SENT(task) && + ff_layout_io_state_lost(hdr)) { + task->tk_status = -EIO; + return 0; + } + if (task->tk_status < 0) { ff_layout_io_track_ds_error(hdr->lseg, hdr->ds_dev, hdr->pgio_mirror_idx, dss_id, @@ -1779,7 +1794,7 @@ static void ff_layout_read_record_layoutstats_done(struct rpc_task *task, static int ff_layout_read_prepare_common(struct rpc_task *task, struct nfs_pgio_header *hdr) { - if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) { + if (unlikely(ff_layout_io_state_lost(hdr))) { rpc_exit(task, -EIO); return -EIO; } @@ -1868,6 +1883,12 @@ static int ff_layout_write_done_cb(struct rpc_task *task, loff_t end_offs = 0; int err; + if (task->tk_status < 0 && !RPC_WAS_SENT(task) && + ff_layout_io_state_lost(hdr)) { + task->tk_status = -EIO; + return 0; + } + if (task->tk_status < 0) { ff_layout_io_track_ds_error(hdr->lseg, hdr->ds_dev, hdr->pgio_mirror_idx, dss_id, @@ -2004,7 +2025,7 @@ static void ff_layout_write_record_layoutstats_done(struct rpc_task *task, static int ff_layout_write_prepare_common(struct rpc_task *task, struct nfs_pgio_header *hdr) { - if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) { + if (unlikely(ff_layout_io_state_lost(hdr))) { rpc_exit(task, -EIO); return -EIO; } diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 76dae699d4d7..2ad2efc43bb9 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -529,6 +529,8 @@ extern int nfs4_select_rw_stateid(struct nfs4_state *, fmode_t, const struct cred **); extern bool nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state); +extern bool nfs4_lock_context_lost(struct nfs4_state *state, + const struct nfs_lock_context *l_ctx); extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask); extern int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index b5d6daa2c416..8cd178f1da4c 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -812,10 +812,13 @@ void nfs4_close_sync(struct nfs4_state *state, fmode_t fmode) * be made. Failing that, a "consistent" choice is preferable. The * consistent choice we make is to prefer the first owner, that of a * Posix lock. + * + * Called with state->state_lock held. No reference is taken: a lock + * state is freed only after it is unlinked under state->state_lock. */ static struct nfs4_lock_state * -__nfs4_find_lock_state(struct nfs4_state *state, - fl_owner_t owner, fl_owner_t owner2) +__nfs4_lookup_lock_state(struct nfs4_state *state, + fl_owner_t owner, fl_owner_t owner2) { struct nfs4_lock_state *pos, *ret = NULL; list_for_each_entry(pos, &state->lock_states, ls_locks) { @@ -826,6 +829,16 @@ __nfs4_find_lock_state(struct nfs4_state *state, if (pos->ls_owner == owner2) ret = pos; } + return ret; +} + +static struct nfs4_lock_state * +__nfs4_find_lock_state(struct nfs4_state *state, + fl_owner_t owner, fl_owner_t owner2) +{ + struct nfs4_lock_state *ret; + + ret = __nfs4_lookup_lock_state(state, owner, owner2); if (ret) refcount_inc(&ret->ls_count); return ret; @@ -968,7 +981,7 @@ static int nfs4_copy_lock_stateid(nfs4_stateid *dst, fl_flock_owner = l_ctx->open_context->flock_owner; spin_lock(&state->state_lock); - lsp = __nfs4_find_lock_state(state, owner, fl_flock_owner); + lsp = __nfs4_lookup_lock_state(state, owner, fl_flock_owner); if (lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags)) ret = -EIO; else if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) { @@ -976,11 +989,28 @@ static int nfs4_copy_lock_stateid(nfs4_stateid *dst, ret = 0; } spin_unlock(&state->state_lock); - nfs4_put_lock_state(lsp); out: return ret; } +bool nfs4_lock_context_lost(struct nfs4_state *state, + const struct nfs_lock_context *l_ctx) +{ + struct nfs4_lock_state *lsp; + bool ret; + + if (!test_bit(LK_STATE_IN_USE, &state->flags)) + return false; + + spin_lock(&state->state_lock); + lsp = __nfs4_lookup_lock_state(state, l_ctx->lockowner, + l_ctx->open_context->flock_owner); + ret = lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags); + spin_unlock(&state->state_lock); + return ret; +} +EXPORT_SYMBOL_GPL(nfs4_lock_context_lost); + bool nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state) { bool ret;