]> git.itanic.dy.fi Git - linux-stable/commitdiff
SUNRPC: never enqueue a ->rq_cong request on ->sending
authorNeil Brown <neilb@suse.de>
Mon, 15 Jun 2015 05:55:30 +0000 (15:55 +1000)
committerZefan Li <lizefan@huawei.com>
Mon, 21 Mar 2016 01:17:56 +0000 (09:17 +0800)
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>
net/sunrpc/xprt.c

index f1a63c1885dcc7ec5974629fd564570b7002e3bc..fe57284226674edbd887bddf85e0fd2ba5651361 100644 (file)
@@ -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;