NFS client bugfixes for Linux 5.7
Highlights include: Stable fixes - fix handling of backchannel binding in BIND_CONN_TO_SESSION Bugfixes - Fix a credential use-after-free issue in pnfs_roc() - Fix potential posix_acl refcnt leak in nfs3_set_acl - defer slow parts of rpc_free_client() to a workqueue - Fix an Oopsable race in __nfs_list_for_each_server() - Fix trace point use-after-free race - Regression: the RDMA client no longer responds to server disconnect requests - Fix return values of xdr_stream_encode_item_{present, absent} - _pnfs_return_layout() must always wait for layoutreturn completion Cleanups - Remove unreachable error conditions -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEESQctxSBg8JpV8KqEZwvnipYKAPIFAl6tczsACgkQZwvnipYK APKHWg//QGx2Tolj5dh2jBHa47A5/SYnJxCZAA0/fWdwRtFkW3HyyGne1jU86do2 SMAVpBpri1WJPt5d3DH66gu4l4UxG1h84s7QP4lGfSa85EmtLh+LoZQCZRqYoDOo JAMzWctELu1TUpaa1N5Dhg/qMtMy6ulRMWgzTLqB9a/pQa3onugTK6W7xiut2prj PBfFq7N9XXmPboSeGV9bR4L8XKSbTCLEt3U1F2zAGU7UUINvDfpjEXq7BHYCewKL ObPW6EWZksyna16H8i/xGWoKgE4JFVjMwQAP7UdDBi+FW9RI6UpTBoR6z9N748j0 jEocDbI21wgnwmtrVTbzsYm6ttHl4D4egoNxn7m5zjxTU4Ba/RQG2aaHUGFOYpJj 1FI1f6V1Y5v4mJajdsEH+pGW/4vK/4YMR+7YHJ/hYU/WiXjLf7onIIifdWt4SQdo lvZbGcx6IAHYUA4lI7hkcvrK4bbqAnPLFq28nlUWEID5q5D+nA1ZR9iN0FToviDy FYyhQzyfD1kt98SV1DjWUqvDDd6IB64iDZTXGmtWvj6c2nbezGiFffvtzUL5LFxY QfI8lkpmUyt1EiWlZWhtOh4zsiM5yMZkJB/3RJv3RMmswizSSAHdgCKWhdLpX0bl TG1L8yEmcTc5ANS37EhlpcBNbfYw7oIF/OXuReTSRoMQl5hxjfY= =w0zk -----END PGP SIGNATURE----- Merge tag 'nfs-for-5.7-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs Pull NFS client bugfixes from Trond Myklebust: "Highlights include: Stable fixes: - fix handling of backchannel binding in BIND_CONN_TO_SESSION Bugfixes: - Fix a credential use-after-free issue in pnfs_roc() - Fix potential posix_acl refcnt leak in nfs3_set_acl - defer slow parts of rpc_free_client() to a workqueue - Fix an Oopsable race in __nfs_list_for_each_server() - Fix trace point use-after-free race - Regression: the RDMA client no longer responds to server disconnect requests - Fix return values of xdr_stream_encode_item_{present, absent} - _pnfs_return_layout() must always wait for layoutreturn completion Cleanups: - Remove unreachable error conditions" * tag 'nfs-for-5.7-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: NFS: Fix a race in __nfs_list_for_each_server() NFSv4.1: fix handling of backchannel binding in BIND_CONN_TO_SESSION SUNRPC: defer slow parts of rpc_free_client() to a workqueue. NFSv4: Remove unreachable error condition due to rpc_run_task() SUNRPC: Remove unreachable error condition xprtrdma: Fix use of xdr_stream_encode_item_{present, absent} xprtrdma: Fix trace point use-after-free race xprtrdma: Restore wake-up-all to rpcrdma_cm_event_handler() nfs: Fix potential posix_acl refcnt leak in nfs3_set_acl NFS/pnfs: Fix a credential use-after-free issue in pnfs_roc() NFS/pnfs: Ensure that _pnfs_return_layout() waits for layoutreturn completion
This commit is contained in:
commit
29a47f456d
|
@ -253,37 +253,45 @@ int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
|
|||
|
||||
int nfs3_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||||
{
|
||||
struct posix_acl *alloc = NULL, *dfacl = NULL;
|
||||
struct posix_acl *orig = acl, *dfacl = NULL, *alloc;
|
||||
int status;
|
||||
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
switch(type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
alloc = dfacl = get_acl(inode, ACL_TYPE_DEFAULT);
|
||||
alloc = get_acl(inode, ACL_TYPE_DEFAULT);
|
||||
if (IS_ERR(alloc))
|
||||
goto fail;
|
||||
dfacl = alloc;
|
||||
break;
|
||||
|
||||
case ACL_TYPE_DEFAULT:
|
||||
dfacl = acl;
|
||||
alloc = acl = get_acl(inode, ACL_TYPE_ACCESS);
|
||||
alloc = get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(alloc))
|
||||
goto fail;
|
||||
dfacl = acl;
|
||||
acl = alloc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (acl == NULL) {
|
||||
alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
|
||||
alloc = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
|
||||
if (IS_ERR(alloc))
|
||||
goto fail;
|
||||
acl = alloc;
|
||||
}
|
||||
status = __nfs3_proc_setacls(inode, acl, dfacl);
|
||||
posix_acl_release(alloc);
|
||||
out:
|
||||
if (acl != orig)
|
||||
posix_acl_release(acl);
|
||||
if (dfacl != orig)
|
||||
posix_acl_release(dfacl);
|
||||
return status;
|
||||
|
||||
fail:
|
||||
return PTR_ERR(alloc);
|
||||
status = PTR_ERR(alloc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
const struct xattr_handler *nfs3_xattr_handlers[] = {
|
||||
|
|
|
@ -7891,6 +7891,7 @@ static void
|
|||
nfs4_bind_one_conn_to_session_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs41_bind_conn_to_session_args *args = task->tk_msg.rpc_argp;
|
||||
struct nfs41_bind_conn_to_session_res *res = task->tk_msg.rpc_resp;
|
||||
struct nfs_client *clp = args->client;
|
||||
|
||||
switch (task->tk_status) {
|
||||
|
@ -7899,6 +7900,12 @@ nfs4_bind_one_conn_to_session_done(struct rpc_task *task, void *calldata)
|
|||
nfs4_schedule_session_recovery(clp->cl_session,
|
||||
task->tk_status);
|
||||
}
|
||||
if (args->dir == NFS4_CDFC4_FORE_OR_BOTH &&
|
||||
res->dir != NFS4_CDFS4_BOTH) {
|
||||
rpc_task_close_connection(task);
|
||||
if (args->retries++ < MAX_BIND_CONN_TO_SESSION_RETRIES)
|
||||
rpc_restart_call(task);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nfs4_bind_one_conn_to_session_ops = {
|
||||
|
@ -7921,6 +7928,7 @@ int nfs4_proc_bind_one_conn_to_session(struct rpc_clnt *clnt,
|
|||
struct nfs41_bind_conn_to_session_args args = {
|
||||
.client = clp,
|
||||
.dir = NFS4_CDFC4_FORE_OR_BOTH,
|
||||
.retries = 0,
|
||||
};
|
||||
struct nfs41_bind_conn_to_session_res res;
|
||||
struct rpc_message msg = {
|
||||
|
@ -9191,8 +9199,7 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout)
|
|||
nfs4_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0, 0);
|
||||
|
||||
task = rpc_run_task(&task_setup_data);
|
||||
if (IS_ERR(task))
|
||||
return ERR_CAST(task);
|
||||
|
||||
status = rpc_wait_for_completion_task(task);
|
||||
if (status != 0)
|
||||
goto out;
|
||||
|
|
|
@ -1332,13 +1332,15 @@ _pnfs_return_layout(struct inode *ino)
|
|||
!valid_layout) {
|
||||
spin_unlock(&ino->i_lock);
|
||||
dprintk("NFS: %s no layout segments to return\n", __func__);
|
||||
goto out_put_layout_hdr;
|
||||
goto out_wait_layoutreturn;
|
||||
}
|
||||
|
||||
send = pnfs_prepare_layoutreturn(lo, &stateid, &cred, NULL);
|
||||
spin_unlock(&ino->i_lock);
|
||||
if (send)
|
||||
status = pnfs_send_layoutreturn(lo, &stateid, &cred, IOMODE_ANY, true);
|
||||
out_wait_layoutreturn:
|
||||
wait_on_bit(&lo->plh_flags, NFS_LAYOUT_RETURN, TASK_UNINTERRUPTIBLE);
|
||||
out_put_layout_hdr:
|
||||
pnfs_free_lseg_list(&tmp_list);
|
||||
pnfs_put_layout_hdr(lo);
|
||||
|
@ -1456,18 +1458,15 @@ retry:
|
|||
/* lo ref dropped in pnfs_roc_release() */
|
||||
layoutreturn = pnfs_prepare_layoutreturn(lo, &stateid, &lc_cred, &iomode);
|
||||
/* If the creds don't match, we can't compound the layoutreturn */
|
||||
if (!layoutreturn)
|
||||
if (!layoutreturn || cred_fscmp(cred, lc_cred) != 0)
|
||||
goto out_noroc;
|
||||
if (cred_fscmp(cred, lc_cred) != 0)
|
||||
goto out_noroc_put_cred;
|
||||
|
||||
roc = layoutreturn;
|
||||
pnfs_init_layoutreturn_args(args, lo, &stateid, iomode);
|
||||
res->lrs_present = 0;
|
||||
layoutreturn = false;
|
||||
|
||||
out_noroc_put_cred:
|
||||
put_cred(lc_cred);
|
||||
|
||||
out_noroc:
|
||||
spin_unlock(&ino->i_lock);
|
||||
rcu_read_unlock();
|
||||
|
|
|
@ -185,7 +185,7 @@ static int __nfs_list_for_each_server(struct list_head *head,
|
|||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(server, head, client_link) {
|
||||
if (!nfs_sb_active(server->super))
|
||||
if (!(server->super && nfs_sb_active(server->super)))
|
||||
continue;
|
||||
rcu_read_unlock();
|
||||
if (last)
|
||||
|
|
|
@ -1317,11 +1317,13 @@ struct nfs41_impl_id {
|
|||
struct nfstime4 date;
|
||||
};
|
||||
|
||||
#define MAX_BIND_CONN_TO_SESSION_RETRIES 3
|
||||
struct nfs41_bind_conn_to_session_args {
|
||||
struct nfs_client *client;
|
||||
struct nfs4_sessionid sessionid;
|
||||
u32 dir;
|
||||
bool use_conn_in_rdma_mode;
|
||||
int retries;
|
||||
};
|
||||
|
||||
struct nfs41_bind_conn_to_session_res {
|
||||
|
|
|
@ -71,7 +71,13 @@ struct rpc_clnt {
|
|||
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
|
||||
struct dentry *cl_debugfs; /* debugfs directory */
|
||||
#endif
|
||||
struct rpc_xprt_iter cl_xpi;
|
||||
/* cl_work is only needed after cl_xpi is no longer used,
|
||||
* and that are of similar size
|
||||
*/
|
||||
union {
|
||||
struct rpc_xprt_iter cl_xpi;
|
||||
struct work_struct cl_work;
|
||||
};
|
||||
const struct cred *cl_cred;
|
||||
};
|
||||
|
||||
|
@ -236,4 +242,9 @@ static inline int rpc_reply_expected(struct rpc_task *task)
|
|||
(task->tk_msg.rpc_proc->p_decode != NULL);
|
||||
}
|
||||
|
||||
static inline void rpc_task_close_connection(struct rpc_task *task)
|
||||
{
|
||||
if (task->tk_xprt)
|
||||
xprt_force_disconnect(task->tk_xprt);
|
||||
}
|
||||
#endif /* _LINUX_SUNRPC_CLNT_H */
|
||||
|
|
|
@ -692,11 +692,10 @@ TRACE_EVENT(xprtrdma_prepsend_failed,
|
|||
|
||||
TRACE_EVENT(xprtrdma_post_send,
|
||||
TP_PROTO(
|
||||
const struct rpcrdma_req *req,
|
||||
int status
|
||||
const struct rpcrdma_req *req
|
||||
),
|
||||
|
||||
TP_ARGS(req, status),
|
||||
TP_ARGS(req),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(const void *, req)
|
||||
|
@ -705,7 +704,6 @@ TRACE_EVENT(xprtrdma_post_send,
|
|||
__field(unsigned int, client_id)
|
||||
__field(int, num_sge)
|
||||
__field(int, signaled)
|
||||
__field(int, status)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
|
@ -718,15 +716,13 @@ TRACE_EVENT(xprtrdma_post_send,
|
|||
__entry->sc = req->rl_sendctx;
|
||||
__entry->num_sge = req->rl_wr.num_sge;
|
||||
__entry->signaled = req->rl_wr.send_flags & IB_SEND_SIGNALED;
|
||||
__entry->status = status;
|
||||
),
|
||||
|
||||
TP_printk("task:%u@%u req=%p sc=%p (%d SGE%s) %sstatus=%d",
|
||||
TP_printk("task:%u@%u req=%p sc=%p (%d SGE%s) %s",
|
||||
__entry->task_id, __entry->client_id,
|
||||
__entry->req, __entry->sc, __entry->num_sge,
|
||||
(__entry->num_sge == 1 ? "" : "s"),
|
||||
(__entry->signaled ? "signaled " : ""),
|
||||
__entry->status
|
||||
(__entry->signaled ? "signaled" : "")
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -880,6 +880,20 @@ EXPORT_SYMBOL_GPL(rpc_shutdown_client);
|
|||
/*
|
||||
* Free an RPC client
|
||||
*/
|
||||
static void rpc_free_client_work(struct work_struct *work)
|
||||
{
|
||||
struct rpc_clnt *clnt = container_of(work, struct rpc_clnt, cl_work);
|
||||
|
||||
/* These might block on processes that might allocate memory,
|
||||
* so they cannot be called in rpciod, so they are handled separately
|
||||
* here.
|
||||
*/
|
||||
rpc_clnt_debugfs_unregister(clnt);
|
||||
rpc_clnt_remove_pipedir(clnt);
|
||||
|
||||
kfree(clnt);
|
||||
rpciod_down();
|
||||
}
|
||||
static struct rpc_clnt *
|
||||
rpc_free_client(struct rpc_clnt *clnt)
|
||||
{
|
||||
|
@ -890,17 +904,16 @@ rpc_free_client(struct rpc_clnt *clnt)
|
|||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
if (clnt->cl_parent != clnt)
|
||||
parent = clnt->cl_parent;
|
||||
rpc_clnt_debugfs_unregister(clnt);
|
||||
rpc_clnt_remove_pipedir(clnt);
|
||||
rpc_unregister_client(clnt);
|
||||
rpc_free_iostats(clnt->cl_metrics);
|
||||
clnt->cl_metrics = NULL;
|
||||
xprt_put(rcu_dereference_raw(clnt->cl_xprt));
|
||||
xprt_iter_destroy(&clnt->cl_xpi);
|
||||
rpciod_down();
|
||||
put_cred(clnt->cl_cred);
|
||||
rpc_free_clid(clnt);
|
||||
kfree(clnt);
|
||||
|
||||
INIT_WORK(&clnt->cl_work, rpc_free_client_work);
|
||||
schedule_work(&clnt->cl_work);
|
||||
return parent;
|
||||
}
|
||||
|
||||
|
@ -2808,8 +2821,7 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt,
|
|||
task = rpc_call_null_helper(clnt, xprt, NULL,
|
||||
RPC_TASK_SOFT|RPC_TASK_SOFTCONN|RPC_TASK_ASYNC|RPC_TASK_NULLCREDS,
|
||||
&rpc_cb_add_xprt_call_ops, data);
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
|
||||
rpc_put_task(task);
|
||||
success:
|
||||
return 1;
|
||||
|
|
|
@ -388,7 +388,9 @@ static int rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt,
|
|||
} while (nsegs);
|
||||
|
||||
done:
|
||||
return xdr_stream_encode_item_absent(xdr);
|
||||
if (xdr_stream_encode_item_absent(xdr) < 0)
|
||||
return -EMSGSIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register and XDR encode the Write list. Supports encoding a list
|
||||
|
@ -454,7 +456,9 @@ static int rpcrdma_encode_write_list(struct rpcrdma_xprt *r_xprt,
|
|||
*segcount = cpu_to_be32(nchunks);
|
||||
|
||||
done:
|
||||
return xdr_stream_encode_item_absent(xdr);
|
||||
if (xdr_stream_encode_item_absent(xdr) < 0)
|
||||
return -EMSGSIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register and XDR encode the Reply chunk. Supports encoding an array
|
||||
|
@ -480,8 +484,11 @@ static int rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt,
|
|||
int nsegs, nchunks;
|
||||
__be32 *segcount;
|
||||
|
||||
if (wtype != rpcrdma_replych)
|
||||
return xdr_stream_encode_item_absent(xdr);
|
||||
if (wtype != rpcrdma_replych) {
|
||||
if (xdr_stream_encode_item_absent(xdr) < 0)
|
||||
return -EMSGSIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
seg = req->rl_segments;
|
||||
nsegs = rpcrdma_convert_iovs(r_xprt, &rqst->rq_rcv_buf, 0, wtype, seg);
|
||||
|
|
|
@ -289,6 +289,7 @@ rpcrdma_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
|
|||
case RDMA_CM_EVENT_DISCONNECTED:
|
||||
ep->re_connect_status = -ECONNABORTED;
|
||||
disconnected:
|
||||
xprt_force_disconnect(xprt);
|
||||
return rpcrdma_ep_destroy(ep);
|
||||
default:
|
||||
break;
|
||||
|
@ -1355,8 +1356,8 @@ int rpcrdma_post_sends(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
|
|||
--ep->re_send_count;
|
||||
}
|
||||
|
||||
trace_xprtrdma_post_send(req);
|
||||
rc = frwr_send(r_xprt, req);
|
||||
trace_xprtrdma_post_send(req, rc);
|
||||
if (rc)
|
||||
return -ENOTCONN;
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue