Commit 29a47f45 authored by Linus Torvalds's avatar Linus Torvalds

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
parents ed6889db 9c07b75b
...@@ -253,37 +253,45 @@ int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, ...@@ -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) 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; int status;
if (S_ISDIR(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) {
switch(type) { switch(type) {
case ACL_TYPE_ACCESS: case ACL_TYPE_ACCESS:
alloc = dfacl = get_acl(inode, ACL_TYPE_DEFAULT); alloc = get_acl(inode, ACL_TYPE_DEFAULT);
if (IS_ERR(alloc)) if (IS_ERR(alloc))
goto fail; goto fail;
dfacl = alloc;
break; break;
case ACL_TYPE_DEFAULT: case ACL_TYPE_DEFAULT:
dfacl = acl; alloc = get_acl(inode, ACL_TYPE_ACCESS);
alloc = acl = get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(alloc)) if (IS_ERR(alloc))
goto fail; goto fail;
dfacl = acl;
acl = alloc;
break; break;
} }
} }
if (acl == NULL) { 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)) if (IS_ERR(alloc))
goto fail; goto fail;
acl = alloc;
} }
status = __nfs3_proc_setacls(inode, acl, dfacl); 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; return status;
fail: fail:
return PTR_ERR(alloc); status = PTR_ERR(alloc);
goto out;
} }
const struct xattr_handler *nfs3_xattr_handlers[] = { const struct xattr_handler *nfs3_xattr_handlers[] = {
......
...@@ -7891,6 +7891,7 @@ static void ...@@ -7891,6 +7891,7 @@ static void
nfs4_bind_one_conn_to_session_done(struct rpc_task *task, void *calldata) 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_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; struct nfs_client *clp = args->client;
switch (task->tk_status) { switch (task->tk_status) {
...@@ -7899,6 +7900,12 @@ nfs4_bind_one_conn_to_session_done(struct rpc_task *task, void *calldata) ...@@ -7899,6 +7900,12 @@ nfs4_bind_one_conn_to_session_done(struct rpc_task *task, void *calldata)
nfs4_schedule_session_recovery(clp->cl_session, nfs4_schedule_session_recovery(clp->cl_session,
task->tk_status); 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 = { 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, ...@@ -7921,6 +7928,7 @@ int nfs4_proc_bind_one_conn_to_session(struct rpc_clnt *clnt,
struct nfs41_bind_conn_to_session_args args = { struct nfs41_bind_conn_to_session_args args = {
.client = clp, .client = clp,
.dir = NFS4_CDFC4_FORE_OR_BOTH, .dir = NFS4_CDFC4_FORE_OR_BOTH,
.retries = 0,
}; };
struct nfs41_bind_conn_to_session_res res; struct nfs41_bind_conn_to_session_res res;
struct rpc_message msg = { struct rpc_message msg = {
...@@ -9191,8 +9199,7 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout) ...@@ -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); nfs4_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0, 0);
task = rpc_run_task(&task_setup_data); task = rpc_run_task(&task_setup_data);
if (IS_ERR(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; goto out;
......
...@@ -1332,13 +1332,15 @@ _pnfs_return_layout(struct inode *ino) ...@@ -1332,13 +1332,15 @@ _pnfs_return_layout(struct inode *ino)
!valid_layout) { !valid_layout) {
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
dprintk("NFS: %s no layout segments to return\n", __func__); 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); send = pnfs_prepare_layoutreturn(lo, &stateid, &cred, NULL);
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
if (send) if (send)
status = pnfs_send_layoutreturn(lo, &stateid, &cred, IOMODE_ANY, true); 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: out_put_layout_hdr:
pnfs_free_lseg_list(&tmp_list); pnfs_free_lseg_list(&tmp_list);
pnfs_put_layout_hdr(lo); pnfs_put_layout_hdr(lo);
...@@ -1456,18 +1458,15 @@ bool pnfs_roc(struct inode *ino, ...@@ -1456,18 +1458,15 @@ bool pnfs_roc(struct inode *ino,
/* lo ref dropped in pnfs_roc_release() */ /* lo ref dropped in pnfs_roc_release() */
layoutreturn = pnfs_prepare_layoutreturn(lo, &stateid, &lc_cred, &iomode); layoutreturn = pnfs_prepare_layoutreturn(lo, &stateid, &lc_cred, &iomode);
/* If the creds don't match, we can't compound the layoutreturn */ /* 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; goto out_noroc;
if (cred_fscmp(cred, lc_cred) != 0)
goto out_noroc_put_cred;
roc = layoutreturn; roc = layoutreturn;
pnfs_init_layoutreturn_args(args, lo, &stateid, iomode); pnfs_init_layoutreturn_args(args, lo, &stateid, iomode);
res->lrs_present = 0; res->lrs_present = 0;
layoutreturn = false; layoutreturn = false;
out_noroc_put_cred:
put_cred(lc_cred); put_cred(lc_cred);
out_noroc: out_noroc:
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
rcu_read_unlock(); rcu_read_unlock();
......
...@@ -185,7 +185,7 @@ static int __nfs_list_for_each_server(struct list_head *head, ...@@ -185,7 +185,7 @@ static int __nfs_list_for_each_server(struct list_head *head,
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(server, head, client_link) { list_for_each_entry_rcu(server, head, client_link) {
if (!nfs_sb_active(server->super)) if (!(server->super && nfs_sb_active(server->super)))
continue; continue;
rcu_read_unlock(); rcu_read_unlock();
if (last) if (last)
......
...@@ -1317,11 +1317,13 @@ struct nfs41_impl_id { ...@@ -1317,11 +1317,13 @@ struct nfs41_impl_id {
struct nfstime4 date; struct nfstime4 date;
}; };
#define MAX_BIND_CONN_TO_SESSION_RETRIES 3
struct nfs41_bind_conn_to_session_args { struct nfs41_bind_conn_to_session_args {
struct nfs_client *client; struct nfs_client *client;
struct nfs4_sessionid sessionid; struct nfs4_sessionid sessionid;
u32 dir; u32 dir;
bool use_conn_in_rdma_mode; bool use_conn_in_rdma_mode;
int retries;
}; };
struct nfs41_bind_conn_to_session_res { struct nfs41_bind_conn_to_session_res {
......
...@@ -71,7 +71,13 @@ struct rpc_clnt { ...@@ -71,7 +71,13 @@ struct rpc_clnt {
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
struct dentry *cl_debugfs; /* debugfs directory */ struct dentry *cl_debugfs; /* debugfs directory */
#endif #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; const struct cred *cl_cred;
}; };
...@@ -236,4 +242,9 @@ static inline int rpc_reply_expected(struct rpc_task *task) ...@@ -236,4 +242,9 @@ static inline int rpc_reply_expected(struct rpc_task *task)
(task->tk_msg.rpc_proc->p_decode != NULL); (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 */ #endif /* _LINUX_SUNRPC_CLNT_H */
...@@ -692,11 +692,10 @@ TRACE_EVENT(xprtrdma_prepsend_failed, ...@@ -692,11 +692,10 @@ TRACE_EVENT(xprtrdma_prepsend_failed,
TRACE_EVENT(xprtrdma_post_send, TRACE_EVENT(xprtrdma_post_send,
TP_PROTO( TP_PROTO(
const struct rpcrdma_req *req, const struct rpcrdma_req *req
int status
), ),
TP_ARGS(req, status), TP_ARGS(req),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const void *, req) __field(const void *, req)
...@@ -705,7 +704,6 @@ TRACE_EVENT(xprtrdma_post_send, ...@@ -705,7 +704,6 @@ TRACE_EVENT(xprtrdma_post_send,
__field(unsigned int, client_id) __field(unsigned int, client_id)
__field(int, num_sge) __field(int, num_sge)
__field(int, signaled) __field(int, signaled)
__field(int, status)
), ),
TP_fast_assign( TP_fast_assign(
...@@ -718,15 +716,13 @@ TRACE_EVENT(xprtrdma_post_send, ...@@ -718,15 +716,13 @@ TRACE_EVENT(xprtrdma_post_send,
__entry->sc = req->rl_sendctx; __entry->sc = req->rl_sendctx;
__entry->num_sge = req->rl_wr.num_sge; __entry->num_sge = req->rl_wr.num_sge;
__entry->signaled = req->rl_wr.send_flags & IB_SEND_SIGNALED; __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->task_id, __entry->client_id,
__entry->req, __entry->sc, __entry->num_sge, __entry->req, __entry->sc, __entry->num_sge,
(__entry->num_sge == 1 ? "" : "s"), (__entry->num_sge == 1 ? "" : "s"),
(__entry->signaled ? "signaled " : ""), (__entry->signaled ? "signaled" : "")
__entry->status
) )
); );
......
...@@ -880,6 +880,20 @@ EXPORT_SYMBOL_GPL(rpc_shutdown_client); ...@@ -880,6 +880,20 @@ EXPORT_SYMBOL_GPL(rpc_shutdown_client);
/* /*
* Free an RPC 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 * static struct rpc_clnt *
rpc_free_client(struct rpc_clnt *clnt) rpc_free_client(struct rpc_clnt *clnt)
{ {
...@@ -890,17 +904,16 @@ rpc_free_client(struct rpc_clnt *clnt) ...@@ -890,17 +904,16 @@ rpc_free_client(struct rpc_clnt *clnt)
rcu_dereference(clnt->cl_xprt)->servername); rcu_dereference(clnt->cl_xprt)->servername);
if (clnt->cl_parent != clnt) if (clnt->cl_parent != clnt)
parent = clnt->cl_parent; parent = clnt->cl_parent;
rpc_clnt_debugfs_unregister(clnt);
rpc_clnt_remove_pipedir(clnt);
rpc_unregister_client(clnt); rpc_unregister_client(clnt);
rpc_free_iostats(clnt->cl_metrics); rpc_free_iostats(clnt->cl_metrics);
clnt->cl_metrics = NULL; clnt->cl_metrics = NULL;
xprt_put(rcu_dereference_raw(clnt->cl_xprt)); xprt_put(rcu_dereference_raw(clnt->cl_xprt));
xprt_iter_destroy(&clnt->cl_xpi); xprt_iter_destroy(&clnt->cl_xpi);
rpciod_down();
put_cred(clnt->cl_cred); put_cred(clnt->cl_cred);
rpc_free_clid(clnt); rpc_free_clid(clnt);
kfree(clnt);
INIT_WORK(&clnt->cl_work, rpc_free_client_work);
schedule_work(&clnt->cl_work);
return parent; return parent;
} }
...@@ -2808,8 +2821,7 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt, ...@@ -2808,8 +2821,7 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt,
task = rpc_call_null_helper(clnt, xprt, NULL, task = rpc_call_null_helper(clnt, xprt, NULL,
RPC_TASK_SOFT|RPC_TASK_SOFTCONN|RPC_TASK_ASYNC|RPC_TASK_NULLCREDS, RPC_TASK_SOFT|RPC_TASK_SOFTCONN|RPC_TASK_ASYNC|RPC_TASK_NULLCREDS,
&rpc_cb_add_xprt_call_ops, data); &rpc_cb_add_xprt_call_ops, data);
if (IS_ERR(task))
return PTR_ERR(task);
rpc_put_task(task); rpc_put_task(task);
success: success:
return 1; return 1;
......
...@@ -388,7 +388,9 @@ static int rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt, ...@@ -388,7 +388,9 @@ static int rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt,
} while (nsegs); } while (nsegs);
done: 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 /* 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, ...@@ -454,7 +456,9 @@ static int rpcrdma_encode_write_list(struct rpcrdma_xprt *r_xprt,
*segcount = cpu_to_be32(nchunks); *segcount = cpu_to_be32(nchunks);
done: 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 /* 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, ...@@ -480,8 +484,11 @@ static int rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt,
int nsegs, nchunks; int nsegs, nchunks;
__be32 *segcount; __be32 *segcount;
if (wtype != rpcrdma_replych) if (wtype != rpcrdma_replych) {
return xdr_stream_encode_item_absent(xdr); if (xdr_stream_encode_item_absent(xdr) < 0)
return -EMSGSIZE;
return 0;
}
seg = req->rl_segments; seg = req->rl_segments;
nsegs = rpcrdma_convert_iovs(r_xprt, &rqst->rq_rcv_buf, 0, wtype, seg); 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) ...@@ -289,6 +289,7 @@ rpcrdma_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
case RDMA_CM_EVENT_DISCONNECTED: case RDMA_CM_EVENT_DISCONNECTED:
ep->re_connect_status = -ECONNABORTED; ep->re_connect_status = -ECONNABORTED;
disconnected: disconnected:
xprt_force_disconnect(xprt);
return rpcrdma_ep_destroy(ep); return rpcrdma_ep_destroy(ep);
default: default:
break; break;
...@@ -1355,8 +1356,8 @@ int rpcrdma_post_sends(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req) ...@@ -1355,8 +1356,8 @@ int rpcrdma_post_sends(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
--ep->re_send_count; --ep->re_send_count;
} }
trace_xprtrdma_post_send(req);
rc = frwr_send(r_xprt, req); rc = frwr_send(r_xprt, req);
trace_xprtrdma_post_send(req, rc);
if (rc) if (rc)
return -ENOTCONN; return -ENOTCONN;
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment