xref: /openbmc/linux/net/sunrpc/xprt.c (revision 80b14d5e)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/net/sunrpc/xprt.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  This is a generic RPC call interface supporting congestion avoidance,
51da177e4SLinus Torvalds  *  and asynchronous calls.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  The interface works like this:
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  -	When a process places a call, it allocates a request slot if
101da177e4SLinus Torvalds  *	one is available. Otherwise, it sleeps on the backlog queue
111da177e4SLinus Torvalds  *	(xprt_reserve).
121da177e4SLinus Torvalds  *  -	Next, the caller puts together the RPC message, stuffs it into
1355aa4f58SChuck Lever  *	the request struct, and calls xprt_transmit().
1455aa4f58SChuck Lever  *  -	xprt_transmit sends the message and installs the caller on the
1555ae1aabSRicardo Labiaga  *	transport's wait list. At the same time, if a reply is expected,
1655ae1aabSRicardo Labiaga  *	it installs a timer that is run after the packet's timeout has
1755ae1aabSRicardo Labiaga  *	expired.
181da177e4SLinus Torvalds  *  -	When a packet arrives, the data_ready handler walks the list of
1955aa4f58SChuck Lever  *	pending requests for that transport. If a matching XID is found, the
201da177e4SLinus Torvalds  *	caller is woken up, and the timer removed.
211da177e4SLinus Torvalds  *  -	When no reply arrives within the timeout interval, the timer is
221da177e4SLinus Torvalds  *	fired by the kernel and runs xprt_timer(). It either adjusts the
231da177e4SLinus Torvalds  *	timeout values (minor timeout) or wakes up the caller with a status
241da177e4SLinus Torvalds  *	of -ETIMEDOUT.
251da177e4SLinus Torvalds  *  -	When the caller receives a notification from RPC that a reply arrived,
261da177e4SLinus Torvalds  *	it should release the RPC slot, and process the reply.
271da177e4SLinus Torvalds  *	If the call timed out, it may choose to retry the operation by
281da177e4SLinus Torvalds  *	adjusting the initial timeout value, and simply calling rpc_call
291da177e4SLinus Torvalds  *	again.
301da177e4SLinus Torvalds  *
311da177e4SLinus Torvalds  *  Support for async RPC is done through a set of RPC-specific scheduling
321da177e4SLinus Torvalds  *  primitives that `transparently' work for processes as well as async
331da177e4SLinus Torvalds  *  tasks that rely on callbacks.
341da177e4SLinus Torvalds  *
351da177e4SLinus Torvalds  *  Copyright (C) 1995-1997, Olaf Kirch <okir@monad.swb.de>
3655aa4f58SChuck Lever  *
3755aa4f58SChuck Lever  *  Transport switch API copyright (C) 2005, Chuck Lever <cel@netapp.com>
381da177e4SLinus Torvalds  */
391da177e4SLinus Torvalds 
40a246b010SChuck Lever #include <linux/module.h>
41a246b010SChuck Lever 
421da177e4SLinus Torvalds #include <linux/types.h>
43a246b010SChuck Lever #include <linux/interrupt.h>
441da177e4SLinus Torvalds #include <linux/workqueue.h>
45bf3fcf89SChuck Lever #include <linux/net.h>
46ff839970SChuck Lever #include <linux/ktime.h>
471da177e4SLinus Torvalds 
48a246b010SChuck Lever #include <linux/sunrpc/clnt.h>
4911c556b3SChuck Lever #include <linux/sunrpc/metrics.h>
50c9acb42eSTrond Myklebust #include <linux/sunrpc/bc_xprt.h>
51fda1bfefSTrond Myklebust #include <linux/rcupdate.h>
521da177e4SLinus Torvalds 
533705ad64SJeff Layton #include <trace/events/sunrpc.h>
543705ad64SJeff Layton 
5555ae1aabSRicardo Labiaga #include "sunrpc.h"
5655ae1aabSRicardo Labiaga 
571da177e4SLinus Torvalds /*
581da177e4SLinus Torvalds  * Local variables
591da177e4SLinus Torvalds  */
601da177e4SLinus Torvalds 
61f895b252SJeff Layton #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
621da177e4SLinus Torvalds # define RPCDBG_FACILITY	RPCDBG_XPRT
631da177e4SLinus Torvalds #endif
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds /*
661da177e4SLinus Torvalds  * Local functions
671da177e4SLinus Torvalds  */
6821de0a95STrond Myklebust static void	 xprt_init(struct rpc_xprt *xprt, struct net *net);
691da177e4SLinus Torvalds static void	xprt_request_init(struct rpc_task *, struct rpc_xprt *);
701da177e4SLinus Torvalds static void	xprt_connect_status(struct rpc_task *task);
711da177e4SLinus Torvalds static int      __xprt_get_cong(struct rpc_xprt *, struct rpc_task *);
7229807318SNeil Brown static void     __xprt_put_cong(struct rpc_xprt *, struct rpc_rqst *);
734e0038b6STrond Myklebust static void	 xprt_destroy(struct rpc_xprt *xprt);
741da177e4SLinus Torvalds 
755ba03e82SJiri Slaby static DEFINE_SPINLOCK(xprt_list_lock);
7681c098afS\"Talpey, Thomas\ static LIST_HEAD(xprt_list);
7781c098afS\"Talpey, Thomas\ 
7812a80469SChuck Lever /**
7981c098afS\"Talpey, Thomas\  * xprt_register_transport - register a transport implementation
8081c098afS\"Talpey, Thomas\  * @transport: transport to register
8181c098afS\"Talpey, Thomas\  *
8281c098afS\"Talpey, Thomas\  * If a transport implementation is loaded as a kernel module, it can
8381c098afS\"Talpey, Thomas\  * call this interface to make itself known to the RPC client.
8481c098afS\"Talpey, Thomas\  *
8581c098afS\"Talpey, Thomas\  * Returns:
8681c098afS\"Talpey, Thomas\  * 0:		transport successfully registered
8781c098afS\"Talpey, Thomas\  * -EEXIST:	transport already registered
8881c098afS\"Talpey, Thomas\  * -EINVAL:	transport module being unloaded
8981c098afS\"Talpey, Thomas\  */
9081c098afS\"Talpey, Thomas\ int xprt_register_transport(struct xprt_class *transport)
9181c098afS\"Talpey, Thomas\ {
9281c098afS\"Talpey, Thomas\ 	struct xprt_class *t;
9381c098afS\"Talpey, Thomas\ 	int result;
9481c098afS\"Talpey, Thomas\ 
9581c098afS\"Talpey, Thomas\ 	result = -EEXIST;
9681c098afS\"Talpey, Thomas\ 	spin_lock(&xprt_list_lock);
9781c098afS\"Talpey, Thomas\ 	list_for_each_entry(t, &xprt_list, list) {
9881c098afS\"Talpey, Thomas\ 		/* don't register the same transport class twice */
994fa016ebS\"Talpey, Thomas\ 		if (t->ident == transport->ident)
10081c098afS\"Talpey, Thomas\ 			goto out;
10181c098afS\"Talpey, Thomas\ 	}
10281c098afS\"Talpey, Thomas\ 
10381c098afS\"Talpey, Thomas\ 	list_add_tail(&transport->list, &xprt_list);
10481c098afS\"Talpey, Thomas\ 	printk(KERN_INFO "RPC: Registered %s transport module.\n",
10581c098afS\"Talpey, Thomas\ 	       transport->name);
10681c098afS\"Talpey, Thomas\ 	result = 0;
10781c098afS\"Talpey, Thomas\ 
10881c098afS\"Talpey, Thomas\ out:
10981c098afS\"Talpey, Thomas\ 	spin_unlock(&xprt_list_lock);
11081c098afS\"Talpey, Thomas\ 	return result;
11181c098afS\"Talpey, Thomas\ }
11281c098afS\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_register_transport);
11381c098afS\"Talpey, Thomas\ 
11481c098afS\"Talpey, Thomas\ /**
11581c098afS\"Talpey, Thomas\  * xprt_unregister_transport - unregister a transport implementation
11665b6e42cSRandy Dunlap  * @transport: transport to unregister
11781c098afS\"Talpey, Thomas\  *
11881c098afS\"Talpey, Thomas\  * Returns:
11981c098afS\"Talpey, Thomas\  * 0:		transport successfully unregistered
12081c098afS\"Talpey, Thomas\  * -ENOENT:	transport never registered
12181c098afS\"Talpey, Thomas\  */
12281c098afS\"Talpey, Thomas\ int xprt_unregister_transport(struct xprt_class *transport)
12381c098afS\"Talpey, Thomas\ {
12481c098afS\"Talpey, Thomas\ 	struct xprt_class *t;
12581c098afS\"Talpey, Thomas\ 	int result;
12681c098afS\"Talpey, Thomas\ 
12781c098afS\"Talpey, Thomas\ 	result = 0;
12881c098afS\"Talpey, Thomas\ 	spin_lock(&xprt_list_lock);
12981c098afS\"Talpey, Thomas\ 	list_for_each_entry(t, &xprt_list, list) {
13081c098afS\"Talpey, Thomas\ 		if (t == transport) {
13181c098afS\"Talpey, Thomas\ 			printk(KERN_INFO
13281c098afS\"Talpey, Thomas\ 				"RPC: Unregistered %s transport module.\n",
13381c098afS\"Talpey, Thomas\ 				transport->name);
13481c098afS\"Talpey, Thomas\ 			list_del_init(&transport->list);
13581c098afS\"Talpey, Thomas\ 			goto out;
13681c098afS\"Talpey, Thomas\ 		}
13781c098afS\"Talpey, Thomas\ 	}
13881c098afS\"Talpey, Thomas\ 	result = -ENOENT;
13981c098afS\"Talpey, Thomas\ 
14081c098afS\"Talpey, Thomas\ out:
14181c098afS\"Talpey, Thomas\ 	spin_unlock(&xprt_list_lock);
14281c098afS\"Talpey, Thomas\ 	return result;
14381c098afS\"Talpey, Thomas\ }
14481c098afS\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_unregister_transport);
14581c098afS\"Talpey, Thomas\ 
14681c098afS\"Talpey, Thomas\ /**
147441e3e24STom Talpey  * xprt_load_transport - load a transport implementation
148441e3e24STom Talpey  * @transport_name: transport to load
149441e3e24STom Talpey  *
150441e3e24STom Talpey  * Returns:
151441e3e24STom Talpey  * 0:		transport successfully loaded
152441e3e24STom Talpey  * -ENOENT:	transport module not available
153441e3e24STom Talpey  */
154441e3e24STom Talpey int xprt_load_transport(const char *transport_name)
155441e3e24STom Talpey {
156441e3e24STom Talpey 	struct xprt_class *t;
157441e3e24STom Talpey 	int result;
158441e3e24STom Talpey 
159441e3e24STom Talpey 	result = 0;
160441e3e24STom Talpey 	spin_lock(&xprt_list_lock);
161441e3e24STom Talpey 	list_for_each_entry(t, &xprt_list, list) {
162441e3e24STom Talpey 		if (strcmp(t->name, transport_name) == 0) {
163441e3e24STom Talpey 			spin_unlock(&xprt_list_lock);
164441e3e24STom Talpey 			goto out;
165441e3e24STom Talpey 		}
166441e3e24STom Talpey 	}
167441e3e24STom Talpey 	spin_unlock(&xprt_list_lock);
168ef7ffe8fSAlex Riesen 	result = request_module("xprt%s", transport_name);
169441e3e24STom Talpey out:
170441e3e24STom Talpey 	return result;
171441e3e24STom Talpey }
172441e3e24STom Talpey EXPORT_SYMBOL_GPL(xprt_load_transport);
173441e3e24STom Talpey 
174441e3e24STom Talpey /**
17512a80469SChuck Lever  * xprt_reserve_xprt - serialize write access to transports
17612a80469SChuck Lever  * @task: task that is requesting access to the transport
177177c27bfSRandy Dunlap  * @xprt: pointer to the target transport
17812a80469SChuck Lever  *
17912a80469SChuck Lever  * This prevents mixing the payload of separate requests, and prevents
18012a80469SChuck Lever  * transport connects from colliding with writes.  No congestion control
18112a80469SChuck Lever  * is provided.
1821da177e4SLinus Torvalds  */
18343cedbf0STrond Myklebust int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
1841da177e4SLinus Torvalds {
18512a80469SChuck Lever 	struct rpc_rqst *req = task->tk_rqstp;
18634006ceeSTrond Myklebust 	int priority;
18712a80469SChuck Lever 
18812a80469SChuck Lever 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) {
18912a80469SChuck Lever 		if (task == xprt->snd_task)
19012a80469SChuck Lever 			return 1;
19112a80469SChuck Lever 		goto out_sleep;
19212a80469SChuck Lever 	}
19312a80469SChuck Lever 	xprt->snd_task = task;
19492551948STrond Myklebust 	if (req != NULL)
19512a80469SChuck Lever 		req->rq_ntrans++;
1964d4a76f3Sj223yang@asset.uwaterloo.ca 
19712a80469SChuck Lever 	return 1;
19812a80469SChuck Lever 
19912a80469SChuck Lever out_sleep:
20046121cf7SChuck Lever 	dprintk("RPC: %5u failed to lock transport %p\n",
20112a80469SChuck Lever 			task->tk_pid, xprt);
20212a80469SChuck Lever 	task->tk_timeout = 0;
20312a80469SChuck Lever 	task->tk_status = -EAGAIN;
20434006ceeSTrond Myklebust 	if (req == NULL)
20534006ceeSTrond Myklebust 		priority = RPC_PRIORITY_LOW;
20634006ceeSTrond Myklebust 	else if (!req->rq_ntrans)
20734006ceeSTrond Myklebust 		priority = RPC_PRIORITY_NORMAL;
20812a80469SChuck Lever 	else
20934006ceeSTrond Myklebust 		priority = RPC_PRIORITY_HIGH;
21034006ceeSTrond Myklebust 	rpc_sleep_on_priority(&xprt->sending, task, NULL, priority);
21112a80469SChuck Lever 	return 0;
21212a80469SChuck Lever }
21312444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_reserve_xprt);
21412a80469SChuck Lever 
215632e3bdcSTrond Myklebust static void xprt_clear_locked(struct rpc_xprt *xprt)
216632e3bdcSTrond Myklebust {
217632e3bdcSTrond Myklebust 	xprt->snd_task = NULL;
218d19751e7STrond Myklebust 	if (!test_bit(XPRT_CLOSE_WAIT, &xprt->state)) {
2194e857c58SPeter Zijlstra 		smp_mb__before_atomic();
220632e3bdcSTrond Myklebust 		clear_bit(XPRT_LOCKED, &xprt->state);
2214e857c58SPeter Zijlstra 		smp_mb__after_atomic();
222632e3bdcSTrond Myklebust 	} else
223c1384c9cSTrond Myklebust 		queue_work(rpciod_workqueue, &xprt->task_cleanup);
224632e3bdcSTrond Myklebust }
225632e3bdcSTrond Myklebust 
22612a80469SChuck Lever /*
22712a80469SChuck Lever  * xprt_reserve_xprt_cong - serialize write access to transports
22812a80469SChuck Lever  * @task: task that is requesting access to the transport
22912a80469SChuck Lever  *
23012a80469SChuck Lever  * Same as xprt_reserve_xprt, but Van Jacobson congestion control is
23112a80469SChuck Lever  * integrated into the decision of whether a request is allowed to be
23212a80469SChuck Lever  * woken up and given access to the transport.
23312a80469SChuck Lever  */
23443cedbf0STrond Myklebust int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task)
23512a80469SChuck Lever {
2361da177e4SLinus Torvalds 	struct rpc_rqst *req = task->tk_rqstp;
23734006ceeSTrond Myklebust 	int priority;
2381da177e4SLinus Torvalds 
2392226feb6SChuck Lever 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) {
2401da177e4SLinus Torvalds 		if (task == xprt->snd_task)
2411da177e4SLinus Torvalds 			return 1;
2421da177e4SLinus Torvalds 		goto out_sleep;
2431da177e4SLinus Torvalds 	}
24443cedbf0STrond Myklebust 	if (req == NULL) {
24543cedbf0STrond Myklebust 		xprt->snd_task = task;
24643cedbf0STrond Myklebust 		return 1;
24743cedbf0STrond Myklebust 	}
24812a80469SChuck Lever 	if (__xprt_get_cong(xprt, task)) {
2491da177e4SLinus Torvalds 		xprt->snd_task = task;
2501da177e4SLinus Torvalds 		req->rq_ntrans++;
2511da177e4SLinus Torvalds 		return 1;
2521da177e4SLinus Torvalds 	}
253632e3bdcSTrond Myklebust 	xprt_clear_locked(xprt);
2541da177e4SLinus Torvalds out_sleep:
25529807318SNeil Brown 	if (req)
25629807318SNeil Brown 		__xprt_put_cong(xprt, req);
25746121cf7SChuck Lever 	dprintk("RPC: %5u failed to lock transport %p\n", task->tk_pid, xprt);
2581da177e4SLinus Torvalds 	task->tk_timeout = 0;
2591da177e4SLinus Torvalds 	task->tk_status = -EAGAIN;
26034006ceeSTrond Myklebust 	if (req == NULL)
26134006ceeSTrond Myklebust 		priority = RPC_PRIORITY_LOW;
26234006ceeSTrond Myklebust 	else if (!req->rq_ntrans)
26334006ceeSTrond Myklebust 		priority = RPC_PRIORITY_NORMAL;
2641da177e4SLinus Torvalds 	else
26534006ceeSTrond Myklebust 		priority = RPC_PRIORITY_HIGH;
26634006ceeSTrond Myklebust 	rpc_sleep_on_priority(&xprt->sending, task, NULL, priority);
2671da177e4SLinus Torvalds 	return 0;
2681da177e4SLinus Torvalds }
26912444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_reserve_xprt_cong);
2701da177e4SLinus Torvalds 
27112a80469SChuck Lever static inline int xprt_lock_write(struct rpc_xprt *xprt, struct rpc_task *task)
2721da177e4SLinus Torvalds {
2731da177e4SLinus Torvalds 	int retval;
2741da177e4SLinus Torvalds 
2754a0f8c04SChuck Lever 	spin_lock_bh(&xprt->transport_lock);
27643cedbf0STrond Myklebust 	retval = xprt->ops->reserve_xprt(xprt, task);
2774a0f8c04SChuck Lever 	spin_unlock_bh(&xprt->transport_lock);
2781da177e4SLinus Torvalds 	return retval;
2791da177e4SLinus Torvalds }
2801da177e4SLinus Torvalds 
281961a828dSTrond Myklebust static bool __xprt_lock_write_func(struct rpc_task *task, void *data)
2821da177e4SLinus Torvalds {
283961a828dSTrond Myklebust 	struct rpc_xprt *xprt = data;
28449e9a890SChuck Lever 	struct rpc_rqst *req;
28549e9a890SChuck Lever 
28649e9a890SChuck Lever 	req = task->tk_rqstp;
28749e9a890SChuck Lever 	xprt->snd_task = task;
28892551948STrond Myklebust 	if (req)
28949e9a890SChuck Lever 		req->rq_ntrans++;
290961a828dSTrond Myklebust 	return true;
291961a828dSTrond Myklebust }
292961a828dSTrond Myklebust 
293961a828dSTrond Myklebust static void __xprt_lock_write_next(struct rpc_xprt *xprt)
294961a828dSTrond Myklebust {
295961a828dSTrond Myklebust 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state))
29649e9a890SChuck Lever 		return;
29749e9a890SChuck Lever 
298961a828dSTrond Myklebust 	if (rpc_wake_up_first(&xprt->sending, __xprt_lock_write_func, xprt))
299961a828dSTrond Myklebust 		return;
300632e3bdcSTrond Myklebust 	xprt_clear_locked(xprt);
30149e9a890SChuck Lever }
30249e9a890SChuck Lever 
303961a828dSTrond Myklebust static bool __xprt_lock_write_cong_func(struct rpc_task *task, void *data)
30449e9a890SChuck Lever {
305961a828dSTrond Myklebust 	struct rpc_xprt *xprt = data;
30643cedbf0STrond Myklebust 	struct rpc_rqst *req;
3071da177e4SLinus Torvalds 
30843cedbf0STrond Myklebust 	req = task->tk_rqstp;
30943cedbf0STrond Myklebust 	if (req == NULL) {
3101da177e4SLinus Torvalds 		xprt->snd_task = task;
311961a828dSTrond Myklebust 		return true;
31243cedbf0STrond Myklebust 	}
31343cedbf0STrond Myklebust 	if (__xprt_get_cong(xprt, task)) {
31443cedbf0STrond Myklebust 		xprt->snd_task = task;
3151da177e4SLinus Torvalds 		req->rq_ntrans++;
316961a828dSTrond Myklebust 		return true;
3171da177e4SLinus Torvalds 	}
318961a828dSTrond Myklebust 	return false;
319961a828dSTrond Myklebust }
320961a828dSTrond Myklebust 
321961a828dSTrond Myklebust static void __xprt_lock_write_next_cong(struct rpc_xprt *xprt)
322961a828dSTrond Myklebust {
323961a828dSTrond Myklebust 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state))
324961a828dSTrond Myklebust 		return;
325961a828dSTrond Myklebust 	if (RPCXPRT_CONGESTED(xprt))
326961a828dSTrond Myklebust 		goto out_unlock;
327961a828dSTrond Myklebust 	if (rpc_wake_up_first(&xprt->sending, __xprt_lock_write_cong_func, xprt))
328961a828dSTrond Myklebust 		return;
3291da177e4SLinus Torvalds out_unlock:
330632e3bdcSTrond Myklebust 	xprt_clear_locked(xprt);
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
3330695314eSTrond Myklebust static void xprt_task_clear_bytes_sent(struct rpc_task *task)
3340695314eSTrond Myklebust {
3350695314eSTrond Myklebust 	if (task != NULL) {
3360695314eSTrond Myklebust 		struct rpc_rqst *req = task->tk_rqstp;
3370695314eSTrond Myklebust 		if (req != NULL)
3380695314eSTrond Myklebust 			req->rq_bytes_sent = 0;
3390695314eSTrond Myklebust 	}
3400695314eSTrond Myklebust }
3410695314eSTrond Myklebust 
34249e9a890SChuck Lever /**
34349e9a890SChuck Lever  * xprt_release_xprt - allow other requests to use a transport
34449e9a890SChuck Lever  * @xprt: transport with other tasks potentially waiting
34549e9a890SChuck Lever  * @task: task that is releasing access to the transport
34649e9a890SChuck Lever  *
34749e9a890SChuck Lever  * Note that "task" can be NULL.  No congestion control is provided.
3481da177e4SLinus Torvalds  */
34949e9a890SChuck Lever void xprt_release_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
3501da177e4SLinus Torvalds {
3511da177e4SLinus Torvalds 	if (xprt->snd_task == task) {
3520695314eSTrond Myklebust 		xprt_task_clear_bytes_sent(task);
353632e3bdcSTrond Myklebust 		xprt_clear_locked(xprt);
3541da177e4SLinus Torvalds 		__xprt_lock_write_next(xprt);
3551da177e4SLinus Torvalds 	}
3561da177e4SLinus Torvalds }
35712444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_release_xprt);
3581da177e4SLinus Torvalds 
35949e9a890SChuck Lever /**
36049e9a890SChuck Lever  * xprt_release_xprt_cong - allow other requests to use a transport
36149e9a890SChuck Lever  * @xprt: transport with other tasks potentially waiting
36249e9a890SChuck Lever  * @task: task that is releasing access to the transport
36349e9a890SChuck Lever  *
36449e9a890SChuck Lever  * Note that "task" can be NULL.  Another task is awoken to use the
36549e9a890SChuck Lever  * transport if the transport's congestion window allows it.
36649e9a890SChuck Lever  */
36749e9a890SChuck Lever void xprt_release_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task)
36849e9a890SChuck Lever {
36949e9a890SChuck Lever 	if (xprt->snd_task == task) {
3700695314eSTrond Myklebust 		xprt_task_clear_bytes_sent(task);
371632e3bdcSTrond Myklebust 		xprt_clear_locked(xprt);
37249e9a890SChuck Lever 		__xprt_lock_write_next_cong(xprt);
37349e9a890SChuck Lever 	}
37449e9a890SChuck Lever }
37512444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_release_xprt_cong);
37649e9a890SChuck Lever 
37749e9a890SChuck Lever static inline void xprt_release_write(struct rpc_xprt *xprt, struct rpc_task *task)
3781da177e4SLinus Torvalds {
3794a0f8c04SChuck Lever 	spin_lock_bh(&xprt->transport_lock);
38049e9a890SChuck Lever 	xprt->ops->release_xprt(xprt, task);
3814a0f8c04SChuck Lever 	spin_unlock_bh(&xprt->transport_lock);
3821da177e4SLinus Torvalds }
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds /*
3851da177e4SLinus Torvalds  * Van Jacobson congestion avoidance. Check if the congestion window
3861da177e4SLinus Torvalds  * overflowed. Put the task to sleep if this is the case.
3871da177e4SLinus Torvalds  */
3881da177e4SLinus Torvalds static int
3891da177e4SLinus Torvalds __xprt_get_cong(struct rpc_xprt *xprt, struct rpc_task *task)
3901da177e4SLinus Torvalds {
3911da177e4SLinus Torvalds 	struct rpc_rqst *req = task->tk_rqstp;
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds 	if (req->rq_cong)
3941da177e4SLinus Torvalds 		return 1;
39546121cf7SChuck Lever 	dprintk("RPC: %5u xprt_cwnd_limited cong = %lu cwnd = %lu\n",
3961da177e4SLinus Torvalds 			task->tk_pid, xprt->cong, xprt->cwnd);
3971da177e4SLinus Torvalds 	if (RPCXPRT_CONGESTED(xprt))
3981da177e4SLinus Torvalds 		return 0;
3991da177e4SLinus Torvalds 	req->rq_cong = 1;
4001da177e4SLinus Torvalds 	xprt->cong += RPC_CWNDSCALE;
4011da177e4SLinus Torvalds 	return 1;
4021da177e4SLinus Torvalds }
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds /*
4051da177e4SLinus Torvalds  * Adjust the congestion window, and wake up the next task
4061da177e4SLinus Torvalds  * that has been sleeping due to congestion
4071da177e4SLinus Torvalds  */
4081da177e4SLinus Torvalds static void
4091da177e4SLinus Torvalds __xprt_put_cong(struct rpc_xprt *xprt, struct rpc_rqst *req)
4101da177e4SLinus Torvalds {
4111da177e4SLinus Torvalds 	if (!req->rq_cong)
4121da177e4SLinus Torvalds 		return;
4131da177e4SLinus Torvalds 	req->rq_cong = 0;
4141da177e4SLinus Torvalds 	xprt->cong -= RPC_CWNDSCALE;
41549e9a890SChuck Lever 	__xprt_lock_write_next_cong(xprt);
4161da177e4SLinus Torvalds }
4171da177e4SLinus Torvalds 
41846c0ee8bSChuck Lever /**
419a58dd398SChuck Lever  * xprt_release_rqst_cong - housekeeping when request is complete
420a58dd398SChuck Lever  * @task: RPC request that recently completed
421a58dd398SChuck Lever  *
422a58dd398SChuck Lever  * Useful for transports that require congestion control.
423a58dd398SChuck Lever  */
424a58dd398SChuck Lever void xprt_release_rqst_cong(struct rpc_task *task)
425a58dd398SChuck Lever {
426a4f0835cSTrond Myklebust 	struct rpc_rqst *req = task->tk_rqstp;
427a4f0835cSTrond Myklebust 
428a4f0835cSTrond Myklebust 	__xprt_put_cong(req->rq_xprt, req);
429a58dd398SChuck Lever }
43012444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_release_rqst_cong);
431a58dd398SChuck Lever 
432a58dd398SChuck Lever /**
43346c0ee8bSChuck Lever  * xprt_adjust_cwnd - adjust transport congestion window
4346a24dfb6STrond Myklebust  * @xprt: pointer to xprt
43546c0ee8bSChuck Lever  * @task: recently completed RPC request used to adjust window
43646c0ee8bSChuck Lever  * @result: result code of completed RPC request
43746c0ee8bSChuck Lever  *
4384f4cf5adSChuck Lever  * The transport code maintains an estimate on the maximum number of out-
4394f4cf5adSChuck Lever  * standing RPC requests, using a smoothed version of the congestion
4404f4cf5adSChuck Lever  * avoidance implemented in 44BSD. This is basically the Van Jacobson
4414f4cf5adSChuck Lever  * congestion algorithm: If a retransmit occurs, the congestion window is
4424f4cf5adSChuck Lever  * halved; otherwise, it is incremented by 1/cwnd when
4434f4cf5adSChuck Lever  *
4444f4cf5adSChuck Lever  *	-	a reply is received and
4454f4cf5adSChuck Lever  *	-	a full number of requests are outstanding and
4464f4cf5adSChuck Lever  *	-	the congestion window hasn't been updated recently.
4471da177e4SLinus Torvalds  */
4486a24dfb6STrond Myklebust void xprt_adjust_cwnd(struct rpc_xprt *xprt, struct rpc_task *task, int result)
4491da177e4SLinus Torvalds {
45046c0ee8bSChuck Lever 	struct rpc_rqst *req = task->tk_rqstp;
45146c0ee8bSChuck Lever 	unsigned long cwnd = xprt->cwnd;
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds 	if (result >= 0 && cwnd <= xprt->cong) {
4541da177e4SLinus Torvalds 		/* The (cwnd >> 1) term makes sure
4551da177e4SLinus Torvalds 		 * the result gets rounded properly. */
4561da177e4SLinus Torvalds 		cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE + (cwnd >> 1)) / cwnd;
4571da177e4SLinus Torvalds 		if (cwnd > RPC_MAXCWND(xprt))
4581da177e4SLinus Torvalds 			cwnd = RPC_MAXCWND(xprt);
45949e9a890SChuck Lever 		__xprt_lock_write_next_cong(xprt);
4601da177e4SLinus Torvalds 	} else if (result == -ETIMEDOUT) {
4611da177e4SLinus Torvalds 		cwnd >>= 1;
4621da177e4SLinus Torvalds 		if (cwnd < RPC_CWNDSCALE)
4631da177e4SLinus Torvalds 			cwnd = RPC_CWNDSCALE;
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds 	dprintk("RPC:       cong %ld, cwnd was %ld, now %ld\n",
4661da177e4SLinus Torvalds 			xprt->cong, xprt->cwnd, cwnd);
4671da177e4SLinus Torvalds 	xprt->cwnd = cwnd;
46846c0ee8bSChuck Lever 	__xprt_put_cong(xprt, req);
4691da177e4SLinus Torvalds }
47012444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_adjust_cwnd);
4711da177e4SLinus Torvalds 
47244fbac22SChuck Lever /**
47344fbac22SChuck Lever  * xprt_wake_pending_tasks - wake all tasks on a transport's pending queue
47444fbac22SChuck Lever  * @xprt: transport with waiting tasks
47544fbac22SChuck Lever  * @status: result code to plant in each task before waking it
47644fbac22SChuck Lever  *
47744fbac22SChuck Lever  */
47844fbac22SChuck Lever void xprt_wake_pending_tasks(struct rpc_xprt *xprt, int status)
47944fbac22SChuck Lever {
48044fbac22SChuck Lever 	if (status < 0)
48144fbac22SChuck Lever 		rpc_wake_up_status(&xprt->pending, status);
48244fbac22SChuck Lever 	else
48344fbac22SChuck Lever 		rpc_wake_up(&xprt->pending);
48444fbac22SChuck Lever }
48512444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_wake_pending_tasks);
48644fbac22SChuck Lever 
487c7b2cae8SChuck Lever /**
488c7b2cae8SChuck Lever  * xprt_wait_for_buffer_space - wait for transport output buffer to clear
489c7b2cae8SChuck Lever  * @task: task to be put to sleep
4900b80ae42SRandy Dunlap  * @action: function pointer to be executed after wait
491a9a6b52eSTrond Myklebust  *
492a9a6b52eSTrond Myklebust  * Note that we only set the timer for the case of RPC_IS_SOFT(), since
493a9a6b52eSTrond Myklebust  * we don't in general want to force a socket disconnection due to
494a9a6b52eSTrond Myklebust  * an incomplete RPC call transmission.
495c7b2cae8SChuck Lever  */
496b6ddf64fSTrond Myklebust void xprt_wait_for_buffer_space(struct rpc_task *task, rpc_action action)
497c7b2cae8SChuck Lever {
498c7b2cae8SChuck Lever 	struct rpc_rqst *req = task->tk_rqstp;
499c7b2cae8SChuck Lever 	struct rpc_xprt *xprt = req->rq_xprt;
500c7b2cae8SChuck Lever 
501a9a6b52eSTrond Myklebust 	task->tk_timeout = RPC_IS_SOFT(task) ? req->rq_timeout : 0;
502b6ddf64fSTrond Myklebust 	rpc_sleep_on(&xprt->pending, task, action);
503c7b2cae8SChuck Lever }
50412444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_wait_for_buffer_space);
505c7b2cae8SChuck Lever 
506c7b2cae8SChuck Lever /**
507c7b2cae8SChuck Lever  * xprt_write_space - wake the task waiting for transport output buffer space
508c7b2cae8SChuck Lever  * @xprt: transport with waiting tasks
509c7b2cae8SChuck Lever  *
510c7b2cae8SChuck Lever  * Can be called in a soft IRQ context, so xprt_write_space never sleeps.
511c7b2cae8SChuck Lever  */
512c7b2cae8SChuck Lever void xprt_write_space(struct rpc_xprt *xprt)
513c7b2cae8SChuck Lever {
514c7b2cae8SChuck Lever 	spin_lock_bh(&xprt->transport_lock);
515c7b2cae8SChuck Lever 	if (xprt->snd_task) {
51646121cf7SChuck Lever 		dprintk("RPC:       write space: waking waiting task on "
51746121cf7SChuck Lever 				"xprt %p\n", xprt);
518fda13939STrond Myklebust 		rpc_wake_up_queued_task(&xprt->pending, xprt->snd_task);
519c7b2cae8SChuck Lever 	}
520c7b2cae8SChuck Lever 	spin_unlock_bh(&xprt->transport_lock);
521c7b2cae8SChuck Lever }
52212444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_write_space);
523c7b2cae8SChuck Lever 
524fe3aca29SChuck Lever /**
525fe3aca29SChuck Lever  * xprt_set_retrans_timeout_def - set a request's retransmit timeout
526fe3aca29SChuck Lever  * @task: task whose timeout is to be set
527fe3aca29SChuck Lever  *
528fe3aca29SChuck Lever  * Set a request's retransmit timeout based on the transport's
529fe3aca29SChuck Lever  * default timeout parameters.  Used by transports that don't adjust
530fe3aca29SChuck Lever  * the retransmit timeout based on round-trip time estimation.
531fe3aca29SChuck Lever  */
532fe3aca29SChuck Lever void xprt_set_retrans_timeout_def(struct rpc_task *task)
533fe3aca29SChuck Lever {
534fe3aca29SChuck Lever 	task->tk_timeout = task->tk_rqstp->rq_timeout;
535fe3aca29SChuck Lever }
53612444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_set_retrans_timeout_def);
537fe3aca29SChuck Lever 
5382c53040fSBen Hutchings /**
539fe3aca29SChuck Lever  * xprt_set_retrans_timeout_rtt - set a request's retransmit timeout
540fe3aca29SChuck Lever  * @task: task whose timeout is to be set
541fe3aca29SChuck Lever  *
542fe3aca29SChuck Lever  * Set a request's retransmit timeout using the RTT estimator.
543fe3aca29SChuck Lever  */
544fe3aca29SChuck Lever void xprt_set_retrans_timeout_rtt(struct rpc_task *task)
545fe3aca29SChuck Lever {
546fe3aca29SChuck Lever 	int timer = task->tk_msg.rpc_proc->p_timer;
547ba7392bbSTrond Myklebust 	struct rpc_clnt *clnt = task->tk_client;
548ba7392bbSTrond Myklebust 	struct rpc_rtt *rtt = clnt->cl_rtt;
549fe3aca29SChuck Lever 	struct rpc_rqst *req = task->tk_rqstp;
550ba7392bbSTrond Myklebust 	unsigned long max_timeout = clnt->cl_timeout->to_maxval;
551fe3aca29SChuck Lever 
552fe3aca29SChuck Lever 	task->tk_timeout = rpc_calc_rto(rtt, timer);
553fe3aca29SChuck Lever 	task->tk_timeout <<= rpc_ntimeo(rtt, timer) + req->rq_retries;
554fe3aca29SChuck Lever 	if (task->tk_timeout > max_timeout || task->tk_timeout == 0)
555fe3aca29SChuck Lever 		task->tk_timeout = max_timeout;
556fe3aca29SChuck Lever }
55712444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_set_retrans_timeout_rtt);
558fe3aca29SChuck Lever 
5591da177e4SLinus Torvalds static void xprt_reset_majortimeo(struct rpc_rqst *req)
5601da177e4SLinus Torvalds {
561ba7392bbSTrond Myklebust 	const struct rpc_timeout *to = req->rq_task->tk_client->cl_timeout;
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 	req->rq_majortimeo = req->rq_timeout;
5641da177e4SLinus Torvalds 	if (to->to_exponential)
5651da177e4SLinus Torvalds 		req->rq_majortimeo <<= to->to_retries;
5661da177e4SLinus Torvalds 	else
5671da177e4SLinus Torvalds 		req->rq_majortimeo += to->to_increment * to->to_retries;
5681da177e4SLinus Torvalds 	if (req->rq_majortimeo > to->to_maxval || req->rq_majortimeo == 0)
5691da177e4SLinus Torvalds 		req->rq_majortimeo = to->to_maxval;
5701da177e4SLinus Torvalds 	req->rq_majortimeo += jiffies;
5711da177e4SLinus Torvalds }
5721da177e4SLinus Torvalds 
5739903cd1cSChuck Lever /**
5749903cd1cSChuck Lever  * xprt_adjust_timeout - adjust timeout values for next retransmit
5759903cd1cSChuck Lever  * @req: RPC request containing parameters to use for the adjustment
5769903cd1cSChuck Lever  *
5771da177e4SLinus Torvalds  */
5781da177e4SLinus Torvalds int xprt_adjust_timeout(struct rpc_rqst *req)
5791da177e4SLinus Torvalds {
5801da177e4SLinus Torvalds 	struct rpc_xprt *xprt = req->rq_xprt;
581ba7392bbSTrond Myklebust 	const struct rpc_timeout *to = req->rq_task->tk_client->cl_timeout;
5821da177e4SLinus Torvalds 	int status = 0;
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 	if (time_before(jiffies, req->rq_majortimeo)) {
5851da177e4SLinus Torvalds 		if (to->to_exponential)
5861da177e4SLinus Torvalds 			req->rq_timeout <<= 1;
5871da177e4SLinus Torvalds 		else
5881da177e4SLinus Torvalds 			req->rq_timeout += to->to_increment;
5891da177e4SLinus Torvalds 		if (to->to_maxval && req->rq_timeout >= to->to_maxval)
5901da177e4SLinus Torvalds 			req->rq_timeout = to->to_maxval;
5911da177e4SLinus Torvalds 		req->rq_retries++;
5921da177e4SLinus Torvalds 	} else {
5931da177e4SLinus Torvalds 		req->rq_timeout = to->to_initval;
5941da177e4SLinus Torvalds 		req->rq_retries = 0;
5951da177e4SLinus Torvalds 		xprt_reset_majortimeo(req);
5961da177e4SLinus Torvalds 		/* Reset the RTT counters == "slow start" */
5974a0f8c04SChuck Lever 		spin_lock_bh(&xprt->transport_lock);
5981da177e4SLinus Torvalds 		rpc_init_rtt(req->rq_task->tk_client->cl_rtt, to->to_initval);
5994a0f8c04SChuck Lever 		spin_unlock_bh(&xprt->transport_lock);
6001da177e4SLinus Torvalds 		status = -ETIMEDOUT;
6011da177e4SLinus Torvalds 	}
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds 	if (req->rq_timeout == 0) {
6041da177e4SLinus Torvalds 		printk(KERN_WARNING "xprt_adjust_timeout: rq_timeout = 0!\n");
6051da177e4SLinus Torvalds 		req->rq_timeout = 5 * HZ;
6061da177e4SLinus Torvalds 	}
6071da177e4SLinus Torvalds 	return status;
6081da177e4SLinus Torvalds }
6091da177e4SLinus Torvalds 
61065f27f38SDavid Howells static void xprt_autoclose(struct work_struct *work)
6111da177e4SLinus Torvalds {
61265f27f38SDavid Howells 	struct rpc_xprt *xprt =
61365f27f38SDavid Howells 		container_of(work, struct rpc_xprt, task_cleanup);
6141da177e4SLinus Torvalds 
61566af1e55STrond Myklebust 	clear_bit(XPRT_CLOSE_WAIT, &xprt->state);
6164876cc77STrond Myklebust 	xprt->ops->close(xprt);
6171da177e4SLinus Torvalds 	xprt_release_write(xprt, NULL);
61879234c3dSTrond Myklebust 	wake_up_bit(&xprt->state, XPRT_LOCKED);
6191da177e4SLinus Torvalds }
6201da177e4SLinus Torvalds 
6219903cd1cSChuck Lever /**
62262da3b24STrond Myklebust  * xprt_disconnect_done - mark a transport as disconnected
6239903cd1cSChuck Lever  * @xprt: transport to flag for disconnect
6249903cd1cSChuck Lever  *
6251da177e4SLinus Torvalds  */
62662da3b24STrond Myklebust void xprt_disconnect_done(struct rpc_xprt *xprt)
6271da177e4SLinus Torvalds {
6281da177e4SLinus Torvalds 	dprintk("RPC:       disconnected transport %p\n", xprt);
6294a0f8c04SChuck Lever 	spin_lock_bh(&xprt->transport_lock);
6301da177e4SLinus Torvalds 	xprt_clear_connected(xprt);
6312a491991STrond Myklebust 	xprt_wake_pending_tasks(xprt, -EAGAIN);
6324a0f8c04SChuck Lever 	spin_unlock_bh(&xprt->transport_lock);
6331da177e4SLinus Torvalds }
63462da3b24STrond Myklebust EXPORT_SYMBOL_GPL(xprt_disconnect_done);
6351da177e4SLinus Torvalds 
63666af1e55STrond Myklebust /**
63766af1e55STrond Myklebust  * xprt_force_disconnect - force a transport to disconnect
63866af1e55STrond Myklebust  * @xprt: transport to disconnect
63966af1e55STrond Myklebust  *
64066af1e55STrond Myklebust  */
64166af1e55STrond Myklebust void xprt_force_disconnect(struct rpc_xprt *xprt)
64266af1e55STrond Myklebust {
64366af1e55STrond Myklebust 	/* Don't race with the test_bit() in xprt_clear_locked() */
64466af1e55STrond Myklebust 	spin_lock_bh(&xprt->transport_lock);
64566af1e55STrond Myklebust 	set_bit(XPRT_CLOSE_WAIT, &xprt->state);
64666af1e55STrond Myklebust 	/* Try to schedule an autoclose RPC call */
64766af1e55STrond Myklebust 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0)
64866af1e55STrond Myklebust 		queue_work(rpciod_workqueue, &xprt->task_cleanup);
6492a491991STrond Myklebust 	xprt_wake_pending_tasks(xprt, -EAGAIN);
65066af1e55STrond Myklebust 	spin_unlock_bh(&xprt->transport_lock);
65166af1e55STrond Myklebust }
65266af1e55STrond Myklebust 
6537c1d71cfSTrond Myklebust /**
6547c1d71cfSTrond Myklebust  * xprt_conditional_disconnect - force a transport to disconnect
6557c1d71cfSTrond Myklebust  * @xprt: transport to disconnect
6567c1d71cfSTrond Myklebust  * @cookie: 'connection cookie'
6577c1d71cfSTrond Myklebust  *
6587c1d71cfSTrond Myklebust  * This attempts to break the connection if and only if 'cookie' matches
6597c1d71cfSTrond Myklebust  * the current transport 'connection cookie'. It ensures that we don't
6607c1d71cfSTrond Myklebust  * try to break the connection more than once when we need to retransmit
6617c1d71cfSTrond Myklebust  * a batch of RPC requests.
6627c1d71cfSTrond Myklebust  *
6637c1d71cfSTrond Myklebust  */
6647c1d71cfSTrond Myklebust void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie)
6657c1d71cfSTrond Myklebust {
6667c1d71cfSTrond Myklebust 	/* Don't race with the test_bit() in xprt_clear_locked() */
6677c1d71cfSTrond Myklebust 	spin_lock_bh(&xprt->transport_lock);
6687c1d71cfSTrond Myklebust 	if (cookie != xprt->connect_cookie)
6697c1d71cfSTrond Myklebust 		goto out;
6707c1d71cfSTrond Myklebust 	if (test_bit(XPRT_CLOSING, &xprt->state) || !xprt_connected(xprt))
6717c1d71cfSTrond Myklebust 		goto out;
6727c1d71cfSTrond Myklebust 	set_bit(XPRT_CLOSE_WAIT, &xprt->state);
6737c1d71cfSTrond Myklebust 	/* Try to schedule an autoclose RPC call */
6747c1d71cfSTrond Myklebust 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0)
6757c1d71cfSTrond Myklebust 		queue_work(rpciod_workqueue, &xprt->task_cleanup);
6762a491991STrond Myklebust 	xprt_wake_pending_tasks(xprt, -EAGAIN);
6777c1d71cfSTrond Myklebust out:
6787c1d71cfSTrond Myklebust 	spin_unlock_bh(&xprt->transport_lock);
6797c1d71cfSTrond Myklebust }
6807c1d71cfSTrond Myklebust 
6811da177e4SLinus Torvalds static void
6821da177e4SLinus Torvalds xprt_init_autodisconnect(unsigned long data)
6831da177e4SLinus Torvalds {
6841da177e4SLinus Torvalds 	struct rpc_xprt *xprt = (struct rpc_xprt *)data;
6851da177e4SLinus Torvalds 
6864a0f8c04SChuck Lever 	spin_lock(&xprt->transport_lock);
687d19751e7STrond Myklebust 	if (!list_empty(&xprt->recv))
6881da177e4SLinus Torvalds 		goto out_abort;
6892226feb6SChuck Lever 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state))
6901da177e4SLinus Torvalds 		goto out_abort;
6914a0f8c04SChuck Lever 	spin_unlock(&xprt->transport_lock);
692c1384c9cSTrond Myklebust 	queue_work(rpciod_workqueue, &xprt->task_cleanup);
6931da177e4SLinus Torvalds 	return;
6941da177e4SLinus Torvalds out_abort:
6954a0f8c04SChuck Lever 	spin_unlock(&xprt->transport_lock);
6961da177e4SLinus Torvalds }
6971da177e4SLinus Torvalds 
698718ba5b8STrond Myklebust bool xprt_lock_connect(struct rpc_xprt *xprt,
699718ba5b8STrond Myklebust 		struct rpc_task *task,
700718ba5b8STrond Myklebust 		void *cookie)
701718ba5b8STrond Myklebust {
702718ba5b8STrond Myklebust 	bool ret = false;
703718ba5b8STrond Myklebust 
704718ba5b8STrond Myklebust 	spin_lock_bh(&xprt->transport_lock);
705718ba5b8STrond Myklebust 	if (!test_bit(XPRT_LOCKED, &xprt->state))
706718ba5b8STrond Myklebust 		goto out;
707718ba5b8STrond Myklebust 	if (xprt->snd_task != task)
708718ba5b8STrond Myklebust 		goto out;
7090695314eSTrond Myklebust 	xprt_task_clear_bytes_sent(task);
710718ba5b8STrond Myklebust 	xprt->snd_task = cookie;
711718ba5b8STrond Myklebust 	ret = true;
712718ba5b8STrond Myklebust out:
713718ba5b8STrond Myklebust 	spin_unlock_bh(&xprt->transport_lock);
714718ba5b8STrond Myklebust 	return ret;
715718ba5b8STrond Myklebust }
716718ba5b8STrond Myklebust 
717718ba5b8STrond Myklebust void xprt_unlock_connect(struct rpc_xprt *xprt, void *cookie)
718718ba5b8STrond Myklebust {
719718ba5b8STrond Myklebust 	spin_lock_bh(&xprt->transport_lock);
720718ba5b8STrond Myklebust 	if (xprt->snd_task != cookie)
721718ba5b8STrond Myklebust 		goto out;
722718ba5b8STrond Myklebust 	if (!test_bit(XPRT_LOCKED, &xprt->state))
723718ba5b8STrond Myklebust 		goto out;
724718ba5b8STrond Myklebust 	xprt->snd_task =NULL;
725718ba5b8STrond Myklebust 	xprt->ops->release_xprt(xprt, NULL);
726718ba5b8STrond Myklebust out:
727718ba5b8STrond Myklebust 	spin_unlock_bh(&xprt->transport_lock);
72879234c3dSTrond Myklebust 	wake_up_bit(&xprt->state, XPRT_LOCKED);
729718ba5b8STrond Myklebust }
730718ba5b8STrond Myklebust 
7319903cd1cSChuck Lever /**
7329903cd1cSChuck Lever  * xprt_connect - schedule a transport connect operation
7339903cd1cSChuck Lever  * @task: RPC task that is requesting the connect
7341da177e4SLinus Torvalds  *
7351da177e4SLinus Torvalds  */
7361da177e4SLinus Torvalds void xprt_connect(struct rpc_task *task)
7371da177e4SLinus Torvalds {
738ad2368d6STrond Myklebust 	struct rpc_xprt	*xprt = task->tk_rqstp->rq_xprt;
7391da177e4SLinus Torvalds 
74046121cf7SChuck Lever 	dprintk("RPC: %5u xprt_connect xprt %p %s connected\n", task->tk_pid,
7411da177e4SLinus Torvalds 			xprt, (xprt_connected(xprt) ? "is" : "is not"));
7421da177e4SLinus Torvalds 
743ec739ef0SChuck Lever 	if (!xprt_bound(xprt)) {
74401d37c42STrond Myklebust 		task->tk_status = -EAGAIN;
7451da177e4SLinus Torvalds 		return;
7461da177e4SLinus Torvalds 	}
7471da177e4SLinus Torvalds 	if (!xprt_lock_write(xprt, task))
7481da177e4SLinus Torvalds 		return;
749feb8ca37STrond Myklebust 
750feb8ca37STrond Myklebust 	if (test_and_clear_bit(XPRT_CLOSE_WAIT, &xprt->state))
751feb8ca37STrond Myklebust 		xprt->ops->close(xprt);
752feb8ca37STrond Myklebust 
753718ba5b8STrond Myklebust 	if (!xprt_connected(xprt)) {
7541da177e4SLinus Torvalds 		task->tk_rqstp->rq_bytes_sent = 0;
755a8ce4a8fSTrond Myklebust 		task->tk_timeout = task->tk_rqstp->rq_timeout;
7565d00837bSTrond Myklebust 		rpc_sleep_on(&xprt->pending, task, xprt_connect_status);
7570b9e7943STrond Myklebust 
7580b9e7943STrond Myklebust 		if (test_bit(XPRT_CLOSING, &xprt->state))
7590b9e7943STrond Myklebust 			return;
7600b9e7943STrond Myklebust 		if (xprt_test_and_set_connecting(xprt))
7610b9e7943STrond Myklebust 			return;
762262ca07dSChuck Lever 		xprt->stat.connect_start = jiffies;
7631b092092STrond Myklebust 		xprt->ops->connect(xprt, task);
7641da177e4SLinus Torvalds 	}
765718ba5b8STrond Myklebust 	xprt_release_write(xprt, task);
7661da177e4SLinus Torvalds }
7671da177e4SLinus Torvalds 
7689903cd1cSChuck Lever static void xprt_connect_status(struct rpc_task *task)
7691da177e4SLinus Torvalds {
770ad2368d6STrond Myklebust 	struct rpc_xprt	*xprt = task->tk_rqstp->rq_xprt;
7711da177e4SLinus Torvalds 
772cd983ef8SChuck Lever 	if (task->tk_status == 0) {
773262ca07dSChuck Lever 		xprt->stat.connect_count++;
774262ca07dSChuck Lever 		xprt->stat.connect_time += (long)jiffies - xprt->stat.connect_start;
77546121cf7SChuck Lever 		dprintk("RPC: %5u xprt_connect_status: connection established\n",
7761da177e4SLinus Torvalds 				task->tk_pid);
7771da177e4SLinus Torvalds 		return;
7781da177e4SLinus Torvalds 	}
7791da177e4SLinus Torvalds 
7801da177e4SLinus Torvalds 	switch (task->tk_status) {
7810fe8d04eSTrond Myklebust 	case -ECONNREFUSED:
7820fe8d04eSTrond Myklebust 	case -ECONNRESET:
7830fe8d04eSTrond Myklebust 	case -ECONNABORTED:
7840fe8d04eSTrond Myklebust 	case -ENETUNREACH:
7850fe8d04eSTrond Myklebust 	case -EHOSTUNREACH:
7862fc193cfSTrond Myklebust 	case -EPIPE:
7872a491991STrond Myklebust 	case -EAGAIN:
7882a491991STrond Myklebust 		dprintk("RPC: %5u xprt_connect_status: retrying\n", task->tk_pid);
78923475d66SChuck Lever 		break;
7901da177e4SLinus Torvalds 	case -ETIMEDOUT:
79146121cf7SChuck Lever 		dprintk("RPC: %5u xprt_connect_status: connect attempt timed "
79246121cf7SChuck Lever 				"out\n", task->tk_pid);
7931da177e4SLinus Torvalds 		break;
7941da177e4SLinus Torvalds 	default:
79546121cf7SChuck Lever 		dprintk("RPC: %5u xprt_connect_status: error %d connecting to "
79646121cf7SChuck Lever 				"server %s\n", task->tk_pid, -task->tk_status,
7974e0038b6STrond Myklebust 				xprt->servername);
79823475d66SChuck Lever 		task->tk_status = -EIO;
79923475d66SChuck Lever 	}
8001da177e4SLinus Torvalds }
8011da177e4SLinus Torvalds 
8029903cd1cSChuck Lever /**
8039903cd1cSChuck Lever  * xprt_lookup_rqst - find an RPC request corresponding to an XID
8049903cd1cSChuck Lever  * @xprt: transport on which the original request was transmitted
8059903cd1cSChuck Lever  * @xid: RPC XID of incoming reply
8069903cd1cSChuck Lever  *
8071da177e4SLinus Torvalds  */
808d8ed029dSAlexey Dobriyan struct rpc_rqst *xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid)
8091da177e4SLinus Torvalds {
8108f3a6de3SPavel Emelyanov 	struct rpc_rqst *entry;
8111da177e4SLinus Torvalds 
8128f3a6de3SPavel Emelyanov 	list_for_each_entry(entry, &xprt->recv, rq_list)
8133705ad64SJeff Layton 		if (entry->rq_xid == xid) {
8143705ad64SJeff Layton 			trace_xprt_lookup_rqst(xprt, xid, 0);
815262ca07dSChuck Lever 			return entry;
8163705ad64SJeff Layton 		}
81746121cf7SChuck Lever 
81846121cf7SChuck Lever 	dprintk("RPC:       xprt_lookup_rqst did not find xid %08x\n",
81946121cf7SChuck Lever 			ntohl(xid));
8203705ad64SJeff Layton 	trace_xprt_lookup_rqst(xprt, xid, -ENOENT);
821262ca07dSChuck Lever 	xprt->stat.bad_xids++;
822262ca07dSChuck Lever 	return NULL;
8231da177e4SLinus Torvalds }
82412444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_lookup_rqst);
8251da177e4SLinus Torvalds 
826bbc72ceaSChuck Lever static void xprt_update_rtt(struct rpc_task *task)
8271da177e4SLinus Torvalds {
8281570c1e4SChuck Lever 	struct rpc_rqst *req = task->tk_rqstp;
8291570c1e4SChuck Lever 	struct rpc_rtt *rtt = task->tk_client->cl_rtt;
83095c96174SEric Dumazet 	unsigned int timer = task->tk_msg.rpc_proc->p_timer;
831d60dbb20STrond Myklebust 	long m = usecs_to_jiffies(ktime_to_us(req->rq_rtt));
8321570c1e4SChuck Lever 
8331da177e4SLinus Torvalds 	if (timer) {
8341da177e4SLinus Torvalds 		if (req->rq_ntrans == 1)
835ff839970SChuck Lever 			rpc_update_rtt(rtt, timer, m);
8361570c1e4SChuck Lever 		rpc_set_timeo(rtt, timer, req->rq_ntrans - 1);
8371da177e4SLinus Torvalds 	}
8381da177e4SLinus Torvalds }
8391da177e4SLinus Torvalds 
8401570c1e4SChuck Lever /**
8411570c1e4SChuck Lever  * xprt_complete_rqst - called when reply processing is complete
8421570c1e4SChuck Lever  * @task: RPC request that recently completed
8431570c1e4SChuck Lever  * @copied: actual number of bytes received from the transport
8441570c1e4SChuck Lever  *
8451570c1e4SChuck Lever  * Caller holds transport lock.
8461570c1e4SChuck Lever  */
8471570c1e4SChuck Lever void xprt_complete_rqst(struct rpc_task *task, int copied)
8481570c1e4SChuck Lever {
8491570c1e4SChuck Lever 	struct rpc_rqst *req = task->tk_rqstp;
850fda13939STrond Myklebust 	struct rpc_xprt *xprt = req->rq_xprt;
8511da177e4SLinus Torvalds 
8521570c1e4SChuck Lever 	dprintk("RPC: %5u xid %08x complete (%d bytes received)\n",
8531570c1e4SChuck Lever 			task->tk_pid, ntohl(req->rq_xid), copied);
8543705ad64SJeff Layton 	trace_xprt_complete_rqst(xprt, req->rq_xid, copied);
8551da177e4SLinus Torvalds 
856fda13939STrond Myklebust 	xprt->stat.recvs++;
857d60dbb20STrond Myklebust 	req->rq_rtt = ktime_sub(ktime_get(), req->rq_xtime);
858bbc72ceaSChuck Lever 	if (xprt->ops->timer != NULL)
859bbc72ceaSChuck Lever 		xprt_update_rtt(task);
860ef759a2eSChuck Lever 
8611da177e4SLinus Torvalds 	list_del_init(&req->rq_list);
8621e799b67STrond Myklebust 	req->rq_private_buf.len = copied;
863dd2b63d0SRicardo Labiaga 	/* Ensure all writes are done before we update */
864dd2b63d0SRicardo Labiaga 	/* req->rq_reply_bytes_recvd */
86543ac3f29STrond Myklebust 	smp_wmb();
866dd2b63d0SRicardo Labiaga 	req->rq_reply_bytes_recvd = copied;
867fda13939STrond Myklebust 	rpc_wake_up_queued_task(&xprt->pending, task);
8681da177e4SLinus Torvalds }
86912444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xprt_complete_rqst);
8701da177e4SLinus Torvalds 
87146c0ee8bSChuck Lever static void xprt_timer(struct rpc_task *task)
8721da177e4SLinus Torvalds {
8731da177e4SLinus Torvalds 	struct rpc_rqst *req = task->tk_rqstp;
8741da177e4SLinus Torvalds 	struct rpc_xprt *xprt = req->rq_xprt;
8751da177e4SLinus Torvalds 
8765d00837bSTrond Myklebust 	if (task->tk_status != -ETIMEDOUT)
8775d00837bSTrond Myklebust 		return;
87846121cf7SChuck Lever 	dprintk("RPC: %5u xprt_timer\n", task->tk_pid);
87946c0ee8bSChuck Lever 
8805d00837bSTrond Myklebust 	spin_lock_bh(&xprt->transport_lock);
881dd2b63d0SRicardo Labiaga 	if (!req->rq_reply_bytes_recvd) {
88246c0ee8bSChuck Lever 		if (xprt->ops->timer)
8836a24dfb6STrond Myklebust 			xprt->ops->timer(xprt, task);
8845d00837bSTrond Myklebust 	} else
8855d00837bSTrond Myklebust 		task->tk_status = 0;
8865d00837bSTrond Myklebust 	spin_unlock_bh(&xprt->transport_lock);
8871da177e4SLinus Torvalds }
8881da177e4SLinus Torvalds 
8894cfc7e60SRahul Iyer static inline int xprt_has_timer(struct rpc_xprt *xprt)
8904cfc7e60SRahul Iyer {
8914cfc7e60SRahul Iyer 	return xprt->idle_timeout != 0;
8924cfc7e60SRahul Iyer }
8934cfc7e60SRahul Iyer 
8949903cd1cSChuck Lever /**
8959903cd1cSChuck Lever  * xprt_prepare_transmit - reserve the transport before sending a request
8969903cd1cSChuck Lever  * @task: RPC task about to send a request
8979903cd1cSChuck Lever  *
8981da177e4SLinus Torvalds  */
89990051ea7STrond Myklebust bool xprt_prepare_transmit(struct rpc_task *task)
9001da177e4SLinus Torvalds {
9011da177e4SLinus Torvalds 	struct rpc_rqst	*req = task->tk_rqstp;
9021da177e4SLinus Torvalds 	struct rpc_xprt	*xprt = req->rq_xprt;
90390051ea7STrond Myklebust 	bool ret = false;
9041da177e4SLinus Torvalds 
90546121cf7SChuck Lever 	dprintk("RPC: %5u xprt_prepare_transmit\n", task->tk_pid);
9061da177e4SLinus Torvalds 
9074a0f8c04SChuck Lever 	spin_lock_bh(&xprt->transport_lock);
9088a19a0b6STrond Myklebust 	if (!req->rq_bytes_sent) {
9098a19a0b6STrond Myklebust 		if (req->rq_reply_bytes_recvd) {
91090051ea7STrond Myklebust 			task->tk_status = req->rq_reply_bytes_recvd;
9111da177e4SLinus Torvalds 			goto out_unlock;
9121da177e4SLinus Torvalds 		}
9138a19a0b6STrond Myklebust 		if ((task->tk_flags & RPC_TASK_NO_RETRANS_TIMEOUT)
9148a19a0b6STrond Myklebust 		    && xprt_connected(xprt)
9158a19a0b6STrond Myklebust 		    && req->rq_connect_cookie == xprt->connect_cookie) {
9168a19a0b6STrond Myklebust 			xprt->ops->set_retrans_timeout(task);
9178a19a0b6STrond Myklebust 			rpc_sleep_on(&xprt->pending, task, xprt_timer);
9188a19a0b6STrond Myklebust 			goto out_unlock;
9198a19a0b6STrond Myklebust 		}
9208a19a0b6STrond Myklebust 	}
92190051ea7STrond Myklebust 	if (!xprt->ops->reserve_xprt(xprt, task)) {
92290051ea7STrond Myklebust 		task->tk_status = -EAGAIN;
92390051ea7STrond Myklebust 		goto out_unlock;
92490051ea7STrond Myklebust 	}
92590051ea7STrond Myklebust 	ret = true;
9261da177e4SLinus Torvalds out_unlock:
9274a0f8c04SChuck Lever 	spin_unlock_bh(&xprt->transport_lock);
92890051ea7STrond Myklebust 	return ret;
9291da177e4SLinus Torvalds }
9301da177e4SLinus Torvalds 
931e0ab53deSTrond Myklebust void xprt_end_transmit(struct rpc_task *task)
9325e5ce5beSTrond Myklebust {
933343952faSRahul Iyer 	xprt_release_write(task->tk_rqstp->rq_xprt, task);
9345e5ce5beSTrond Myklebust }
9355e5ce5beSTrond Myklebust 
9369903cd1cSChuck Lever /**
9379903cd1cSChuck Lever  * xprt_transmit - send an RPC request on a transport
9389903cd1cSChuck Lever  * @task: controlling RPC task
9399903cd1cSChuck Lever  *
9409903cd1cSChuck Lever  * We have to copy the iovec because sendmsg fiddles with its contents.
9419903cd1cSChuck Lever  */
9429903cd1cSChuck Lever void xprt_transmit(struct rpc_task *task)
9431da177e4SLinus Torvalds {
9441da177e4SLinus Torvalds 	struct rpc_rqst	*req = task->tk_rqstp;
9451da177e4SLinus Torvalds 	struct rpc_xprt	*xprt = req->rq_xprt;
94615a45206SAndy Adamson 	int status, numreqs;
9471da177e4SLinus Torvalds 
94846121cf7SChuck Lever 	dprintk("RPC: %5u xprt_transmit(%u)\n", task->tk_pid, req->rq_slen);
9491da177e4SLinus Torvalds 
950dd2b63d0SRicardo Labiaga 	if (!req->rq_reply_bytes_recvd) {
95155ae1aabSRicardo Labiaga 		if (list_empty(&req->rq_list) && rpc_reply_expected(task)) {
95255ae1aabSRicardo Labiaga 			/*
95355ae1aabSRicardo Labiaga 			 * Add to the list only if we're expecting a reply
95455ae1aabSRicardo Labiaga 			 */
9554a0f8c04SChuck Lever 			spin_lock_bh(&xprt->transport_lock);
9561da177e4SLinus Torvalds 			/* Update the softirq receive buffer */
9571da177e4SLinus Torvalds 			memcpy(&req->rq_private_buf, &req->rq_rcv_buf,
9581da177e4SLinus Torvalds 					sizeof(req->rq_private_buf));
9591da177e4SLinus Torvalds 			/* Add request to the receive list */
9601da177e4SLinus Torvalds 			list_add_tail(&req->rq_list, &xprt->recv);
9614a0f8c04SChuck Lever 			spin_unlock_bh(&xprt->transport_lock);
9621da177e4SLinus Torvalds 			xprt_reset_majortimeo(req);
9630f9dc2b1STrond Myklebust 			/* Turn off autodisconnect */
9640f9dc2b1STrond Myklebust 			del_singleshot_timer_sync(&xprt->timer);
9651da177e4SLinus Torvalds 		}
9661da177e4SLinus Torvalds 	} else if (!req->rq_bytes_sent)
9671da177e4SLinus Torvalds 		return;
9681da177e4SLinus Torvalds 
969ff839970SChuck Lever 	req->rq_xtime = ktime_get();
970a246b010SChuck Lever 	status = xprt->ops->send_request(task);
9713705ad64SJeff Layton 	trace_xprt_transmit(xprt, req->rq_xid, status);
972c8485e4dSTrond Myklebust 	if (status != 0) {
973c8485e4dSTrond Myklebust 		task->tk_status = status;
974c8485e4dSTrond Myklebust 		return;
975c8485e4dSTrond Myklebust 	}
9764a068258SChuck Lever 	xprt_inject_disconnect(xprt);
977c8485e4dSTrond Myklebust 
97846121cf7SChuck Lever 	dprintk("RPC: %5u xmit complete\n", task->tk_pid);
979468f8613SBryan Schumaker 	task->tk_flags |= RPC_TASK_SENT;
980fe3aca29SChuck Lever 	spin_lock_bh(&xprt->transport_lock);
981262ca07dSChuck Lever 
982fe3aca29SChuck Lever 	xprt->ops->set_retrans_timeout(task);
983262ca07dSChuck Lever 
98415a45206SAndy Adamson 	numreqs = atomic_read(&xprt->num_reqs);
98515a45206SAndy Adamson 	if (numreqs > xprt->stat.max_slots)
98615a45206SAndy Adamson 		xprt->stat.max_slots = numreqs;
987262ca07dSChuck Lever 	xprt->stat.sends++;
988262ca07dSChuck Lever 	xprt->stat.req_u += xprt->stat.sends - xprt->stat.recvs;
989262ca07dSChuck Lever 	xprt->stat.bklog_u += xprt->backlog.qlen;
99015a45206SAndy Adamson 	xprt->stat.sending_u += xprt->sending.qlen;
99115a45206SAndy Adamson 	xprt->stat.pending_u += xprt->pending.qlen;
992262ca07dSChuck Lever 
993fe3aca29SChuck Lever 	/* Don't race with disconnect */
994fe3aca29SChuck Lever 	if (!xprt_connected(xprt))
995fe3aca29SChuck Lever 		task->tk_status = -ENOTCONN;
9960a660521STrond Myklebust 	else {
99755ae1aabSRicardo Labiaga 		/*
99855ae1aabSRicardo Labiaga 		 * Sleep on the pending queue since
99955ae1aabSRicardo Labiaga 		 * we're expecting a reply.
100055ae1aabSRicardo Labiaga 		 */
10010a660521STrond Myklebust 		if (!req->rq_reply_bytes_recvd && rpc_reply_expected(task))
10025d00837bSTrond Myklebust 			rpc_sleep_on(&xprt->pending, task, xprt_timer);
10030a660521STrond Myklebust 		req->rq_connect_cookie = xprt->connect_cookie;
100455ae1aabSRicardo Labiaga 	}
1005fe3aca29SChuck Lever 	spin_unlock_bh(&xprt->transport_lock);
10061da177e4SLinus Torvalds }
10071da177e4SLinus Torvalds 
1008ba60eb25STrond Myklebust static void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task)
1009ba60eb25STrond Myklebust {
1010ba60eb25STrond Myklebust 	set_bit(XPRT_CONGESTED, &xprt->state);
1011ba60eb25STrond Myklebust 	rpc_sleep_on(&xprt->backlog, task, NULL);
1012ba60eb25STrond Myklebust }
1013ba60eb25STrond Myklebust 
1014ba60eb25STrond Myklebust static void xprt_wake_up_backlog(struct rpc_xprt *xprt)
1015ba60eb25STrond Myklebust {
1016ba60eb25STrond Myklebust 	if (rpc_wake_up_next(&xprt->backlog) == NULL)
1017ba60eb25STrond Myklebust 		clear_bit(XPRT_CONGESTED, &xprt->state);
1018ba60eb25STrond Myklebust }
1019ba60eb25STrond Myklebust 
1020ba60eb25STrond Myklebust static bool xprt_throttle_congested(struct rpc_xprt *xprt, struct rpc_task *task)
1021ba60eb25STrond Myklebust {
1022ba60eb25STrond Myklebust 	bool ret = false;
1023ba60eb25STrond Myklebust 
1024ba60eb25STrond Myklebust 	if (!test_bit(XPRT_CONGESTED, &xprt->state))
1025ba60eb25STrond Myklebust 		goto out;
1026ba60eb25STrond Myklebust 	spin_lock(&xprt->reserve_lock);
1027ba60eb25STrond Myklebust 	if (test_bit(XPRT_CONGESTED, &xprt->state)) {
1028ba60eb25STrond Myklebust 		rpc_sleep_on(&xprt->backlog, task, NULL);
1029ba60eb25STrond Myklebust 		ret = true;
1030ba60eb25STrond Myklebust 	}
1031ba60eb25STrond Myklebust 	spin_unlock(&xprt->reserve_lock);
1032ba60eb25STrond Myklebust out:
1033ba60eb25STrond Myklebust 	return ret;
1034ba60eb25STrond Myklebust }
1035ba60eb25STrond Myklebust 
1036d9ba131dSTrond Myklebust static struct rpc_rqst *xprt_dynamic_alloc_slot(struct rpc_xprt *xprt, gfp_t gfp_flags)
1037d9ba131dSTrond Myklebust {
1038d9ba131dSTrond Myklebust 	struct rpc_rqst *req = ERR_PTR(-EAGAIN);
1039d9ba131dSTrond Myklebust 
1040d9ba131dSTrond Myklebust 	if (!atomic_add_unless(&xprt->num_reqs, 1, xprt->max_reqs))
1041d9ba131dSTrond Myklebust 		goto out;
1042d9ba131dSTrond Myklebust 	req = kzalloc(sizeof(struct rpc_rqst), gfp_flags);
1043d9ba131dSTrond Myklebust 	if (req != NULL)
1044d9ba131dSTrond Myklebust 		goto out;
1045d9ba131dSTrond Myklebust 	atomic_dec(&xprt->num_reqs);
1046d9ba131dSTrond Myklebust 	req = ERR_PTR(-ENOMEM);
1047d9ba131dSTrond Myklebust out:
1048d9ba131dSTrond Myklebust 	return req;
1049d9ba131dSTrond Myklebust }
1050d9ba131dSTrond Myklebust 
1051d9ba131dSTrond Myklebust static bool xprt_dynamic_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
1052d9ba131dSTrond Myklebust {
1053d9ba131dSTrond Myklebust 	if (atomic_add_unless(&xprt->num_reqs, -1, xprt->min_reqs)) {
1054d9ba131dSTrond Myklebust 		kfree(req);
1055d9ba131dSTrond Myklebust 		return true;
1056d9ba131dSTrond Myklebust 	}
1057d9ba131dSTrond Myklebust 	return false;
1058d9ba131dSTrond Myklebust }
1059d9ba131dSTrond Myklebust 
1060f39c1bfbSTrond Myklebust void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
10611da177e4SLinus Torvalds {
1062d9ba131dSTrond Myklebust 	struct rpc_rqst *req;
10631da177e4SLinus Torvalds 
1064f39c1bfbSTrond Myklebust 	spin_lock(&xprt->reserve_lock);
10651da177e4SLinus Torvalds 	if (!list_empty(&xprt->free)) {
1066d9ba131dSTrond Myklebust 		req = list_entry(xprt->free.next, struct rpc_rqst, rq_list);
1067d9ba131dSTrond Myklebust 		list_del(&req->rq_list);
1068d9ba131dSTrond Myklebust 		goto out_init_req;
1069d9ba131dSTrond Myklebust 	}
10706b343099SJeff Layton 	req = xprt_dynamic_alloc_slot(xprt, GFP_NOWAIT|__GFP_NOWARN);
1071d9ba131dSTrond Myklebust 	if (!IS_ERR(req))
1072d9ba131dSTrond Myklebust 		goto out_init_req;
1073d9ba131dSTrond Myklebust 	switch (PTR_ERR(req)) {
1074d9ba131dSTrond Myklebust 	case -ENOMEM:
1075d9ba131dSTrond Myklebust 		dprintk("RPC:       dynamic allocation of request slot "
1076d9ba131dSTrond Myklebust 				"failed! Retrying\n");
10771afeaf5cSTrond Myklebust 		task->tk_status = -ENOMEM;
1078d9ba131dSTrond Myklebust 		break;
1079d9ba131dSTrond Myklebust 	case -EAGAIN:
1080ba60eb25STrond Myklebust 		xprt_add_backlog(xprt, task);
1081d9ba131dSTrond Myklebust 		dprintk("RPC:       waiting for request slot\n");
10821afeaf5cSTrond Myklebust 	default:
1083d9ba131dSTrond Myklebust 		task->tk_status = -EAGAIN;
10841afeaf5cSTrond Myklebust 	}
1085f39c1bfbSTrond Myklebust 	spin_unlock(&xprt->reserve_lock);
1086d9ba131dSTrond Myklebust 	return;
1087d9ba131dSTrond Myklebust out_init_req:
1088d9ba131dSTrond Myklebust 	task->tk_status = 0;
10891da177e4SLinus Torvalds 	task->tk_rqstp = req;
10901da177e4SLinus Torvalds 	xprt_request_init(task, xprt);
1091f39c1bfbSTrond Myklebust 	spin_unlock(&xprt->reserve_lock);
10921da177e4SLinus Torvalds }
1093f39c1bfbSTrond Myklebust EXPORT_SYMBOL_GPL(xprt_alloc_slot);
1094f39c1bfbSTrond Myklebust 
1095f39c1bfbSTrond Myklebust void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
1096f39c1bfbSTrond Myklebust {
1097f39c1bfbSTrond Myklebust 	/* Note: grabbing the xprt_lock_write() ensures that we throttle
1098f39c1bfbSTrond Myklebust 	 * new slot allocation if the transport is congested (i.e. when
1099f39c1bfbSTrond Myklebust 	 * reconnecting a stream transport or when out of socket write
1100f39c1bfbSTrond Myklebust 	 * buffer space).
1101f39c1bfbSTrond Myklebust 	 */
1102f39c1bfbSTrond Myklebust 	if (xprt_lock_write(xprt, task)) {
1103f39c1bfbSTrond Myklebust 		xprt_alloc_slot(xprt, task);
1104f39c1bfbSTrond Myklebust 		xprt_release_write(xprt, task);
1105f39c1bfbSTrond Myklebust 	}
1106f39c1bfbSTrond Myklebust }
1107f39c1bfbSTrond Myklebust EXPORT_SYMBOL_GPL(xprt_lock_and_alloc_slot);
11081da177e4SLinus Torvalds 
1109ee5ebe85STrond Myklebust static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
1110ee5ebe85STrond Myklebust {
1111ee5ebe85STrond Myklebust 	spin_lock(&xprt->reserve_lock);
1112c25573b5STrond Myklebust 	if (!xprt_dynamic_free_slot(xprt, req)) {
1113c25573b5STrond Myklebust 		memset(req, 0, sizeof(*req));	/* mark unused */
1114ee5ebe85STrond Myklebust 		list_add(&req->rq_list, &xprt->free);
1115c25573b5STrond Myklebust 	}
1116ba60eb25STrond Myklebust 	xprt_wake_up_backlog(xprt);
1117ee5ebe85STrond Myklebust 	spin_unlock(&xprt->reserve_lock);
1118ee5ebe85STrond Myklebust }
1119ee5ebe85STrond Myklebust 
112021de0a95STrond Myklebust static void xprt_free_all_slots(struct rpc_xprt *xprt)
112121de0a95STrond Myklebust {
112221de0a95STrond Myklebust 	struct rpc_rqst *req;
112321de0a95STrond Myklebust 	while (!list_empty(&xprt->free)) {
112421de0a95STrond Myklebust 		req = list_first_entry(&xprt->free, struct rpc_rqst, rq_list);
112521de0a95STrond Myklebust 		list_del(&req->rq_list);
112621de0a95STrond Myklebust 		kfree(req);
112721de0a95STrond Myklebust 	}
112821de0a95STrond Myklebust }
112921de0a95STrond Myklebust 
1130d9ba131dSTrond Myklebust struct rpc_xprt *xprt_alloc(struct net *net, size_t size,
1131d9ba131dSTrond Myklebust 		unsigned int num_prealloc,
1132d9ba131dSTrond Myklebust 		unsigned int max_alloc)
1133bd1722d4SPavel Emelyanov {
1134bd1722d4SPavel Emelyanov 	struct rpc_xprt *xprt;
113521de0a95STrond Myklebust 	struct rpc_rqst *req;
113621de0a95STrond Myklebust 	int i;
1137bd1722d4SPavel Emelyanov 
1138bd1722d4SPavel Emelyanov 	xprt = kzalloc(size, GFP_KERNEL);
1139bd1722d4SPavel Emelyanov 	if (xprt == NULL)
1140bd1722d4SPavel Emelyanov 		goto out;
1141bd1722d4SPavel Emelyanov 
114221de0a95STrond Myklebust 	xprt_init(xprt, net);
114321de0a95STrond Myklebust 
114421de0a95STrond Myklebust 	for (i = 0; i < num_prealloc; i++) {
114521de0a95STrond Myklebust 		req = kzalloc(sizeof(struct rpc_rqst), GFP_KERNEL);
114621de0a95STrond Myklebust 		if (!req)
11478313164cSwangweidong 			goto out_free;
114821de0a95STrond Myklebust 		list_add(&req->rq_list, &xprt->free);
114921de0a95STrond Myklebust 	}
1150d9ba131dSTrond Myklebust 	if (max_alloc > num_prealloc)
1151d9ba131dSTrond Myklebust 		xprt->max_reqs = max_alloc;
1152d9ba131dSTrond Myklebust 	else
115321de0a95STrond Myklebust 		xprt->max_reqs = num_prealloc;
1154d9ba131dSTrond Myklebust 	xprt->min_reqs = num_prealloc;
1155d9ba131dSTrond Myklebust 	atomic_set(&xprt->num_reqs, num_prealloc);
1156bd1722d4SPavel Emelyanov 
1157bd1722d4SPavel Emelyanov 	return xprt;
1158bd1722d4SPavel Emelyanov 
1159bd1722d4SPavel Emelyanov out_free:
116021de0a95STrond Myklebust 	xprt_free(xprt);
1161bd1722d4SPavel Emelyanov out:
1162bd1722d4SPavel Emelyanov 	return NULL;
1163bd1722d4SPavel Emelyanov }
1164bd1722d4SPavel Emelyanov EXPORT_SYMBOL_GPL(xprt_alloc);
1165bd1722d4SPavel Emelyanov 
1166e204e621SPavel Emelyanov void xprt_free(struct rpc_xprt *xprt)
1167e204e621SPavel Emelyanov {
116837aa2133SPavel Emelyanov 	put_net(xprt->xprt_net);
116921de0a95STrond Myklebust 	xprt_free_all_slots(xprt);
1170fda1bfefSTrond Myklebust 	kfree_rcu(xprt, rcu);
1171e204e621SPavel Emelyanov }
1172e204e621SPavel Emelyanov EXPORT_SYMBOL_GPL(xprt_free);
1173e204e621SPavel Emelyanov 
11749903cd1cSChuck Lever /**
11759903cd1cSChuck Lever  * xprt_reserve - allocate an RPC request slot
11769903cd1cSChuck Lever  * @task: RPC task requesting a slot allocation
11779903cd1cSChuck Lever  *
1178ba60eb25STrond Myklebust  * If the transport is marked as being congested, or if no more
1179ba60eb25STrond Myklebust  * slots are available, place the task on the transport's
11809903cd1cSChuck Lever  * backlog queue.
11819903cd1cSChuck Lever  */
11829903cd1cSChuck Lever void xprt_reserve(struct rpc_task *task)
11831da177e4SLinus Torvalds {
118445bc0dceSTrond Myklebust 	struct rpc_xprt	*xprt;
11851da177e4SLinus Torvalds 
118643cedbf0STrond Myklebust 	task->tk_status = 0;
118743cedbf0STrond Myklebust 	if (task->tk_rqstp != NULL)
118843cedbf0STrond Myklebust 		return;
118943cedbf0STrond Myklebust 
119043cedbf0STrond Myklebust 	task->tk_timeout = 0;
119143cedbf0STrond Myklebust 	task->tk_status = -EAGAIN;
119245bc0dceSTrond Myklebust 	rcu_read_lock();
119345bc0dceSTrond Myklebust 	xprt = rcu_dereference(task->tk_client->cl_xprt);
1194ba60eb25STrond Myklebust 	if (!xprt_throttle_congested(xprt, task))
1195ba60eb25STrond Myklebust 		xprt->ops->alloc_slot(xprt, task);
1196ba60eb25STrond Myklebust 	rcu_read_unlock();
1197ba60eb25STrond Myklebust }
1198ba60eb25STrond Myklebust 
1199ba60eb25STrond Myklebust /**
1200ba60eb25STrond Myklebust  * xprt_retry_reserve - allocate an RPC request slot
1201ba60eb25STrond Myklebust  * @task: RPC task requesting a slot allocation
1202ba60eb25STrond Myklebust  *
1203ba60eb25STrond Myklebust  * If no more slots are available, place the task on the transport's
1204ba60eb25STrond Myklebust  * backlog queue.
1205ba60eb25STrond Myklebust  * Note that the only difference with xprt_reserve is that we now
1206ba60eb25STrond Myklebust  * ignore the value of the XPRT_CONGESTED flag.
1207ba60eb25STrond Myklebust  */
1208ba60eb25STrond Myklebust void xprt_retry_reserve(struct rpc_task *task)
1209ba60eb25STrond Myklebust {
1210ba60eb25STrond Myklebust 	struct rpc_xprt	*xprt;
1211ba60eb25STrond Myklebust 
1212ba60eb25STrond Myklebust 	task->tk_status = 0;
1213ba60eb25STrond Myklebust 	if (task->tk_rqstp != NULL)
1214ba60eb25STrond Myklebust 		return;
1215ba60eb25STrond Myklebust 
1216ba60eb25STrond Myklebust 	task->tk_timeout = 0;
1217ba60eb25STrond Myklebust 	task->tk_status = -EAGAIN;
1218ba60eb25STrond Myklebust 	rcu_read_lock();
1219ba60eb25STrond Myklebust 	xprt = rcu_dereference(task->tk_client->cl_xprt);
1220f39c1bfbSTrond Myklebust 	xprt->ops->alloc_slot(xprt, task);
122145bc0dceSTrond Myklebust 	rcu_read_unlock();
12221da177e4SLinus Torvalds }
12231da177e4SLinus Torvalds 
1224d8ed029dSAlexey Dobriyan static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt)
12251da177e4SLinus Torvalds {
12260eae88f3SEric Dumazet 	return (__force __be32)xprt->xid++;
12271da177e4SLinus Torvalds }
12281da177e4SLinus Torvalds 
12291da177e4SLinus Torvalds static inline void xprt_init_xid(struct rpc_xprt *xprt)
12301da177e4SLinus Torvalds {
123163862b5bSAruna-Hewapathirane 	xprt->xid = prandom_u32();
12321da177e4SLinus Torvalds }
12331da177e4SLinus Torvalds 
12349903cd1cSChuck Lever static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
12351da177e4SLinus Torvalds {
12361da177e4SLinus Torvalds 	struct rpc_rqst	*req = task->tk_rqstp;
12371da177e4SLinus Torvalds 
1238d9ba131dSTrond Myklebust 	INIT_LIST_HEAD(&req->rq_list);
1239ba7392bbSTrond Myklebust 	req->rq_timeout = task->tk_client->cl_timeout->to_initval;
12401da177e4SLinus Torvalds 	req->rq_task	= task;
12411da177e4SLinus Torvalds 	req->rq_xprt    = xprt;
124202107148SChuck Lever 	req->rq_buffer  = NULL;
12431da177e4SLinus Torvalds 	req->rq_xid     = xprt_alloc_xid(xprt);
12440a660521STrond Myklebust 	req->rq_connect_cookie = xprt->connect_cookie - 1;
124592551948STrond Myklebust 	req->rq_bytes_sent = 0;
124692551948STrond Myklebust 	req->rq_snd_buf.len = 0;
124792551948STrond Myklebust 	req->rq_snd_buf.buflen = 0;
124892551948STrond Myklebust 	req->rq_rcv_buf.len = 0;
124992551948STrond Myklebust 	req->rq_rcv_buf.buflen = 0;
1250ead5e1c2SJ. Bruce Fields 	req->rq_release_snd_buf = NULL;
1251da45828eSTrond Myklebust 	xprt_reset_majortimeo(req);
125246121cf7SChuck Lever 	dprintk("RPC: %5u reserved req %p xid %08x\n", task->tk_pid,
12531da177e4SLinus Torvalds 			req, ntohl(req->rq_xid));
12541da177e4SLinus Torvalds }
12551da177e4SLinus Torvalds 
12569903cd1cSChuck Lever /**
12579903cd1cSChuck Lever  * xprt_release - release an RPC request slot
12589903cd1cSChuck Lever  * @task: task which is finished with the slot
12599903cd1cSChuck Lever  *
12601da177e4SLinus Torvalds  */
12619903cd1cSChuck Lever void xprt_release(struct rpc_task *task)
12621da177e4SLinus Torvalds {
126355ae1aabSRicardo Labiaga 	struct rpc_xprt	*xprt;
126487ed5003STrond Myklebust 	struct rpc_rqst	*req = task->tk_rqstp;
12651da177e4SLinus Torvalds 
126687ed5003STrond Myklebust 	if (req == NULL) {
126787ed5003STrond Myklebust 		if (task->tk_client) {
126887ed5003STrond Myklebust 			rcu_read_lock();
126987ed5003STrond Myklebust 			xprt = rcu_dereference(task->tk_client->cl_xprt);
127087ed5003STrond Myklebust 			if (xprt->snd_task == task)
127187ed5003STrond Myklebust 				xprt_release_write(xprt, task);
127287ed5003STrond Myklebust 			rcu_read_unlock();
127387ed5003STrond Myklebust 		}
12741da177e4SLinus Torvalds 		return;
127587ed5003STrond Myklebust 	}
127655ae1aabSRicardo Labiaga 
127755ae1aabSRicardo Labiaga 	xprt = req->rq_xprt;
12780a702195SWeston Andros Adamson 	if (task->tk_ops->rpc_count_stats != NULL)
12790a702195SWeston Andros Adamson 		task->tk_ops->rpc_count_stats(task, task->tk_calldata);
12800a702195SWeston Andros Adamson 	else if (task->tk_client)
12810a702195SWeston Andros Adamson 		rpc_count_iostats(task, task->tk_client->cl_metrics);
12824a0f8c04SChuck Lever 	spin_lock_bh(&xprt->transport_lock);
128349e9a890SChuck Lever 	xprt->ops->release_xprt(xprt, task);
1284a58dd398SChuck Lever 	if (xprt->ops->release_request)
1285a58dd398SChuck Lever 		xprt->ops->release_request(task);
12861da177e4SLinus Torvalds 	if (!list_empty(&req->rq_list))
12871da177e4SLinus Torvalds 		list_del(&req->rq_list);
12881da177e4SLinus Torvalds 	xprt->last_used = jiffies;
12894cfc7e60SRahul Iyer 	if (list_empty(&xprt->recv) && xprt_has_timer(xprt))
1290a246b010SChuck Lever 		mod_timer(&xprt->timer,
129103bf4b70SChuck Lever 				xprt->last_used + xprt->idle_timeout);
12924a0f8c04SChuck Lever 	spin_unlock_bh(&xprt->transport_lock);
1293ee5ebe85STrond Myklebust 	if (req->rq_buffer)
1294c5a4dd8bSChuck Lever 		xprt->ops->buf_free(req->rq_buffer);
12954a068258SChuck Lever 	xprt_inject_disconnect(xprt);
1296a17c2153STrond Myklebust 	if (req->rq_cred != NULL)
1297a17c2153STrond Myklebust 		put_rpccred(req->rq_cred);
12981da177e4SLinus Torvalds 	task->tk_rqstp = NULL;
1299ead5e1c2SJ. Bruce Fields 	if (req->rq_release_snd_buf)
1300ead5e1c2SJ. Bruce Fields 		req->rq_release_snd_buf(req);
130155ae1aabSRicardo Labiaga 
130246121cf7SChuck Lever 	dprintk("RPC: %5u release request %p\n", task->tk_pid, req);
1303ee5ebe85STrond Myklebust 	if (likely(!bc_prealloc(req)))
1304ee5ebe85STrond Myklebust 		xprt_free_slot(xprt, req);
1305ee5ebe85STrond Myklebust 	else
1306c9acb42eSTrond Myklebust 		xprt_free_bc_request(req);
13071da177e4SLinus Torvalds }
13081da177e4SLinus Torvalds 
130921de0a95STrond Myklebust static void xprt_init(struct rpc_xprt *xprt, struct net *net)
1310c2866763SChuck Lever {
131130c5116bSTrond Myklebust 	kref_init(&xprt->kref);
1312c2866763SChuck Lever 
1313c2866763SChuck Lever 	spin_lock_init(&xprt->transport_lock);
1314c2866763SChuck Lever 	spin_lock_init(&xprt->reserve_lock);
1315c2866763SChuck Lever 
1316c2866763SChuck Lever 	INIT_LIST_HEAD(&xprt->free);
1317c2866763SChuck Lever 	INIT_LIST_HEAD(&xprt->recv);
13189e00abc3STrond Myklebust #if defined(CONFIG_SUNRPC_BACKCHANNEL)
1319f9acac1aSRicardo Labiaga 	spin_lock_init(&xprt->bc_pa_lock);
1320f9acac1aSRicardo Labiaga 	INIT_LIST_HEAD(&xprt->bc_pa_list);
13219e00abc3STrond Myklebust #endif /* CONFIG_SUNRPC_BACKCHANNEL */
132280b14d5eSTrond Myklebust 	INIT_LIST_HEAD(&xprt->xprt_switch);
1323f9acac1aSRicardo Labiaga 
1324c2866763SChuck Lever 	xprt->last_used = jiffies;
1325c2866763SChuck Lever 	xprt->cwnd = RPC_INITCWND;
1326a509050bSChuck Lever 	xprt->bind_index = 0;
1327c2866763SChuck Lever 
1328c2866763SChuck Lever 	rpc_init_wait_queue(&xprt->binding, "xprt_binding");
1329c2866763SChuck Lever 	rpc_init_wait_queue(&xprt->pending, "xprt_pending");
133034006ceeSTrond Myklebust 	rpc_init_priority_wait_queue(&xprt->sending, "xprt_sending");
1331c2866763SChuck Lever 	rpc_init_priority_wait_queue(&xprt->backlog, "xprt_backlog");
1332c2866763SChuck Lever 
1333c2866763SChuck Lever 	xprt_init_xid(xprt);
1334c2866763SChuck Lever 
133521de0a95STrond Myklebust 	xprt->xprt_net = get_net(net);
13368d9266ffSTrond Myklebust }
13378d9266ffSTrond Myklebust 
13388d9266ffSTrond Myklebust /**
13398d9266ffSTrond Myklebust  * xprt_create_transport - create an RPC transport
13408d9266ffSTrond Myklebust  * @args: rpc transport creation arguments
13418d9266ffSTrond Myklebust  *
13428d9266ffSTrond Myklebust  */
13438d9266ffSTrond Myklebust struct rpc_xprt *xprt_create_transport(struct xprt_create *args)
13448d9266ffSTrond Myklebust {
13458d9266ffSTrond Myklebust 	struct rpc_xprt	*xprt;
13468d9266ffSTrond Myklebust 	struct xprt_class *t;
13478d9266ffSTrond Myklebust 
13488d9266ffSTrond Myklebust 	spin_lock(&xprt_list_lock);
13498d9266ffSTrond Myklebust 	list_for_each_entry(t, &xprt_list, list) {
13508d9266ffSTrond Myklebust 		if (t->ident == args->ident) {
13518d9266ffSTrond Myklebust 			spin_unlock(&xprt_list_lock);
13528d9266ffSTrond Myklebust 			goto found;
13538d9266ffSTrond Myklebust 		}
13548d9266ffSTrond Myklebust 	}
13558d9266ffSTrond Myklebust 	spin_unlock(&xprt_list_lock);
13563c45ddf8SChuck Lever 	dprintk("RPC: transport (%d) not supported\n", args->ident);
13578d9266ffSTrond Myklebust 	return ERR_PTR(-EIO);
13588d9266ffSTrond Myklebust 
13598d9266ffSTrond Myklebust found:
13608d9266ffSTrond Myklebust 	xprt = t->setup(args);
13618d9266ffSTrond Myklebust 	if (IS_ERR(xprt)) {
13628d9266ffSTrond Myklebust 		dprintk("RPC:       xprt_create_transport: failed, %ld\n",
13638d9266ffSTrond Myklebust 				-PTR_ERR(xprt));
136421de0a95STrond Myklebust 		goto out;
13658d9266ffSTrond Myklebust 	}
136633d90ac0SJ. Bruce Fields 	if (args->flags & XPRT_CREATE_NO_IDLE_TIMEOUT)
136733d90ac0SJ. Bruce Fields 		xprt->idle_timeout = 0;
136821de0a95STrond Myklebust 	INIT_WORK(&xprt->task_cleanup, xprt_autoclose);
136921de0a95STrond Myklebust 	if (xprt_has_timer(xprt))
137021de0a95STrond Myklebust 		setup_timer(&xprt->timer, xprt_init_autodisconnect,
137121de0a95STrond Myklebust 			    (unsigned long)xprt);
137221de0a95STrond Myklebust 	else
137321de0a95STrond Myklebust 		init_timer(&xprt->timer);
13744e0038b6STrond Myklebust 
13754e0038b6STrond Myklebust 	if (strlen(args->servername) > RPC_MAXNETNAMELEN) {
13764e0038b6STrond Myklebust 		xprt_destroy(xprt);
13774e0038b6STrond Myklebust 		return ERR_PTR(-EINVAL);
13784e0038b6STrond Myklebust 	}
13794e0038b6STrond Myklebust 	xprt->servername = kstrdup(args->servername, GFP_KERNEL);
13804e0038b6STrond Myklebust 	if (xprt->servername == NULL) {
13814e0038b6STrond Myklebust 		xprt_destroy(xprt);
13824e0038b6STrond Myklebust 		return ERR_PTR(-ENOMEM);
13834e0038b6STrond Myklebust 	}
13844e0038b6STrond Myklebust 
13853f940098SJeff Layton 	rpc_xprt_debugfs_register(xprt);
1386388f0c77SJeff Layton 
1387c2866763SChuck Lever 	dprintk("RPC:       created transport %p with %u slots\n", xprt,
1388c2866763SChuck Lever 			xprt->max_reqs);
138921de0a95STrond Myklebust out:
1390c2866763SChuck Lever 	return xprt;
1391c2866763SChuck Lever }
1392c2866763SChuck Lever 
13939903cd1cSChuck Lever /**
13949903cd1cSChuck Lever  * xprt_destroy - destroy an RPC transport, killing off all requests.
1395a8de240aSTrond Myklebust  * @xprt: transport to destroy
13969903cd1cSChuck Lever  *
13971da177e4SLinus Torvalds  */
1398a8de240aSTrond Myklebust static void xprt_destroy(struct rpc_xprt *xprt)
13991da177e4SLinus Torvalds {
14001da177e4SLinus Torvalds 	dprintk("RPC:       destroying transport %p\n", xprt);
140179234c3dSTrond Myklebust 
140279234c3dSTrond Myklebust 	/* Exclude transport connect/disconnect handlers */
140379234c3dSTrond Myklebust 	wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_UNINTERRUPTIBLE);
140479234c3dSTrond Myklebust 
14050065db32STrond Myklebust 	del_timer_sync(&xprt->timer);
1406c8541ecdSChuck Lever 
1407388f0c77SJeff Layton 	rpc_xprt_debugfs_unregister(xprt);
1408f6a1cc89STrond Myklebust 	rpc_destroy_wait_queue(&xprt->binding);
1409f6a1cc89STrond Myklebust 	rpc_destroy_wait_queue(&xprt->pending);
1410f6a1cc89STrond Myklebust 	rpc_destroy_wait_queue(&xprt->sending);
1411f6a1cc89STrond Myklebust 	rpc_destroy_wait_queue(&xprt->backlog);
1412c3ae62aeSJ. Bruce Fields 	cancel_work_sync(&xprt->task_cleanup);
14134e0038b6STrond Myklebust 	kfree(xprt->servername);
1414c8541ecdSChuck Lever 	/*
1415c8541ecdSChuck Lever 	 * Tear down transport state and free the rpc_xprt
1416c8541ecdSChuck Lever 	 */
1417a246b010SChuck Lever 	xprt->ops->destroy(xprt);
14186b6ca86bSTrond Myklebust }
14191da177e4SLinus Torvalds 
142030c5116bSTrond Myklebust static void xprt_destroy_kref(struct kref *kref)
142130c5116bSTrond Myklebust {
142230c5116bSTrond Myklebust 	xprt_destroy(container_of(kref, struct rpc_xprt, kref));
142330c5116bSTrond Myklebust }
142430c5116bSTrond Myklebust 
142530c5116bSTrond Myklebust /**
142630c5116bSTrond Myklebust  * xprt_get - return a reference to an RPC transport.
142730c5116bSTrond Myklebust  * @xprt: pointer to the transport
142830c5116bSTrond Myklebust  *
142930c5116bSTrond Myklebust  */
143030c5116bSTrond Myklebust struct rpc_xprt *xprt_get(struct rpc_xprt *xprt)
143130c5116bSTrond Myklebust {
143230c5116bSTrond Myklebust 	if (xprt != NULL && kref_get_unless_zero(&xprt->kref))
143330c5116bSTrond Myklebust 		return xprt;
143430c5116bSTrond Myklebust 	return NULL;
143530c5116bSTrond Myklebust }
143630c5116bSTrond Myklebust EXPORT_SYMBOL_GPL(xprt_get);
143730c5116bSTrond Myklebust 
14386b6ca86bSTrond Myklebust /**
14396b6ca86bSTrond Myklebust  * xprt_put - release a reference to an RPC transport.
14406b6ca86bSTrond Myklebust  * @xprt: pointer to the transport
14416b6ca86bSTrond Myklebust  *
14426b6ca86bSTrond Myklebust  */
14436b6ca86bSTrond Myklebust void xprt_put(struct rpc_xprt *xprt)
14446b6ca86bSTrond Myklebust {
144530c5116bSTrond Myklebust 	if (xprt != NULL)
144630c5116bSTrond Myklebust 		kref_put(&xprt->kref, xprt_destroy_kref);
14476b6ca86bSTrond Myklebust }
14485d252f90SChuck Lever EXPORT_SYMBOL_GPL(xprt_put);
1449