diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-12-16 16:12:53 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-12-16 16:12:53 -0500 |
commit | d025fbf1a2cd1d60f39579d331a9accf84c5ec5b (patch) | |
tree | 0314bc504eb0f5cc1ea4dd611eb55eb2cc88b98e | |
parent | f6f3732162b5ae3c771b9285a5a32d72b8586920 (diff) | |
parent | 90d91b0cd371193d9dbfa9beacab8ab9a4cb75e0 (diff) |
Merge tag 'nfs-for-4.15-3' of git://git.linux-nfs.org/projects/anna/linux-nfs
Pull NFS client fixes from Anna Schumaker:
"This has two stable bugfixes, one to fix a BUG_ON() when
nfs_commit_inode() is called with no outstanding commit requests and
another to fix a race in the SUNRPC receive codepath.
Additionally, there are also fixes for an NFS client deadlock and an
xprtrdma performance regression.
Summary:
Stable bugfixes:
- NFS: Avoid a BUG_ON() in nfs_commit_inode() by not waiting for a
commit in the case that there were no commit requests.
- SUNRPC: Fix a race in the receive code path
Other fixes:
- NFS: Fix a deadlock in nfs client initialization
- xprtrdma: Fix a performance regression for small IOs"
* tag 'nfs-for-4.15-3' of git://git.linux-nfs.org/projects/anna/linux-nfs:
SUNRPC: Fix a race in the receive code path
nfs: don't wait on commit in nfs_commit_inode() if there were no commit requests
xprtrdma: Spread reply processing over more CPUs
nfs: fix a deadlock in nfs client initialization
-rw-r--r-- | fs/nfs/client.c | 11 | ||||
-rw-r--r-- | fs/nfs/nfs4client.c | 17 | ||||
-rw-r--r-- | fs/nfs/write.c | 2 | ||||
-rw-r--r-- | net/sunrpc/xprt.c | 28 | ||||
-rw-r--r-- | net/sunrpc/xprtrdma/rpc_rdma.c | 6 | ||||
-rw-r--r-- | net/sunrpc/xprtrdma/transport.c | 2 | ||||
-rw-r--r-- | net/sunrpc/xprtrdma/verbs.c | 2 | ||||
-rw-r--r-- | net/sunrpc/xprtrdma/xprt_rdma.h | 1 |
8 files changed, 50 insertions, 19 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 0ac2fb1c6b63..b9129e2befea 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c | |||
@@ -291,12 +291,23 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat | |||
291 | const struct sockaddr *sap = data->addr; | 291 | const struct sockaddr *sap = data->addr; |
292 | struct nfs_net *nn = net_generic(data->net, nfs_net_id); | 292 | struct nfs_net *nn = net_generic(data->net, nfs_net_id); |
293 | 293 | ||
294 | again: | ||
294 | list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { | 295 | list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { |
295 | const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; | 296 | const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; |
296 | /* Don't match clients that failed to initialise properly */ | 297 | /* Don't match clients that failed to initialise properly */ |
297 | if (clp->cl_cons_state < 0) | 298 | if (clp->cl_cons_state < 0) |
298 | continue; | 299 | continue; |
299 | 300 | ||
301 | /* If a client is still initializing then we need to wait */ | ||
302 | if (clp->cl_cons_state > NFS_CS_READY) { | ||
303 | refcount_inc(&clp->cl_count); | ||
304 | spin_unlock(&nn->nfs_client_lock); | ||
305 | nfs_wait_client_init_complete(clp); | ||
306 | nfs_put_client(clp); | ||
307 | spin_lock(&nn->nfs_client_lock); | ||
308 | goto again; | ||
309 | } | ||
310 | |||
300 | /* Different NFS versions cannot share the same nfs_client */ | 311 | /* Different NFS versions cannot share the same nfs_client */ |
301 | if (clp->rpc_ops != data->nfs_mod->rpc_ops) | 312 | if (clp->rpc_ops != data->nfs_mod->rpc_ops) |
302 | continue; | 313 | continue; |
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 12bbab0becb4..65a7e5da508c 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c | |||
@@ -404,15 +404,19 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp, | |||
404 | if (error < 0) | 404 | if (error < 0) |
405 | goto error; | 405 | goto error; |
406 | 406 | ||
407 | if (!nfs4_has_session(clp)) | ||
408 | nfs_mark_client_ready(clp, NFS_CS_READY); | ||
409 | |||
410 | error = nfs4_discover_server_trunking(clp, &old); | 407 | error = nfs4_discover_server_trunking(clp, &old); |
411 | if (error < 0) | 408 | if (error < 0) |
412 | goto error; | 409 | goto error; |
413 | 410 | ||
414 | if (clp != old) | 411 | if (clp != old) { |
415 | clp->cl_preserve_clid = true; | 412 | clp->cl_preserve_clid = true; |
413 | /* | ||
414 | * Mark the client as having failed initialization so other | ||
415 | * processes walking the nfs_client_list in nfs_match_client() | ||
416 | * won't try to use it. | ||
417 | */ | ||
418 | nfs_mark_client_ready(clp, -EPERM); | ||
419 | } | ||
416 | nfs_put_client(clp); | 420 | nfs_put_client(clp); |
417 | clear_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags); | 421 | clear_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags); |
418 | return old; | 422 | return old; |
@@ -539,6 +543,9 @@ int nfs40_walk_client_list(struct nfs_client *new, | |||
539 | spin_lock(&nn->nfs_client_lock); | 543 | spin_lock(&nn->nfs_client_lock); |
540 | list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { | 544 | list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { |
541 | 545 | ||
546 | if (pos == new) | ||
547 | goto found; | ||
548 | |||
542 | status = nfs4_match_client(pos, new, &prev, nn); | 549 | status = nfs4_match_client(pos, new, &prev, nn); |
543 | if (status < 0) | 550 | if (status < 0) |
544 | goto out_unlock; | 551 | goto out_unlock; |
@@ -559,6 +566,7 @@ int nfs40_walk_client_list(struct nfs_client *new, | |||
559 | * way that a SETCLIENTID_CONFIRM to pos can succeed is | 566 | * way that a SETCLIENTID_CONFIRM to pos can succeed is |
560 | * if new and pos point to the same server: | 567 | * if new and pos point to the same server: |
561 | */ | 568 | */ |
569 | found: | ||
562 | refcount_inc(&pos->cl_count); | 570 | refcount_inc(&pos->cl_count); |
563 | spin_unlock(&nn->nfs_client_lock); | 571 | spin_unlock(&nn->nfs_client_lock); |
564 | 572 | ||
@@ -572,6 +580,7 @@ int nfs40_walk_client_list(struct nfs_client *new, | |||
572 | case 0: | 580 | case 0: |
573 | nfs4_swap_callback_idents(pos, new); | 581 | nfs4_swap_callback_idents(pos, new); |
574 | pos->cl_confirm = new->cl_confirm; | 582 | pos->cl_confirm = new->cl_confirm; |
583 | nfs_mark_client_ready(pos, NFS_CS_READY); | ||
575 | 584 | ||
576 | prev = NULL; | 585 | prev = NULL; |
577 | *result = pos; | 586 | *result = pos; |
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 5b5f464f6f2a..4a379d7918f2 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -1890,6 +1890,8 @@ int nfs_commit_inode(struct inode *inode, int how) | |||
1890 | if (res) | 1890 | if (res) |
1891 | error = nfs_generic_commit_list(inode, &head, how, &cinfo); | 1891 | error = nfs_generic_commit_list(inode, &head, how, &cinfo); |
1892 | nfs_commit_end(cinfo.mds); | 1892 | nfs_commit_end(cinfo.mds); |
1893 | if (res == 0) | ||
1894 | return res; | ||
1893 | if (error < 0) | 1895 | if (error < 0) |
1894 | goto out_error; | 1896 | goto out_error; |
1895 | if (!may_wait) | 1897 | if (!may_wait) |
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 333b9d697ae5..33b74fd84051 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c | |||
@@ -1001,6 +1001,7 @@ void xprt_transmit(struct rpc_task *task) | |||
1001 | { | 1001 | { |
1002 | struct rpc_rqst *req = task->tk_rqstp; | 1002 | struct rpc_rqst *req = task->tk_rqstp; |
1003 | struct rpc_xprt *xprt = req->rq_xprt; | 1003 | struct rpc_xprt *xprt = req->rq_xprt; |
1004 | unsigned int connect_cookie; | ||
1004 | int status, numreqs; | 1005 | int status, numreqs; |
1005 | 1006 | ||
1006 | dprintk("RPC: %5u xprt_transmit(%u)\n", task->tk_pid, req->rq_slen); | 1007 | dprintk("RPC: %5u xprt_transmit(%u)\n", task->tk_pid, req->rq_slen); |
@@ -1024,6 +1025,7 @@ void xprt_transmit(struct rpc_task *task) | |||
1024 | } else if (!req->rq_bytes_sent) | 1025 | } else if (!req->rq_bytes_sent) |
1025 | return; | 1026 | return; |
1026 | 1027 | ||
1028 | connect_cookie = xprt->connect_cookie; | ||
1027 | req->rq_xtime = ktime_get(); | 1029 | req->rq_xtime = ktime_get(); |
1028 | status = xprt->ops->send_request(task); | 1030 | status = xprt->ops->send_request(task); |
1029 | trace_xprt_transmit(xprt, req->rq_xid, status); | 1031 | trace_xprt_transmit(xprt, req->rq_xid, status); |
@@ -1047,20 +1049,28 @@ void xprt_transmit(struct rpc_task *task) | |||
1047 | xprt->stat.bklog_u += xprt->backlog.qlen; | 1049 | xprt->stat.bklog_u += xprt->backlog.qlen; |
1048 | xprt->stat.sending_u += xprt->sending.qlen; | 1050 | xprt->stat.sending_u += xprt->sending.qlen; |
1049 | xprt->stat.pending_u += xprt->pending.qlen; | 1051 | xprt->stat.pending_u += xprt->pending.qlen; |
1052 | spin_unlock_bh(&xprt->transport_lock); | ||
1050 | 1053 | ||
1051 | /* Don't race with disconnect */ | 1054 | req->rq_connect_cookie = connect_cookie; |
1052 | if (!xprt_connected(xprt)) | 1055 | if (rpc_reply_expected(task) && !READ_ONCE(req->rq_reply_bytes_recvd)) { |
1053 | task->tk_status = -ENOTCONN; | ||
1054 | else { | ||
1055 | /* | 1056 | /* |
1056 | * Sleep on the pending queue since | 1057 | * Sleep on the pending queue if we're expecting a reply. |
1057 | * we're expecting a reply. | 1058 | * The spinlock ensures atomicity between the test of |
1059 | * req->rq_reply_bytes_recvd, and the call to rpc_sleep_on(). | ||
1058 | */ | 1060 | */ |
1059 | if (!req->rq_reply_bytes_recvd && rpc_reply_expected(task)) | 1061 | spin_lock(&xprt->recv_lock); |
1062 | if (!req->rq_reply_bytes_recvd) { | ||
1060 | rpc_sleep_on(&xprt->pending, task, xprt_timer); | 1063 | rpc_sleep_on(&xprt->pending, task, xprt_timer); |
1061 | req->rq_connect_cookie = xprt->connect_cookie; | 1064 | /* |
1065 | * Send an extra queue wakeup call if the | ||
1066 | * connection was dropped in case the call to | ||
1067 | * rpc_sleep_on() raced. | ||
1068 | */ | ||
1069 | if (!xprt_connected(xprt)) | ||
1070 | xprt_wake_pending_tasks(xprt, -ENOTCONN); | ||
1071 | } | ||
1072 | spin_unlock(&xprt->recv_lock); | ||
1062 | } | 1073 | } |
1063 | spin_unlock_bh(&xprt->transport_lock); | ||
1064 | } | 1074 | } |
1065 | 1075 | ||
1066 | static void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task) | 1076 | static void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task) |
diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index ed34dc0f144c..a3f2ab283aeb 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c | |||
@@ -1408,11 +1408,7 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) | |||
1408 | dprintk("RPC: %s: reply %p completes request %p (xid 0x%08x)\n", | 1408 | dprintk("RPC: %s: reply %p completes request %p (xid 0x%08x)\n", |
1409 | __func__, rep, req, be32_to_cpu(rep->rr_xid)); | 1409 | __func__, rep, req, be32_to_cpu(rep->rr_xid)); |
1410 | 1410 | ||
1411 | if (list_empty(&req->rl_registered) && | 1411 | queue_work_on(req->rl_cpu, rpcrdma_receive_wq, &rep->rr_work); |
1412 | !test_bit(RPCRDMA_REQ_F_TX_RESOURCES, &req->rl_flags)) | ||
1413 | rpcrdma_complete_rqst(rep); | ||
1414 | else | ||
1415 | queue_work(rpcrdma_receive_wq, &rep->rr_work); | ||
1416 | return; | 1412 | return; |
1417 | 1413 | ||
1418 | out_badstatus: | 1414 | out_badstatus: |
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 646c24494ea7..6ee1ad8978f3 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c | |||
@@ -52,6 +52,7 @@ | |||
52 | #include <linux/slab.h> | 52 | #include <linux/slab.h> |
53 | #include <linux/seq_file.h> | 53 | #include <linux/seq_file.h> |
54 | #include <linux/sunrpc/addr.h> | 54 | #include <linux/sunrpc/addr.h> |
55 | #include <linux/smp.h> | ||
55 | 56 | ||
56 | #include "xprt_rdma.h" | 57 | #include "xprt_rdma.h" |
57 | 58 | ||
@@ -656,6 +657,7 @@ xprt_rdma_allocate(struct rpc_task *task) | |||
656 | task->tk_pid, __func__, rqst->rq_callsize, | 657 | task->tk_pid, __func__, rqst->rq_callsize, |
657 | rqst->rq_rcvsize, req); | 658 | rqst->rq_rcvsize, req); |
658 | 659 | ||
660 | req->rl_cpu = smp_processor_id(); | ||
659 | req->rl_connect_cookie = 0; /* our reserved value */ | 661 | req->rl_connect_cookie = 0; /* our reserved value */ |
660 | rpcrdma_set_xprtdata(rqst, req); | 662 | rpcrdma_set_xprtdata(rqst, req); |
661 | rqst->rq_buffer = req->rl_sendbuf->rg_base; | 663 | rqst->rq_buffer = req->rl_sendbuf->rg_base; |
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 710b3f77db82..8607c029c0dd 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c | |||
@@ -83,7 +83,7 @@ rpcrdma_alloc_wq(void) | |||
83 | struct workqueue_struct *recv_wq; | 83 | struct workqueue_struct *recv_wq; |
84 | 84 | ||
85 | recv_wq = alloc_workqueue("xprtrdma_receive", | 85 | recv_wq = alloc_workqueue("xprtrdma_receive", |
86 | WQ_MEM_RECLAIM | WQ_UNBOUND | WQ_HIGHPRI, | 86 | WQ_MEM_RECLAIM | WQ_HIGHPRI, |
87 | 0); | 87 | 0); |
88 | if (!recv_wq) | 88 | if (!recv_wq) |
89 | return -ENOMEM; | 89 | return -ENOMEM; |
diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index 51686d9eac5f..1342f743f1c4 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h | |||
@@ -342,6 +342,7 @@ enum { | |||
342 | struct rpcrdma_buffer; | 342 | struct rpcrdma_buffer; |
343 | struct rpcrdma_req { | 343 | struct rpcrdma_req { |
344 | struct list_head rl_list; | 344 | struct list_head rl_list; |
345 | int rl_cpu; | ||
345 | unsigned int rl_connect_cookie; | 346 | unsigned int rl_connect_cookie; |
346 | struct rpcrdma_buffer *rl_buffer; | 347 | struct rpcrdma_buffer *rl_buffer; |
347 | struct rpcrdma_rep *rl_reply; | 348 | struct rpcrdma_rep *rl_reply; |