NFS client bugfixes for Linux 4.18

Hightlights include:
 
 Bugfixes:
 - Fix an rcu deadlock in nfs_delegation_find_inode()
 - Fix NFSv4 deadlocks due to not freeing the session slot in layoutget
 - Don't send layoutreturn if the layout is already invalid
 - Prevent duplicate XID allocation
 - flexfiles: Don't tie up all the rpciod threads in resends
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJbK9avAAoJEA4mA3inWBJclq0P/1VCigyDlsbtdby3z2leV84k
 l0asrGjOQndljJ7I21awAgEo8KvXOd66cMv6YT3+UqEW18aNblH4/ngjyId6hPVb
 RZDX7tsG16ZHEqfe9f9irNZo90mdvuSC4ChJ/CbbPesaK9pblE1d76b/qUVr4FUX
 Gj7JPAC5ckoiZXPFfRWfc+o7JnGvs5wkEuDTy+ig6v7BRdL64hdPG3veRNpmLIAZ
 uS/NCyRpO+nFN/ukmvuoI2ZQ3qfHubHBD+rHxr1UKT/ad7dywLmL2UBaYQ0Tl3bq
 /iSQHutgJYj/80VaRTqdlLt/m4ebUZg+9BEZgM5MvqBWkXcpXND51zxExVJN4cGW
 BOytqjLz0gP1OGb8w+Oow58K8l4XyEgHe2CtZ6Yz8Vwof7nchkpv7RSX50hJFIcA
 YlikeDyDzfOmTT6ove5kF31WQSa3Bk6OMEei0of6hWU3UVHyEdr9az73pm/CLSHE
 /R7w0osU3B9tmQD4btQeJ2DxP+syQwhelOYodyVTwOlkmmGg7DSV7fehnGyH8t8f
 I4Yp8f0raiYGbwonYVE2+zDO140VRETEfTE4XQZnn41fZUfB74oIqk77JtgvGMk2
 /+XFNCYBGadHdSBdxyJmhSjhoAWrhgChEIz1G12SiHrNvqIRY/uHhdCX1Ut5vlPf
 5aqyn/yXm6rUH7aNh/Gd
 =tz0M
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-4.18-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client bugfixes from Trond Myklebust:
 "Hightlights include:

   - fix an rcu deadlock in nfs_delegation_find_inode()

   - fix NFSv4 deadlocks due to not freeing the session slot in
     layoutget

   - don't send layoutreturn if the layout is already invalid

   - prevent duplicate XID allocation

   - flexfiles: Don't tie up all the rpciod threads in resends"

* tag 'nfs-for-4.18-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  pNFS/flexfiles: Process writeback resends from nfsiod context as well
  pNFS/flexfiles: Don't tie up all the rpciod threads in resends
  sunrpc: Prevent duplicate XID allocation
  pNFS: Don't send layoutreturn if the layout is already invalid
  pNFS: Always free the session slot on error in nfs4_layoutget_handle_exception
  NFS: Fix an rcu deadlock in nfs_delegation_find_inode()
This commit is contained in:
Linus Torvalds 2018-06-22 06:21:34 +09:00
commit 27db64f65f
6 changed files with 59 additions and 16 deletions

View File

@ -883,8 +883,10 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
res = nfs_delegation_find_inode_server(server, fhandle); res = nfs_delegation_find_inode_server(server, fhandle);
if (res != ERR_PTR(-ENOENT)) if (res != ERR_PTR(-ENOENT)) {
rcu_read_unlock();
return res; return res;
}
} }
rcu_read_unlock(); rcu_read_unlock();
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);

View File

