Skip to content

Commit

Permalink
Merge tag 'nfs-for-4.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 bugfixes from Trond Myklebust:
 "Highlights include:

  Stable patches:
   - fix v4.2 SEEK on files over 2 gigs
   - Fix a layout segment reference leak when pNFS I/O falls back to inband I/O.
   - Fix recovery of recalled read delegations

  Bugfixes:
   - Fix a case where NFSv4 fails to send CLOSE after a server reboot
   - Fix sunrpc to wait for connections to complete before retrying
   - Fix sunrpc races between transport connect/disconnect and shutdown
   - Fix an infinite loop when layoutget fail with BAD_STATEID
   - nfs/filelayout: Fix NULL reference caused by double freeing of fh_array
   - Fix a bogus WARN_ON_ONCE() in O_DIRECT when layout commit_through_mds is set
   - Fix layoutreturn/close ordering issues"

* tag 'nfs-for-4.3-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS41: make close wait for layoutreturn
  NFS: Skip checking ds_cinfo.buckets when lseg's commit_through_mds is set
  NFSv4.x/pnfs: Don't try to recover stateids twice in layoutget
  NFSv4: Recovery of recalled read delegations is broken
  NFS: Fix an infinite loop when layoutget fail with BAD_STATEID
  NFS: Do cleanup before resetting pageio read/write to mds
  SUNRPC: xs_sock_mark_closed() does not need to trigger socket autoclose
  SUNRPC: Lock the transport layer on shutdown
  nfs/filelayout: Fix NULL reference caused by double freeing of fh_array
  SUNRPC: Ensure that we wait for connections to complete before retrying
  SUNRPC: drop null test before destroy functions
  nfs: fix v4.2 SEEK on files over 2 gigs
  SUNRPC: Fix races between socket connection and destroy code
  nfs: fix pg_test page count calculation
  Failing to send a CLOSE if file is opened WRONLY and server reboots on a 4.x mount
  • Loading branch information
torvalds committed Sep 25, 2015
2 parents ddff42e + 500d701 commit 101688f
Show file tree
Hide file tree
Showing 16 changed files with 172 additions and 95 deletions.
8 changes: 5 additions & 3 deletions fs/nfs/delegation.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
return status;
}

static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
static int nfs_delegation_claim_opens(struct inode *inode,
const nfs4_stateid *stateid, fmode_t type)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *ctx;
Expand All @@ -140,7 +141,7 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s
/* 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);
err = nfs4_open_delegation_recall(ctx, state, stateid, type);
if (!err)
err = nfs_delegation_claim_locks(ctx, state, stateid);
if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
Expand Down Expand Up @@ -411,7 +412,8 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
do {
if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
break;
err = nfs_delegation_claim_opens(inode, &delegation->stateid);
err = nfs_delegation_claim_opens(inode, &delegation->stateid,
delegation->type);
if (!issync || err != -EAGAIN)
break;
/*
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/delegation.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp);

/* NFSv4 delegation-related procedures */
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_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);
int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid, fmode_t type);
int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid);
bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_t flags);

