mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 05:11:48 +00:00
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:
commit
27db64f65f
@ -883,9 +883,11 @@ 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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user