@ -1243,17 +1243,18 @@ static int ff_layout_read_done_cb(struct rpc_task *task,
hdr->ds_clp, hdr->lseg, hdr->ds_clp, hdr->lseg,
hdr->pgio_mirror_idx); hdr->pgio_mirror_idx);
clear_bit(NFS_IOHDR_RESEND_PNFS, &hdr->flags);
clear_bit(NFS_IOHDR_RESEND_MDS, &hdr->flags);
switch (err) { switch (err) {
case -NFS4ERR_RESET_TO_PNFS: case -NFS4ERR_RESET_TO_PNFS:
if (ff_layout_choose_best_ds_for_read(hdr->lseg, if (ff_layout_choose_best_ds_for_read(hdr->lseg,
hdr->pgio_mirror_idx + 1, hdr->pgio_mirror_idx + 1,
&hdr->pgio_mirror_idx)) &hdr->pgio_mirror_idx))
goto out_eagain; goto out_eagain;
ff_layout_read_record_layoutstats_done(task, hdr); set_bit(NFS_IOHDR_RESEND_PNFS, &hdr->flags);
pnfs_read_resend_pnfs(hdr);
return task->tk_status; return task->tk_status;
case -NFS4ERR_RESET_TO_MDS: case -NFS4ERR_RESET_TO_MDS:
ff_layout_reset_read(hdr); set_bit(NFS_IOHDR_RESEND_MDS, &hdr->flags);
return task->tk_status; return task->tk_status;
case -EAGAIN: case -EAGAIN:
goto out_eagain; goto out_eagain;
@ -1403,6 +1404,10 @@ static void ff_layout_read_release(void *data)
struct nfs_pgio_header *hdr = data; struct nfs_pgio_header *hdr = data;
ff_layout_read_record_layoutstats_done(&hdr->task, hdr); ff_layout_read_record_layoutstats_done(&hdr->task, hdr);
if (test_bit(NFS_IOHDR_RESEND_PNFS, &hdr->flags))
pnfs_read_resend_pnfs(hdr);
else if (test_bit(NFS_IOHDR_RESEND_MDS, &hdr->flags))
ff_layout_reset_read(hdr);
pnfs_generic_rw_release(data); pnfs_generic_rw_release(data);
} }
@ -1423,12 +1428,14 @@ static int ff_layout_write_done_cb(struct rpc_task *task,
hdr->ds_clp, hdr->lseg, hdr->ds_clp, hdr->lseg,
hdr->pgio_mirror_idx); hdr->pgio_mirror_idx);
clear_bit(NFS_IOHDR_RESEND_PNFS, &hdr->flags);
clear_bit(NFS_IOHDR_RESEND_MDS, &hdr->flags);
switch (err) { switch (err) {
case -NFS4ERR_RESET_TO_PNFS: case -NFS4ERR_RESET_TO_PNFS:
ff_layout_reset_write(hdr, true); set_bit(NFS_IOHDR_RESEND_PNFS, &hdr->flags);
return task->tk_status; return task->tk_status;
case -NFS4ERR_RESET_TO_MDS: case -NFS4ERR_RESET_TO_MDS:
ff_layout_reset_write(hdr, false); set_bit(NFS_IOHDR_RESEND_MDS, &hdr->flags);
return task->tk_status; return task->tk_status;
case -EAGAIN: case -EAGAIN:
return -EAGAIN; return -EAGAIN;
@ -1575,6 +1582,10 @@ static void ff_layout_write_release(void *data)
struct nfs_pgio_header *hdr = data; struct nfs_pgio_header *hdr = data;
ff_layout_write_record_layoutstats_done(&hdr->task, hdr); ff_layout_write_record_layoutstats_done(&hdr->task, hdr);
if (test_bit(NFS_IOHDR_RESEND_PNFS, &hdr->flags))
ff_layout_reset_write(hdr, true);
else if (test_bit(NFS_IOHDR_RESEND_MDS, &hdr->flags))
ff_layout_reset_write(hdr, false);
pnfs_generic_rw_release(data); pnfs_generic_rw_release(data);
} }

View File