Expand Down
7 changes: 5 additions & 2 deletions fs/nfs/direct.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,11 @@ nfs_direct_select_verf(struct nfs_direct_req *dreq,
struct nfs_writeverf *verfp = &dreq->verf;

#ifdef CONFIG_NFS_V4_1
if (ds_clp) {
/* pNFS is in use, use the DS verf */
/*
* pNFS is in use, use the DS verf except commit_through_mds is set
* for layout segment where nbuckets is zero.
*/
if (ds_clp && dreq->ds_cinfo.nbuckets > 0) {
if (commit_idx >= 0 && commit_idx < dreq->ds_cinfo.nbuckets)
verfp = &dreq->ds_cinfo.buckets[commit_idx].direct_verf;
else
Expand Down
31 changes: 12 additions & 19 deletions fs/nfs/filelayout/filelayout.c
Original file line number Diff line number Diff line change
Expand Up @@ -629,23 +629,18 @@ filelayout_check_layout(struct pnfs_layout_hdr *lo,
goto out;
}

static void filelayout_free_fh_array(struct nfs4_filelayout_segment *fl)
static void _filelayout_free_lseg(struct nfs4_filelayout_segment *fl)
{
int i;

for (i = 0; i < fl->num_fh; i++) {
if (!fl->fh_array[i])
break;
kfree(fl->fh_array[i]);
if (fl->fh_array) {
for (i = 0; i < fl->num_fh; i++) {
if (!fl->fh_array[i])
break;
kfree(fl->fh_array[i]);
}
kfree(fl->fh_array);
}
kfree(fl->fh_array);
fl->fh_array = NULL;
}

static void
_filelayout_free_lseg(struct nfs4_filelayout_segment *fl)
{
filelayout_free_fh_array(fl);
kfree(fl);
}

Expand Down Expand Up @@ -716,21 +711,21 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo,
/* Do we want to use a mempool here? */
fl->fh_array[i] = kmalloc(sizeof(struct nfs_fh), gfp_flags);
if (!fl->fh_array[i])
goto out_err_free;
goto out_err;

p = xdr_inline_decode(&stream, 4);
if (unlikely(!p))
goto out_err_free;
goto out_err;
fl->fh_array[i]->size = be32_to_cpup(p++);
if (sizeof(struct nfs_fh) < fl->fh_array[i]->size) {
printk(KERN_ERR "NFS: Too big fh %d received %d\n",
i, fl->fh_array[i]->size);
goto out_err_free;
goto out_err;
}

p = xdr_inline_decode(&stream, fl->fh_array[i]->size);
if (unlikely(!p))
goto out_err_free;
goto out_err;
memcpy(fl->fh_array[i]->data, p, fl->fh_array[i]->size);
dprintk("DEBUG: %s: fh len %d\n", __func__,
fl->fh_array[i]->size);
Expand All @@ -739,8 +734,6 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo,
__free_page(scratch);
return 0;

out_err_free:
filelayout_free_fh_array(fl);
out_err:
__free_page(scratch);
return -EIO;
Expand Down
4 changes: 3 additions & 1 deletion fs/nfs/nfs42proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,12 @@ loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
{
struct nfs_server *server = NFS_SERVER(file_inode(filep));
struct nfs4_exception exception = { };
int err;
loff_t err;

do {
err = _nfs42_proc_llseek(filep, offset, whence);
if (err >= 0)
break;
if (err == -ENOTSUPP)
return -EOPNOTSUPP;
err = nfs4_handle_exception(server, err, &exception);
Expand Down
127 changes: 82 additions & 45 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,21 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
return ret;
}

static bool nfs4_mode_match_open_stateid(struct nfs4_state *state,
fmode_t fmode)
{
switch(fmode & (FMODE_READ|FMODE_WRITE)) {
case FMODE_READ|FMODE_WRITE:
return state->n_rdwr != 0;
case FMODE_WRITE:
return state->n_wronly != 0;
case FMODE_READ:
return state->n_rdonly != 0;
}
WARN_ON_ONCE(1);
return false;
}

static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)
{
int ret = 0;
Expand Down Expand Up @@ -1571,17 +1586,13 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
return opendata;
}

static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res)
static int nfs4_open_recover_helper(struct nfs4_opendata *opendata,
fmode_t fmode)
{
struct nfs4_state *newstate;
int ret;

if ((opendata->o_arg.claim == NFS4_OPEN_CLAIM_DELEGATE_CUR ||
opendata->o_arg.claim == NFS4_OPEN_CLAIM_DELEG_CUR_FH) &&
(opendata->o_arg.u.delegation_type & fmode) != fmode)
/* This mode can't have been delegated, so we must have
* a valid open_stateid to cover it - not need to reclaim.
*/
if (!nfs4_mode_match_open_stateid(opendata->state, fmode))
return 0;
opendata->o_arg.open_flags = 0;
opendata->o_arg.fmode = fmode;
Expand All @@ -1597,14 +1608,14 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmod
newstate = nfs4_opendata_to_nfs4_state(opendata);
if (IS_ERR(newstate))
return PTR_ERR(newstate);
if (newstate != opendata->state)
ret = -ESTALE;
nfs4_close_state(newstate, fmode);
*res = newstate;
return 0;
return ret;
}

static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state)
{
struct nfs4_state *newstate;
int ret;

/* Don't trigger recovery in nfs_test_and_clear_all_open_stateid */
Expand All @@ -1615,27 +1626,15 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
clear_bit(NFS_DELEGATED_STATE, &state->flags);
clear_bit(NFS_OPEN_STATE, &state->flags);
smp_rmb();
if (state->n_rdwr != 0) {
ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate);
if (ret != 0)
return ret;
if (newstate != state)
return -ESTALE;
}
if (state->n_wronly != 0) {
ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate);
if (ret != 0)
return ret;
if (newstate != state)
return -ESTALE;
}
if (state->n_rdonly != 0) {
ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate);
if (ret != 0)
return ret;
if (newstate != state)
return -ESTALE;
}
ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE);
if (ret != 0)
return ret;
ret = nfs4_open_recover_helper(opendata, FMODE_WRITE);
if (ret != 0)
return ret;
ret = nfs4_open_recover_helper(opendata, FMODE_READ);
if (ret != 0)
return ret;
/*
* We may have performed cached opens for all three recoveries.
* Check if we need to update the current stateid.
Expand Down Expand Up @@ -1759,18 +1758,32 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
return err;
}

int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
int nfs4_open_delegation_recall(struct nfs_open_context *ctx,
struct nfs4_state *state, const nfs4_stateid *stateid,
fmode_t type)
{
struct nfs_server *server = NFS_SERVER(state->inode);
struct nfs4_opendata *opendata;
int err;
int err = 0;

opendata = nfs4_open_recoverdata_alloc(ctx, state,
NFS4_OPEN_CLAIM_DELEG_CUR_FH);
if (IS_ERR(opendata))
return PTR_ERR(opendata);
nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
err = nfs4_open_recover(opendata, state);
clear_bit(NFS_DELEGATED_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);
if (err)
break;
err = nfs4_open_recover_helper(opendata, FMODE_WRITE);
if (err)
break;
case FMODE_READ:
err = nfs4_open_recover_helper(opendata, FMODE_READ);
}
nfs4_opendata_put(opendata);
return nfs4_handle_delegation_recall_error(server, state, stateid, err);
}
Expand Down Expand Up @@ -2645,6 +2658,15 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
return err;
}

static bool
nfs4_wait_on_layoutreturn(struct inode *inode, struct rpc_task *task)
{
if (inode == NULL || !nfs_have_layout(inode))
return false;

return pnfs_wait_on_layoutreturn(inode, task);
}

struct nfs4_closedata {
struct inode *inode;
struct nfs4_state *state;
Expand Down Expand Up @@ -2763,6 +2785,11 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
goto out_no_action;
}

if (nfs4_wait_on_layoutreturn(inode, task)) {
nfs_release_seqid(calldata->arg.seqid);
goto out_wait;
}

if (calldata->arg.fmode == 0)
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
if (calldata->roc)
Expand Down Expand Up @@ -5308,6 +5335,9 @@ static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data)

d_data = (struct nfs4_delegreturndata *)data;

if (nfs4_wait_on_layoutreturn(d_data->inode, task))
return;

if (d_data->roc)
pnfs_roc_get_barrier(d_data->inode, &d_data->roc_barrier);

Expand Down Expand Up @@ -7800,39 +7830,46 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
dprintk("%s: NFS4ERR_RECALLCONFLICT waiting %lu\n",
__func__, delay);
rpc_delay(task, delay);
task->tk_status = 0;
rpc_restart_call_prepare(task);
goto out; /* Do not call nfs4_async_handle_error() */
/* Do not call nfs4_async_handle_error() */
goto out_restart;
}
break;
case -NFS4ERR_EXPIRED:
case -NFS4ERR_BAD_STATEID:
spin_lock(&inode->i_lock);
lo = NFS_I(inode)->layout;
if (!lo || list_empty(&lo->plh_segs)) {
if (nfs4_stateid_match(&lgp->args.stateid,
&lgp->args.ctx->state->stateid)) {
spin_unlock(&inode->i_lock);
/* If the open stateid was bad, then recover it. */
state = lgp->args.ctx->state;
} else {
break;
}
lo = NFS_I(inode)->layout;
if (lo && nfs4_stateid_match(&lgp->args.stateid,
&lo->plh_stateid)) {
LIST_HEAD(head);

/*
* Mark the bad layout state as invalid, then retry
* with the current stateid.
*/
set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
pnfs_mark_matching_lsegs_invalid(lo, &head, NULL);
spin_unlock(&inode->i_lock);
pnfs_free_lseg_list(&head);

task->tk_status = 0;
rpc_restart_call_prepare(task);
}
} else
spin_unlock(&inode->i_lock);
goto out_restart;
}
if (nfs4_async_handle_error(task, server, state, NULL) == -EAGAIN)
rpc_restart_call_prepare(task);
goto out_restart;
out:
dprintk("<-- %s\n", __func__);
return;
out_restart:
task->tk_status = 0;
rpc_restart_call_prepare(task);
return;
out_overflow:
task->tk_status = -EOVERFLOW;
goto out;
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/nfs4state.c
Original file line number Diff line number Diff line change
Expand Up @@ -1481,7 +1481,7 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
spin_unlock(&state->state_lock);
}
nfs4_put_open_state(state);
clear_bit(NFS4CLNT_RECLAIM_NOGRACE,
clear_bit(NFS_STATE_RECLAIM_NOGRACE,
&state->flags);
spin_lock(&sp->so_lock);
goto restart;
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/pagelist.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ size_t nfs_generic_pg_test(struct nfs_pageio_descriptor *desc,
* for it without upsetting the slab allocator.
*/
if (((mirror->pg_count + req->wb_bytes) >> PAGE_SHIFT) *
sizeof(struct page) > PAGE_SIZE)
sizeof(struct page *) > PAGE_SIZE)
return 0;

return min(mirror->pg_bsize - mirror->pg_count, (size_t)req->wb_bytes);
Expand Down
Loading

0 comments on commit 101688f

Please sign in to comment.