diff options
author | Neil Brown <neilb@suse.de> | 2015-06-15 08:55:30 +0300 |
---|---|---|
committer | Zefan Li <lizefan@huawei.com> | 2016-03-21 04:17:56 +0300 |
commit | 31469735a1e6618754230266fb842c3f7510cd28 (patch) | |
tree | 1f6a1d64df93e0dfb3c438c3870a4f1484699be7 /net | |
parent | faf7b6b0d45be29f6586784f56159592c9e5f3fc (diff) | |
download | linux-31469735a1e6618754230266fb842c3f7510cd28.tar.xz |
SUNRPC: never enqueue a ->rq_cong request on ->sending
commit 298073181112a6ab6c30fe7971b99de968daf81e upstream.
If the sending queue has a task without ->rq_cong set at the front,
and then a number of tasks with ->rq_cong set such that they use
the entire congestion window, then the queue deadlocks. The first
entry cannot be processed until later entries complete.
This scenario has been seen with a client using UDP to access a server,
and the network connection breaking for a period of time - it doesn't
recover.
It never really makes sense for an ->rq_cong request to be on the ->sending
queue, but it can happen when a request is being retried, and finds
the transport if locked (XPRT_LOCKED). In this case we simple call
__xprt_put_cong() and the deadlock goes away.
Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: Zefan Li <lizefan@huawei.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/sunrpc/xprt.c | 3 |
1 files changed, 3 insertions, 0 deletions
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index f1a63c1885dc..fe5728422667 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -66,6 +66,7 @@ static void xprt_init(struct rpc_xprt *xprt, struct net *net); static void xprt_request_init(struct rpc_task *, struct rpc_xprt *); static void xprt_connect_status(struct rpc_task *task); static int __xprt_get_cong(struct rpc_xprt *, struct rpc_task *); +static void __xprt_put_cong(struct rpc_xprt *, struct rpc_rqst *); static void xprt_destroy(struct rpc_xprt *xprt); static DEFINE_SPINLOCK(xprt_list_lock); @@ -269,6 +270,8 @@ int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task) } xprt_clear_locked(xprt); out_sleep: + if (req) + __xprt_put_cong(xprt, req); dprintk("RPC: %5u failed to lock transport %p\n", task->tk_pid, xprt); task->tk_timeout = 0; task->tk_status = -EAGAIN; |