@ -3294,6 +3294,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
struct nfs4_closedata *calldata = data; struct nfs4_closedata *calldata = data;
struct nfs4_state *state = calldata->state; struct nfs4_state *state = calldata->state;
struct inode *inode = calldata->inode; struct inode *inode = calldata->inode;
struct pnfs_layout_hdr *lo;
bool is_rdonly, is_wronly, is_rdwr; bool is_rdonly, is_wronly, is_rdwr;
int call_close = 0; int call_close = 0;
@ -3337,6 +3338,12 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
goto out_wait; goto out_wait;
} }
lo = calldata->arg.lr_args ? calldata->arg.lr_args->layout : NULL;
if (lo && !pnfs_layout_is_valid(lo)) {
calldata->arg.lr_args = NULL;
calldata->res.lr_res = NULL;
}
if (calldata->arg.fmode == 0) if (calldata->arg.fmode == 0)
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE]; task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
@ -5972,12 +5979,19 @@ static void nfs4_delegreturn_release(void *calldata)
static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data) static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data)
{ {
struct nfs4_delegreturndata *d_data; struct nfs4_delegreturndata *d_data;
struct pnfs_layout_hdr *lo;
d_data = (struct nfs4_delegreturndata *)data; d_data = (struct nfs4_delegreturndata *)data;
if (!d_data->lr.roc && nfs4_wait_on_layoutreturn(d_data->inode, task)) if (!d_data->lr.roc && nfs4_wait_on_layoutreturn(d_data->inode, task))
return; return;
lo = d_data->args.lr_args ? d_data->args.lr_args->layout : NULL;
if (lo && !pnfs_layout_is_valid(lo)) {
d_data->args.lr_args = NULL;
d_data->res.lr_res = NULL;
}
nfs4_setup_sequence(d_data->res.server->nfs_client, nfs4_setup_sequence(d_data->res.server->nfs_client,
&d_data->args.seq_args, &d_data->args.seq_args,
&d_data->res.seq_res, &d_data->res.seq_res,
@ -8650,6 +8664,8 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
dprintk("--> %s tk_status => %d\n", __func__, -task->tk_status); dprintk("--> %s tk_status => %d\n", __func__, -task->tk_status);
nfs4_sequence_free_slot(&lgp->res.seq_res);
switch (nfs4err) { switch (nfs4err) {
case 0: case 0:
goto out; goto out;
@ -8714,7 +8730,6 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
goto out; goto out;
} }
nfs4_sequence_free_slot(&lgp->res.seq_res);
err = nfs4_handle_exception(server, nfs4err, exception); err = nfs4_handle_exception(server, nfs4err, exception);
if (!status) { if (!status) {
if (exception->retry) if (exception->retry)
@ -8786,20 +8801,22 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout)
if (IS_ERR(task)) if (IS_ERR(task))
return ERR_CAST(task); return ERR_CAST(task);
status = rpc_wait_for_completion_task(task); status = rpc_wait_for_completion_task(task);
if (status == 0) { if (status != 0)
goto out;
/* if layoutp->len is 0, nfs4_layoutget_prepare called rpc_exit */
if (task->tk_status < 0 || lgp->res.layoutp->len == 0) {
status = nfs4_layoutget_handle_exception(task, lgp, &exception); status = nfs4_layoutget_handle_exception(task, lgp, &exception);
*timeout = exception.timeout; *timeout = exception.timeout;
} } else
lseg = pnfs_layout_process(lgp);
out:
trace_nfs4_layoutget(lgp->args.ctx, trace_nfs4_layoutget(lgp->args.ctx,
&lgp->args.range, &lgp->args.range,
&lgp->res.range, &lgp->res.range,
&lgp->res.stateid, &lgp->res.stateid,
status); status);
/* if layoutp->len is 0, nfs4_layoutget_prepare called rpc_exit */
if (status == 0 && lgp->res.layoutp->len)
lseg = pnfs_layout_process(lgp);
rpc_put_task(task); rpc_put_task(task);
dprintk("<-- %s status=%d\n", __func__, status); dprintk("<-- %s status=%d\n", __func__, status);
if (status) if (status)
@ -8817,6 +8834,8 @@ nfs4_layoutreturn_prepare(struct rpc_task *task, void *calldata)
&lrp->args.seq_args, &lrp->args.seq_args,
&lrp->res.seq_res, &lrp->res.seq_res,
task); task);
if (!pnfs_layout_is_valid(lrp->args.layout))
rpc_exit(task, 0);
} }
static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata) static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)

View File

@ -801,6 +801,11 @@ static inline void nfs4_lgopen_release(struct nfs4_layoutget *lgp)
{ {
} }
static inline bool pnfs_layout_is_valid(const struct pnfs_layout_hdr *lo)
{
return false;
}
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
#if IS_ENABLED(CONFIG_NFS_V4_2) #if IS_ENABLED(CONFIG_NFS_V4_2)

View File

@ -1438,6 +1438,8 @@ enum {
NFS_IOHDR_EOF, NFS_IOHDR_EOF,
NFS_IOHDR_REDO, NFS_IOHDR_REDO,
NFS_IOHDR_STAT, NFS_IOHDR_STAT,
NFS_IOHDR_RESEND_PNFS,
NFS_IOHDR_RESEND_MDS,
}; };
struct nfs_io_completion; struct nfs_io_completion;

View File

@ -987,8 +987,6 @@ bool xprt_prepare_transmit(struct rpc_task *task)
task->tk_status = -EAGAIN; task->tk_status = -EAGAIN;
goto out_unlock; goto out_unlock;
} }
if (!bc_prealloc(req) && !req->rq_xmit_bytes_sent)
req->rq_xid = xprt_alloc_xid(xprt);
ret = true; ret = true;
out_unlock: out_unlock:
spin_unlock_bh(&xprt->transport_lock); spin_unlock_bh(&xprt->transport_lock);
@ -1298,7 +1296,12 @@ void xprt_retry_reserve(struct rpc_task *task)
static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt) static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt)
{ {
return (__force __be32)xprt->xid++; __be32 xid;
spin_lock(&xprt->reserve_lock);
xid = (__force __be32)xprt->xid++;
spin_unlock(&xprt->reserve_lock);
return xid;
} }
static inline void xprt_init_xid(struct rpc_xprt *xprt) static inline void xprt_init_xid(struct rpc_xprt *xprt)
@ -1316,6 +1319,7 @@ void xprt_request_init(struct rpc_task *task)
req->rq_task = task; req->rq_task = task;
req->rq_xprt = xprt; req->rq_xprt = xprt;
req->rq_buffer = NULL; req->rq_buffer = NULL;
req->rq_xid = xprt_alloc_xid(xprt);
req->rq_connect_cookie = xprt->connect_cookie - 1; req->rq_connect_cookie = xprt->connect_cookie - 1;
req->rq_bytes_sent = 0; req->rq_bytes_sent = 0;
req->rq_snd_buf.len = 0; req->rq_snd_buf.len = 0;