xref: /freebsd/sys/rpc/clnt_dg.c (revision a9148abd9da5db2f1c682fb17bed791845fc41c9)
1dfdcada3SDoug Rabson /*	$NetBSD: clnt_dg.c,v 1.4 2000/07/14 08:40:41 fvdl Exp $	*/
2dfdcada3SDoug Rabson 
3dfdcada3SDoug Rabson /*
4dfdcada3SDoug Rabson  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5dfdcada3SDoug Rabson  * unrestricted use provided that this legend is included on all tape
6dfdcada3SDoug Rabson  * media and as a part of the software program in whole or part.  Users
7dfdcada3SDoug Rabson  * may copy or modify Sun RPC without charge, but are not authorized
8dfdcada3SDoug Rabson  * to license or distribute it to anyone else except as part of a product or
9dfdcada3SDoug Rabson  * program developed by the user.
10dfdcada3SDoug Rabson  *
11dfdcada3SDoug Rabson  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12dfdcada3SDoug Rabson  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13dfdcada3SDoug Rabson  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14dfdcada3SDoug Rabson  *
15dfdcada3SDoug Rabson  * Sun RPC is provided with no support and without any obligation on the
16dfdcada3SDoug Rabson  * part of Sun Microsystems, Inc. to assist in its use, correction,
17dfdcada3SDoug Rabson  * modification or enhancement.
18dfdcada3SDoug Rabson  *
19dfdcada3SDoug Rabson  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20dfdcada3SDoug Rabson  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21dfdcada3SDoug Rabson  * OR ANY PART THEREOF.
22dfdcada3SDoug Rabson  *
23dfdcada3SDoug Rabson  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24dfdcada3SDoug Rabson  * or profits or other special, indirect and consequential damages, even if
25dfdcada3SDoug Rabson  * Sun has been advised of the possibility of such damages.
26dfdcada3SDoug Rabson  *
27dfdcada3SDoug Rabson  * Sun Microsystems, Inc.
28dfdcada3SDoug Rabson  * 2550 Garcia Avenue
29dfdcada3SDoug Rabson  * Mountain View, California  94043
30dfdcada3SDoug Rabson  */
31dfdcada3SDoug Rabson /*
32dfdcada3SDoug Rabson  * Copyright (c) 1986-1991 by Sun Microsystems Inc.
33dfdcada3SDoug Rabson  */
34dfdcada3SDoug Rabson 
35dfdcada3SDoug Rabson #if defined(LIBC_SCCS) && !defined(lint)
36dfdcada3SDoug Rabson #ident	"@(#)clnt_dg.c	1.23	94/04/22 SMI"
37dfdcada3SDoug Rabson static char sccsid[] = "@(#)clnt_dg.c 1.19 89/03/16 Copyr 1988 Sun Micro";
38dfdcada3SDoug Rabson #endif
39dfdcada3SDoug Rabson #include <sys/cdefs.h>
40dfdcada3SDoug Rabson __FBSDID("$FreeBSD$");
41dfdcada3SDoug Rabson 
42dfdcada3SDoug Rabson /*
43dfdcada3SDoug Rabson  * Implements a connectionless client side RPC.
44dfdcada3SDoug Rabson  */
45dfdcada3SDoug Rabson 
46dfdcada3SDoug Rabson #include <sys/param.h>
47dfdcada3SDoug Rabson #include <sys/systm.h>
48c675522fSDoug Rabson #include <sys/kernel.h>
49dfdcada3SDoug Rabson #include <sys/lock.h>
50dfdcada3SDoug Rabson #include <sys/malloc.h>
51dfdcada3SDoug Rabson #include <sys/mbuf.h>
52dfdcada3SDoug Rabson #include <sys/mutex.h>
53dfdcada3SDoug Rabson #include <sys/pcpu.h>
54dfdcada3SDoug Rabson #include <sys/proc.h>
55dfdcada3SDoug Rabson #include <sys/socket.h>
56dfdcada3SDoug Rabson #include <sys/socketvar.h>
57dfdcada3SDoug Rabson #include <sys/time.h>
58dfdcada3SDoug Rabson #include <sys/uio.h>
59dfdcada3SDoug Rabson 
60dfdcada3SDoug Rabson #include <rpc/rpc.h>
61ee31b83aSDoug Rabson #include <rpc/rpc_com.h>
62dfdcada3SDoug Rabson 
63dfdcada3SDoug Rabson 
64dfdcada3SDoug Rabson #ifdef _FREEFALL_CONFIG
65dfdcada3SDoug Rabson /*
66dfdcada3SDoug Rabson  * Disable RPC exponential back-off for FreeBSD.org systems.
67dfdcada3SDoug Rabson  */
68dfdcada3SDoug Rabson #define	RPC_MAX_BACKOFF		1 /* second */
69dfdcada3SDoug Rabson #else
70dfdcada3SDoug Rabson #define	RPC_MAX_BACKOFF		30 /* seconds */
71dfdcada3SDoug Rabson #endif
72dfdcada3SDoug Rabson 
73dfdcada3SDoug Rabson static bool_t time_not_ok(struct timeval *);
74c675522fSDoug Rabson static enum clnt_stat clnt_dg_call(CLIENT *, struct rpc_callextra *,
75a9148abdSDoug Rabson     rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
76dfdcada3SDoug Rabson static void clnt_dg_geterr(CLIENT *, struct rpc_err *);
77dfdcada3SDoug Rabson static bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *);
78dfdcada3SDoug Rabson static void clnt_dg_abort(CLIENT *);
79dfdcada3SDoug Rabson static bool_t clnt_dg_control(CLIENT *, u_int, void *);
80a9148abdSDoug Rabson static void clnt_dg_close(CLIENT *);
81dfdcada3SDoug Rabson static void clnt_dg_destroy(CLIENT *);
82dfdcada3SDoug Rabson static void clnt_dg_soupcall(struct socket *so, void *arg, int waitflag);
83dfdcada3SDoug Rabson 
84dfdcada3SDoug Rabson static struct clnt_ops clnt_dg_ops = {
85dfdcada3SDoug Rabson 	.cl_call =	clnt_dg_call,
86dfdcada3SDoug Rabson 	.cl_abort =	clnt_dg_abort,
87dfdcada3SDoug Rabson 	.cl_geterr =	clnt_dg_geterr,
88dfdcada3SDoug Rabson 	.cl_freeres =	clnt_dg_freeres,
89a9148abdSDoug Rabson 	.cl_close =	clnt_dg_close,
90dfdcada3SDoug Rabson 	.cl_destroy =	clnt_dg_destroy,
91dfdcada3SDoug Rabson 	.cl_control =	clnt_dg_control
92dfdcada3SDoug Rabson };
93dfdcada3SDoug Rabson 
94dfdcada3SDoug Rabson static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
95dfdcada3SDoug Rabson 
96dfdcada3SDoug Rabson /*
97c675522fSDoug Rabson  * A pending RPC request which awaits a reply. Requests which have
98c675522fSDoug Rabson  * received their reply will have cr_xid set to zero and cr_mrep to
99c675522fSDoug Rabson  * the mbuf chain of the reply.
100dfdcada3SDoug Rabson  */
101dfdcada3SDoug Rabson struct cu_request {
102dfdcada3SDoug Rabson 	TAILQ_ENTRY(cu_request) cr_link;
103c675522fSDoug Rabson 	CLIENT			*cr_client;	/* owner */
104dfdcada3SDoug Rabson 	uint32_t		cr_xid;		/* XID of request */
105dfdcada3SDoug Rabson 	struct mbuf		*cr_mrep;	/* reply received by upcall */
106dfdcada3SDoug Rabson 	int			cr_error;	/* any error from upcall */
107a9148abdSDoug Rabson 	char			cr_verf[MAX_AUTH_BYTES]; /* reply verf */
108dfdcada3SDoug Rabson };
109dfdcada3SDoug Rabson 
110dfdcada3SDoug Rabson TAILQ_HEAD(cu_request_list, cu_request);
111dfdcada3SDoug Rabson 
112dfdcada3SDoug Rabson #define MCALL_MSG_SIZE 24
113dfdcada3SDoug Rabson 
114dfdcada3SDoug Rabson /*
115dfdcada3SDoug Rabson  * This structure is pointed to by the socket's so_upcallarg
116dfdcada3SDoug Rabson  * member. It is separate from the client private data to facilitate
117dfdcada3SDoug Rabson  * multiple clients sharing the same socket. The cs_lock mutex is used
118dfdcada3SDoug Rabson  * to protect all fields of this structure, the socket's receive
119dfdcada3SDoug Rabson  * buffer SOCKBUF_LOCK is used to ensure that exactly one of these
120dfdcada3SDoug Rabson  * structures is installed on the socket.
121dfdcada3SDoug Rabson  */
122dfdcada3SDoug Rabson struct cu_socket {
123dfdcada3SDoug Rabson 	struct mtx		cs_lock;
124dfdcada3SDoug Rabson 	int			cs_refs;	/* Count of clients */
125dfdcada3SDoug Rabson 	struct cu_request_list	cs_pending;	/* Requests awaiting replies */
126dfdcada3SDoug Rabson };
127dfdcada3SDoug Rabson 
128dfdcada3SDoug Rabson /*
129dfdcada3SDoug Rabson  * Private data kept per client handle
130dfdcada3SDoug Rabson  */
131dfdcada3SDoug Rabson struct cu_data {
132c675522fSDoug Rabson 	int			cu_threads;	/* # threads in clnt_vc_call */
133a9148abdSDoug Rabson 	bool_t			cu_closing;	/* TRUE if we are closing */
134a9148abdSDoug Rabson 	bool_t			cu_closed;	/* TRUE if we are closed */
135dfdcada3SDoug Rabson 	struct socket		*cu_socket;	/* connection socket */
136dfdcada3SDoug Rabson 	bool_t			cu_closeit;	/* opened by library */
137dfdcada3SDoug Rabson 	struct sockaddr_storage	cu_raddr;	/* remote address */
138dfdcada3SDoug Rabson 	int			cu_rlen;
139dfdcada3SDoug Rabson 	struct timeval		cu_wait;	/* retransmit interval */
140dfdcada3SDoug Rabson 	struct timeval		cu_total;	/* total time for the call */
141dfdcada3SDoug Rabson 	struct rpc_err		cu_error;
142dfdcada3SDoug Rabson 	uint32_t		cu_xid;
143dfdcada3SDoug Rabson 	char			cu_mcallc[MCALL_MSG_SIZE]; /* marshalled callmsg */
144dfdcada3SDoug Rabson 	size_t			cu_mcalllen;
145dfdcada3SDoug Rabson 	size_t			cu_sendsz;	/* send size */
146dfdcada3SDoug Rabson 	size_t			cu_recvsz;	/* recv size */
147dfdcada3SDoug Rabson 	int			cu_async;
148dfdcada3SDoug Rabson 	int			cu_connect;	/* Use connect(). */
149dfdcada3SDoug Rabson 	int			cu_connected;	/* Have done connect(). */
150dfdcada3SDoug Rabson 	const char		*cu_waitchan;
151dfdcada3SDoug Rabson 	int			cu_waitflag;
152a9148abdSDoug Rabson 	int			cu_cwnd;	/* congestion window */
153a9148abdSDoug Rabson 	int			cu_sent;	/* number of in-flight RPCs */
154a9148abdSDoug Rabson 	bool_t			cu_cwnd_wait;
155dfdcada3SDoug Rabson };
156dfdcada3SDoug Rabson 
157a9148abdSDoug Rabson #define CWNDSCALE	256
158a9148abdSDoug Rabson #define MAXCWND		(32 * CWNDSCALE)
159a9148abdSDoug Rabson 
160dfdcada3SDoug Rabson /*
161dfdcada3SDoug Rabson  * Connection less client creation returns with client handle parameters.
162dfdcada3SDoug Rabson  * Default options are set, which the user can change using clnt_control().
163dfdcada3SDoug Rabson  * fd should be open and bound.
164dfdcada3SDoug Rabson  * NB: The rpch->cl_auth is initialized to null authentication.
165dfdcada3SDoug Rabson  * 	Caller may wish to set this something more useful.
166dfdcada3SDoug Rabson  *
167dfdcada3SDoug Rabson  * sendsz and recvsz are the maximum allowable packet sizes that can be
168dfdcada3SDoug Rabson  * sent and received. Normally they are the same, but they can be
169dfdcada3SDoug Rabson  * changed to improve the program efficiency and buffer allocation.
170dfdcada3SDoug Rabson  * If they are 0, use the transport default.
171dfdcada3SDoug Rabson  *
172dfdcada3SDoug Rabson  * If svcaddr is NULL, returns NULL.
173dfdcada3SDoug Rabson  */
174dfdcada3SDoug Rabson CLIENT *
175dfdcada3SDoug Rabson clnt_dg_create(
176dfdcada3SDoug Rabson 	struct socket *so,
177dfdcada3SDoug Rabson 	struct sockaddr *svcaddr,	/* servers address */
178dfdcada3SDoug Rabson 	rpcprog_t program,		/* program number */
179dfdcada3SDoug Rabson 	rpcvers_t version,		/* version number */
180dfdcada3SDoug Rabson 	size_t sendsz,			/* buffer recv size */
181dfdcada3SDoug Rabson 	size_t recvsz)			/* buffer send size */
182dfdcada3SDoug Rabson {
183dfdcada3SDoug Rabson 	CLIENT *cl = NULL;		/* client handle */
184dfdcada3SDoug Rabson 	struct cu_data *cu = NULL;	/* private data */
185dfdcada3SDoug Rabson 	struct cu_socket *cs = NULL;
186dfdcada3SDoug Rabson 	struct timeval now;
187dfdcada3SDoug Rabson 	struct rpc_msg call_msg;
188dfdcada3SDoug Rabson 	struct __rpc_sockinfo si;
189dfdcada3SDoug Rabson 	XDR xdrs;
190dfdcada3SDoug Rabson 
191dfdcada3SDoug Rabson 	if (svcaddr == NULL) {
192dfdcada3SDoug Rabson 		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
193dfdcada3SDoug Rabson 		return (NULL);
194dfdcada3SDoug Rabson 	}
195dfdcada3SDoug Rabson 
196dfdcada3SDoug Rabson 	if (!__rpc_socket2sockinfo(so, &si)) {
197dfdcada3SDoug Rabson 		rpc_createerr.cf_stat = RPC_TLIERROR;
198dfdcada3SDoug Rabson 		rpc_createerr.cf_error.re_errno = 0;
199dfdcada3SDoug Rabson 		return (NULL);
200dfdcada3SDoug Rabson 	}
201dfdcada3SDoug Rabson 
202dfdcada3SDoug Rabson 	/*
203dfdcada3SDoug Rabson 	 * Find the receive and the send size
204dfdcada3SDoug Rabson 	 */
205dfdcada3SDoug Rabson 	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
206dfdcada3SDoug Rabson 	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
207dfdcada3SDoug Rabson 	if ((sendsz == 0) || (recvsz == 0)) {
208dfdcada3SDoug Rabson 		rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */
209dfdcada3SDoug Rabson 		rpc_createerr.cf_error.re_errno = 0;
210dfdcada3SDoug Rabson 		return (NULL);
211dfdcada3SDoug Rabson 	}
212dfdcada3SDoug Rabson 
213dfdcada3SDoug Rabson 	cl = mem_alloc(sizeof (CLIENT));
214dfdcada3SDoug Rabson 
215dfdcada3SDoug Rabson 	/*
216dfdcada3SDoug Rabson 	 * Should be multiple of 4 for XDR.
217dfdcada3SDoug Rabson 	 */
218dfdcada3SDoug Rabson 	sendsz = ((sendsz + 3) / 4) * 4;
219dfdcada3SDoug Rabson 	recvsz = ((recvsz + 3) / 4) * 4;
220dfdcada3SDoug Rabson 	cu = mem_alloc(sizeof (*cu));
221c675522fSDoug Rabson 	cu->cu_threads = 0;
222c675522fSDoug Rabson 	cu->cu_closing = FALSE;
223a9148abdSDoug Rabson 	cu->cu_closed = FALSE;
224dfdcada3SDoug Rabson 	(void) memcpy(&cu->cu_raddr, svcaddr, (size_t)svcaddr->sa_len);
225dfdcada3SDoug Rabson 	cu->cu_rlen = svcaddr->sa_len;
226dfdcada3SDoug Rabson 	/* Other values can also be set through clnt_control() */
227c675522fSDoug Rabson 	cu->cu_wait.tv_sec = 3;	/* heuristically chosen */
228dfdcada3SDoug Rabson 	cu->cu_wait.tv_usec = 0;
229dfdcada3SDoug Rabson 	cu->cu_total.tv_sec = -1;
230dfdcada3SDoug Rabson 	cu->cu_total.tv_usec = -1;
231dfdcada3SDoug Rabson 	cu->cu_sendsz = sendsz;
232dfdcada3SDoug Rabson 	cu->cu_recvsz = recvsz;
233dfdcada3SDoug Rabson 	cu->cu_async = FALSE;
234dfdcada3SDoug Rabson 	cu->cu_connect = FALSE;
235dfdcada3SDoug Rabson 	cu->cu_connected = FALSE;
236dfdcada3SDoug Rabson 	cu->cu_waitchan = "rpcrecv";
237dfdcada3SDoug Rabson 	cu->cu_waitflag = 0;
238a9148abdSDoug Rabson 	cu->cu_cwnd = MAXCWND / 2;
239a9148abdSDoug Rabson 	cu->cu_sent = 0;
240a9148abdSDoug Rabson 	cu->cu_cwnd_wait = FALSE;
241dfdcada3SDoug Rabson 	(void) getmicrotime(&now);
242dfdcada3SDoug Rabson 	cu->cu_xid = __RPC_GETXID(&now);
243dfdcada3SDoug Rabson 	call_msg.rm_xid = cu->cu_xid;
244dfdcada3SDoug Rabson 	call_msg.rm_call.cb_prog = program;
245dfdcada3SDoug Rabson 	call_msg.rm_call.cb_vers = version;
246dfdcada3SDoug Rabson 	xdrmem_create(&xdrs, cu->cu_mcallc, MCALL_MSG_SIZE, XDR_ENCODE);
247dfdcada3SDoug Rabson 	if (! xdr_callhdr(&xdrs, &call_msg)) {
248dfdcada3SDoug Rabson 		rpc_createerr.cf_stat = RPC_CANTENCODEARGS;  /* XXX */
249dfdcada3SDoug Rabson 		rpc_createerr.cf_error.re_errno = 0;
250dfdcada3SDoug Rabson 		goto err2;
251dfdcada3SDoug Rabson 	}
252dfdcada3SDoug Rabson 	cu->cu_mcalllen = XDR_GETPOS(&xdrs);;
253dfdcada3SDoug Rabson 
254dfdcada3SDoug Rabson 	/*
255dfdcada3SDoug Rabson 	 * By default, closeit is always FALSE. It is users responsibility
256dfdcada3SDoug Rabson 	 * to do a close on it, else the user may use clnt_control
257dfdcada3SDoug Rabson 	 * to let clnt_destroy do it for him/her.
258dfdcada3SDoug Rabson 	 */
259dfdcada3SDoug Rabson 	cu->cu_closeit = FALSE;
260dfdcada3SDoug Rabson 	cu->cu_socket = so;
261c675522fSDoug Rabson 	soreserve(so, 256*1024, 256*1024);
262dfdcada3SDoug Rabson 
263dfdcada3SDoug Rabson 	SOCKBUF_LOCK(&so->so_rcv);
264dfdcada3SDoug Rabson recheck_socket:
265dfdcada3SDoug Rabson 	if (so->so_upcall) {
266dfdcada3SDoug Rabson 		if (so->so_upcall != clnt_dg_soupcall) {
267dfdcada3SDoug Rabson 			SOCKBUF_UNLOCK(&so->so_rcv);
268dfdcada3SDoug Rabson 			printf("clnt_dg_create(): socket already has an incompatible upcall\n");
269dfdcada3SDoug Rabson 			goto err2;
270dfdcada3SDoug Rabson 		}
271dfdcada3SDoug Rabson 		cs = (struct cu_socket *) so->so_upcallarg;
272dfdcada3SDoug Rabson 		mtx_lock(&cs->cs_lock);
273dfdcada3SDoug Rabson 		cs->cs_refs++;
274dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
275dfdcada3SDoug Rabson 	} else {
276dfdcada3SDoug Rabson 		/*
277dfdcada3SDoug Rabson 		 * We are the first on this socket - allocate the
278dfdcada3SDoug Rabson 		 * structure and install it in the socket.
279dfdcada3SDoug Rabson 		 */
280dfdcada3SDoug Rabson 		SOCKBUF_UNLOCK(&cu->cu_socket->so_rcv);
281dfdcada3SDoug Rabson 		cs = mem_alloc(sizeof(*cs));
282dfdcada3SDoug Rabson 		SOCKBUF_LOCK(&cu->cu_socket->so_rcv);
283dfdcada3SDoug Rabson 		if (so->so_upcall) {
284dfdcada3SDoug Rabson 			/*
285dfdcada3SDoug Rabson 			 * We have lost a race with some other client.
286dfdcada3SDoug Rabson 			 */
287dfdcada3SDoug Rabson 			mem_free(cs, sizeof(*cs));
288dfdcada3SDoug Rabson 			goto recheck_socket;
289dfdcada3SDoug Rabson 		}
290dfdcada3SDoug Rabson 		mtx_init(&cs->cs_lock, "cs->cs_lock", NULL, MTX_DEF);
291dfdcada3SDoug Rabson 		cs->cs_refs = 1;
292dfdcada3SDoug Rabson 		TAILQ_INIT(&cs->cs_pending);
293dfdcada3SDoug Rabson 		so->so_upcallarg = cs;
294dfdcada3SDoug Rabson 		so->so_upcall = clnt_dg_soupcall;
295dfdcada3SDoug Rabson 		so->so_rcv.sb_flags |= SB_UPCALL;
296dfdcada3SDoug Rabson 	}
297dfdcada3SDoug Rabson 	SOCKBUF_UNLOCK(&so->so_rcv);
298dfdcada3SDoug Rabson 
299c675522fSDoug Rabson 	cl->cl_refs = 1;
300dfdcada3SDoug Rabson 	cl->cl_ops = &clnt_dg_ops;
301dfdcada3SDoug Rabson 	cl->cl_private = (caddr_t)(void *)cu;
302dfdcada3SDoug Rabson 	cl->cl_auth = authnone_create();
303dfdcada3SDoug Rabson 	cl->cl_tp = NULL;
304dfdcada3SDoug Rabson 	cl->cl_netid = NULL;
305dfdcada3SDoug Rabson 	return (cl);
306dfdcada3SDoug Rabson err2:
307dfdcada3SDoug Rabson 	if (cl) {
308dfdcada3SDoug Rabson 		mem_free(cl, sizeof (CLIENT));
309dfdcada3SDoug Rabson 		if (cu)
310dfdcada3SDoug Rabson 			mem_free(cu, sizeof (*cu));
311dfdcada3SDoug Rabson 	}
312dfdcada3SDoug Rabson 	return (NULL);
313dfdcada3SDoug Rabson }
314dfdcada3SDoug Rabson 
315dfdcada3SDoug Rabson static enum clnt_stat
316dfdcada3SDoug Rabson clnt_dg_call(
317dfdcada3SDoug Rabson 	CLIENT		*cl,		/* client handle */
318c675522fSDoug Rabson 	struct rpc_callextra *ext,	/* call metadata */
319dfdcada3SDoug Rabson 	rpcproc_t	proc,		/* procedure number */
320a9148abdSDoug Rabson 	struct mbuf	*args,		/* pointer to args */
321a9148abdSDoug Rabson 	struct mbuf	**resultsp,	/* pointer to results */
322dfdcada3SDoug Rabson 	struct timeval	utimeout)	/* seconds to wait before giving up */
323dfdcada3SDoug Rabson {
324dfdcada3SDoug Rabson 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
325dfdcada3SDoug Rabson 	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
326a9148abdSDoug Rabson 	struct rpc_timers *rt;
327c675522fSDoug Rabson 	AUTH *auth;
328a9148abdSDoug Rabson 	struct rpc_err *errp;
329a9148abdSDoug Rabson 	enum clnt_stat stat;
330dfdcada3SDoug Rabson 	XDR xdrs;
331dfdcada3SDoug Rabson 	struct rpc_msg reply_msg;
332dfdcada3SDoug Rabson 	bool_t ok;
333c675522fSDoug Rabson 	int retrans;			/* number of re-transmits so far */
334dfdcada3SDoug Rabson 	int nrefreshes = 2;		/* number of times to refresh cred */
335c675522fSDoug Rabson 	struct timeval *tvp;
336c675522fSDoug Rabson 	int timeout;
337c675522fSDoug Rabson 	int retransmit_time;
338a9148abdSDoug Rabson 	int next_sendtime, starttime, rtt, time_waited, tv = 0;
339dfdcada3SDoug Rabson 	struct sockaddr *sa;
340dfdcada3SDoug Rabson 	socklen_t salen;
341a9148abdSDoug Rabson 	uint32_t xid = 0;
342a9148abdSDoug Rabson 	struct mbuf *mreq = NULL, *results;
343c675522fSDoug Rabson 	struct cu_request *cr;
344dfdcada3SDoug Rabson 	int error;
345dfdcada3SDoug Rabson 
346c675522fSDoug Rabson 	cr = malloc(sizeof(struct cu_request), M_RPC, M_WAITOK);
347c675522fSDoug Rabson 
348dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
349dfdcada3SDoug Rabson 
350a9148abdSDoug Rabson 	if (cu->cu_closing || cu->cu_closed) {
351c675522fSDoug Rabson 		mtx_unlock(&cs->cs_lock);
352c675522fSDoug Rabson 		free(cr, M_RPC);
353c675522fSDoug Rabson 		return (RPC_CANTSEND);
354c675522fSDoug Rabson 	}
355c675522fSDoug Rabson 	cu->cu_threads++;
356c675522fSDoug Rabson 
357a9148abdSDoug Rabson 	if (ext) {
358c675522fSDoug Rabson 		auth = ext->rc_auth;
359a9148abdSDoug Rabson 		errp = &ext->rc_err;
360a9148abdSDoug Rabson 	} else {
361c675522fSDoug Rabson 		auth = cl->cl_auth;
362a9148abdSDoug Rabson 		errp = &cu->cu_error;
363a9148abdSDoug Rabson 	}
364c675522fSDoug Rabson 
365c675522fSDoug Rabson 	cr->cr_client = cl;
366c675522fSDoug Rabson 	cr->cr_mrep = NULL;
367c675522fSDoug Rabson 	cr->cr_error = 0;
368dfdcada3SDoug Rabson 
369dfdcada3SDoug Rabson 	if (cu->cu_total.tv_usec == -1) {
370c675522fSDoug Rabson 		tvp = &utimeout; /* use supplied timeout */
371dfdcada3SDoug Rabson 	} else {
372c675522fSDoug Rabson 		tvp = &cu->cu_total; /* use default timeout */
373dfdcada3SDoug Rabson 	}
374c675522fSDoug Rabson 	if (tvp->tv_sec || tvp->tv_usec)
375c675522fSDoug Rabson 		timeout = tvtohz(tvp);
376c675522fSDoug Rabson 	else
377c675522fSDoug Rabson 		timeout = 0;
378dfdcada3SDoug Rabson 
379dfdcada3SDoug Rabson 	if (cu->cu_connect && !cu->cu_connected) {
380dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
381dfdcada3SDoug Rabson 		error = soconnect(cu->cu_socket,
382dfdcada3SDoug Rabson 		    (struct sockaddr *)&cu->cu_raddr, curthread);
383dfdcada3SDoug Rabson 		mtx_lock(&cs->cs_lock);
384dfdcada3SDoug Rabson 		if (error) {
385a9148abdSDoug Rabson 			errp->re_errno = error;
386a9148abdSDoug Rabson 			errp->re_status = stat = RPC_CANTSEND;
387dfdcada3SDoug Rabson 			goto out;
388dfdcada3SDoug Rabson 		}
389dfdcada3SDoug Rabson 		cu->cu_connected = 1;
390dfdcada3SDoug Rabson 	}
391dfdcada3SDoug Rabson 	if (cu->cu_connected) {
392dfdcada3SDoug Rabson 		sa = NULL;
393dfdcada3SDoug Rabson 		salen = 0;
394dfdcada3SDoug Rabson 	} else {
395dfdcada3SDoug Rabson 		sa = (struct sockaddr *)&cu->cu_raddr;
396dfdcada3SDoug Rabson 		salen = cu->cu_rlen;
397dfdcada3SDoug Rabson 	}
398c675522fSDoug Rabson 	time_waited = 0;
399c675522fSDoug Rabson 	retrans = 0;
400a9148abdSDoug Rabson 	if (ext && ext->rc_timers) {
401a9148abdSDoug Rabson 		rt = ext->rc_timers;
402a9148abdSDoug Rabson 		if (!rt->rt_rtxcur)
403a9148abdSDoug Rabson 			rt->rt_rtxcur = tvtohz(&cu->cu_wait);
404a9148abdSDoug Rabson 		retransmit_time = next_sendtime = rt->rt_rtxcur;
405a9148abdSDoug Rabson 	} else {
406a9148abdSDoug Rabson 		rt = NULL;
407c675522fSDoug Rabson 		retransmit_time = next_sendtime = tvtohz(&cu->cu_wait);
408a9148abdSDoug Rabson 	}
409dfdcada3SDoug Rabson 
410c675522fSDoug Rabson 	starttime = ticks;
411dfdcada3SDoug Rabson 
412dfdcada3SDoug Rabson call_again:
413dfdcada3SDoug Rabson 	mtx_assert(&cs->cs_lock, MA_OWNED);
414dfdcada3SDoug Rabson 
415dfdcada3SDoug Rabson 	cu->cu_xid++;
416dfdcada3SDoug Rabson 	xid = cu->cu_xid;
417dfdcada3SDoug Rabson 
418dfdcada3SDoug Rabson send_again:
419dfdcada3SDoug Rabson 	mtx_unlock(&cs->cs_lock);
420dfdcada3SDoug Rabson 
421dfdcada3SDoug Rabson 	MGETHDR(mreq, M_WAIT, MT_DATA);
422a9148abdSDoug Rabson 	KASSERT(cu->cu_mcalllen <= MHLEN, ("RPC header too big"));
423a9148abdSDoug Rabson 	bcopy(cu->cu_mcallc, mreq->m_data, cu->cu_mcalllen);
424a9148abdSDoug Rabson 	mreq->m_len = cu->cu_mcalllen;
425dfdcada3SDoug Rabson 
426dfdcada3SDoug Rabson 	/*
427dfdcada3SDoug Rabson 	 * The XID is the first thing in the request.
428dfdcada3SDoug Rabson 	 */
429dfdcada3SDoug Rabson 	*mtod(mreq, uint32_t *) = htonl(xid);
430dfdcada3SDoug Rabson 
431dfdcada3SDoug Rabson 	xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
432dfdcada3SDoug Rabson 
433a9148abdSDoug Rabson 	if (cu->cu_async == TRUE && args == NULL)
434dfdcada3SDoug Rabson 		goto get_reply;
435dfdcada3SDoug Rabson 
436dfdcada3SDoug Rabson 	if ((! XDR_PUTINT32(&xdrs, &proc)) ||
437a9148abdSDoug Rabson 	    (! AUTH_MARSHALL(auth, xid, &xdrs,
438a9148abdSDoug Rabson 		m_copym(args, 0, M_COPYALL, M_WAITOK)))) {
439a9148abdSDoug Rabson 		errp->re_status = stat = RPC_CANTENCODEARGS;
440dfdcada3SDoug Rabson 		mtx_lock(&cs->cs_lock);
441dfdcada3SDoug Rabson 		goto out;
442dfdcada3SDoug Rabson 	}
443a9148abdSDoug Rabson 	mreq->m_pkthdr.len = m_length(mreq, NULL);
444dfdcada3SDoug Rabson 
445c675522fSDoug Rabson 	cr->cr_xid = xid;
446dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
447a9148abdSDoug Rabson 
448a9148abdSDoug Rabson 	/*
449a9148abdSDoug Rabson 	 * Try to get a place in the congestion window.
450a9148abdSDoug Rabson 	 */
451a9148abdSDoug Rabson 	while (cu->cu_sent >= cu->cu_cwnd) {
452a9148abdSDoug Rabson 		cu->cu_cwnd_wait = TRUE;
453a9148abdSDoug Rabson 		error = msleep(&cu->cu_cwnd_wait, &cs->cs_lock,
454a9148abdSDoug Rabson 		    cu->cu_waitflag, "rpccwnd", 0);
455a9148abdSDoug Rabson 		if (error) {
456a9148abdSDoug Rabson 			errp->re_errno = error;
457a9148abdSDoug Rabson 			errp->re_status = stat = RPC_CANTSEND;
458a9148abdSDoug Rabson 			goto out;
459a9148abdSDoug Rabson 		}
460a9148abdSDoug Rabson 	}
461a9148abdSDoug Rabson 	cu->cu_sent += CWNDSCALE;
462a9148abdSDoug Rabson 
463c675522fSDoug Rabson 	TAILQ_INSERT_TAIL(&cs->cs_pending, cr, cr_link);
464dfdcada3SDoug Rabson 	mtx_unlock(&cs->cs_lock);
465dfdcada3SDoug Rabson 
466dfdcada3SDoug Rabson 	/*
467dfdcada3SDoug Rabson 	 * sosend consumes mreq.
468dfdcada3SDoug Rabson 	 */
469dfdcada3SDoug Rabson 	error = sosend(cu->cu_socket, sa, NULL, mreq, NULL, 0, curthread);
470dfdcada3SDoug Rabson 	mreq = NULL;
471dfdcada3SDoug Rabson 
472dfdcada3SDoug Rabson 	/*
473dfdcada3SDoug Rabson 	 * sub-optimal code appears here because we have
474dfdcada3SDoug Rabson 	 * some clock time to spare while the packets are in flight.
475dfdcada3SDoug Rabson 	 * (We assume that this is actually only executed once.)
476dfdcada3SDoug Rabson 	 */
477a9148abdSDoug Rabson 	reply_msg.acpted_rply.ar_verf.oa_flavor = AUTH_NULL;
478a9148abdSDoug Rabson 	reply_msg.acpted_rply.ar_verf.oa_base = cr->cr_verf;
479a9148abdSDoug Rabson 	reply_msg.acpted_rply.ar_verf.oa_length = 0;
480a9148abdSDoug Rabson 	reply_msg.acpted_rply.ar_results.where = NULL;
481a9148abdSDoug Rabson 	reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
482dfdcada3SDoug Rabson 
483dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
484dfdcada3SDoug Rabson 	if (error) {
485c675522fSDoug Rabson 		TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
486a9148abdSDoug Rabson 		errp->re_errno = error;
487a9148abdSDoug Rabson 		errp->re_status = stat = RPC_CANTSEND;
488a9148abdSDoug Rabson 		cu->cu_sent -= CWNDSCALE;
489a9148abdSDoug Rabson 		if (cu->cu_cwnd_wait) {
490a9148abdSDoug Rabson 			cu->cu_cwnd_wait = FALSE;
491a9148abdSDoug Rabson 			wakeup(&cu->cu_cwnd_wait);
492a9148abdSDoug Rabson 		}
493dfdcada3SDoug Rabson 		goto out;
494dfdcada3SDoug Rabson 	}
495dfdcada3SDoug Rabson 
496dfdcada3SDoug Rabson 	/*
497dfdcada3SDoug Rabson 	 * Check to see if we got an upcall while waiting for the
498c675522fSDoug Rabson 	 * lock.
499dfdcada3SDoug Rabson 	 */
500c675522fSDoug Rabson 	if (cr->cr_error) {
501c675522fSDoug Rabson 		TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
502a9148abdSDoug Rabson 		errp->re_errno = cr->cr_error;
503a9148abdSDoug Rabson 		errp->re_status = stat = RPC_CANTRECV;
504a9148abdSDoug Rabson 		cu->cu_sent -= CWNDSCALE;
505a9148abdSDoug Rabson 		if (cu->cu_cwnd_wait) {
506a9148abdSDoug Rabson 			cu->cu_cwnd_wait = FALSE;
507a9148abdSDoug Rabson 			wakeup(&cu->cu_cwnd_wait);
508a9148abdSDoug Rabson 		}
509dfdcada3SDoug Rabson 		goto out;
510dfdcada3SDoug Rabson 	}
511c675522fSDoug Rabson 	if (cr->cr_mrep) {
512c675522fSDoug Rabson 		TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
513a9148abdSDoug Rabson 		cu->cu_sent -= CWNDSCALE;
514a9148abdSDoug Rabson 		if (cu->cu_cwnd_wait) {
515a9148abdSDoug Rabson 			cu->cu_cwnd_wait = FALSE;
516a9148abdSDoug Rabson 			wakeup(&cu->cu_cwnd_wait);
517a9148abdSDoug Rabson 		}
518dfdcada3SDoug Rabson 		goto got_reply;
519dfdcada3SDoug Rabson 	}
520dfdcada3SDoug Rabson 
521dfdcada3SDoug Rabson 	/*
522dfdcada3SDoug Rabson 	 * Hack to provide rpc-based message passing
523dfdcada3SDoug Rabson 	 */
524c675522fSDoug Rabson 	if (timeout == 0) {
525c675522fSDoug Rabson 		TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
526a9148abdSDoug Rabson 		errp->re_status = stat = RPC_TIMEDOUT;
527a9148abdSDoug Rabson 		cu->cu_sent -= CWNDSCALE;
528a9148abdSDoug Rabson 		if (cu->cu_cwnd_wait) {
529a9148abdSDoug Rabson 			cu->cu_cwnd_wait = FALSE;
530a9148abdSDoug Rabson 			wakeup(&cu->cu_cwnd_wait);
531a9148abdSDoug Rabson 		}
532dfdcada3SDoug Rabson 		goto out;
533dfdcada3SDoug Rabson 	}
534dfdcada3SDoug Rabson 
535dfdcada3SDoug Rabson get_reply:
536dfdcada3SDoug Rabson 	for (;;) {
537dfdcada3SDoug Rabson 		/* Decide how long to wait. */
538c675522fSDoug Rabson 		if (next_sendtime < timeout)
539dfdcada3SDoug Rabson 			tv = next_sendtime;
540c675522fSDoug Rabson 		else
541dfdcada3SDoug Rabson 			tv = timeout;
542c675522fSDoug Rabson 		tv -= time_waited;
543dfdcada3SDoug Rabson 
544c675522fSDoug Rabson 		if (tv > 0) {
545a9148abdSDoug Rabson 			if (cu->cu_closing || cu->cu_closed)
546c675522fSDoug Rabson 				error = 0;
547c675522fSDoug Rabson 			else
548c675522fSDoug Rabson 				error = msleep(cr, &cs->cs_lock,
549c675522fSDoug Rabson 				    cu->cu_waitflag, cu->cu_waitchan, tv);
550c675522fSDoug Rabson 		} else {
551c675522fSDoug Rabson 			error = EWOULDBLOCK;
552c675522fSDoug Rabson 		}
553c675522fSDoug Rabson 
554c675522fSDoug Rabson 		TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
555a9148abdSDoug Rabson 		cu->cu_sent -= CWNDSCALE;
556a9148abdSDoug Rabson 		if (cu->cu_cwnd_wait) {
557a9148abdSDoug Rabson 			cu->cu_cwnd_wait = FALSE;
558a9148abdSDoug Rabson 			wakeup(&cu->cu_cwnd_wait);
559a9148abdSDoug Rabson 		}
560dfdcada3SDoug Rabson 
561dfdcada3SDoug Rabson 		if (!error) {
562dfdcada3SDoug Rabson 			/*
563dfdcada3SDoug Rabson 			 * We were woken up by the upcall.  If the
564dfdcada3SDoug Rabson 			 * upcall had a receive error, report that,
565dfdcada3SDoug Rabson 			 * otherwise we have a reply.
566dfdcada3SDoug Rabson 			 */
567c675522fSDoug Rabson 			if (cr->cr_error) {
568a9148abdSDoug Rabson 				errp->re_errno = cr->cr_error;
569a9148abdSDoug Rabson 				errp->re_status = stat = RPC_CANTRECV;
570dfdcada3SDoug Rabson 				goto out;
571dfdcada3SDoug Rabson 			}
572a9148abdSDoug Rabson 
573a9148abdSDoug Rabson 			cu->cu_cwnd += (CWNDSCALE * CWNDSCALE
574a9148abdSDoug Rabson 			    + cu->cu_cwnd / 2) / cu->cu_cwnd;
575a9148abdSDoug Rabson 			if (cu->cu_cwnd > MAXCWND)
576a9148abdSDoug Rabson 				cu->cu_cwnd = MAXCWND;
577a9148abdSDoug Rabson 
578a9148abdSDoug Rabson 			if (rt) {
579a9148abdSDoug Rabson 				/*
580a9148abdSDoug Rabson 				 * Add one to the time since a tick
581a9148abdSDoug Rabson 				 * count of N means that the actual
582a9148abdSDoug Rabson 				 * time taken was somewhere between N
583a9148abdSDoug Rabson 				 * and N+1.
584a9148abdSDoug Rabson 				 */
585a9148abdSDoug Rabson 				rtt = ticks - starttime + 1;
586a9148abdSDoug Rabson 
587a9148abdSDoug Rabson 				/*
588a9148abdSDoug Rabson 				 * Update our estimate of the round
589a9148abdSDoug Rabson 				 * trip time using roughly the
590a9148abdSDoug Rabson 				 * algorithm described in RFC
591a9148abdSDoug Rabson 				 * 2988. Given an RTT sample R:
592a9148abdSDoug Rabson 				 *
593a9148abdSDoug Rabson 				 * RTTVAR = (1-beta) * RTTVAR + beta * |SRTT-R|
594a9148abdSDoug Rabson 				 * SRTT = (1-alpha) * SRTT + alpha * R
595a9148abdSDoug Rabson 				 *
596a9148abdSDoug Rabson 				 * where alpha = 0.125 and beta = 0.25.
597a9148abdSDoug Rabson 				 *
598a9148abdSDoug Rabson 				 * The initial retransmit timeout is
599a9148abdSDoug Rabson 				 * SRTT + 4*RTTVAR and doubles on each
600a9148abdSDoug Rabson 				 * retransmision.
601a9148abdSDoug Rabson 				 */
602a9148abdSDoug Rabson 				if (rt->rt_srtt == 0) {
603a9148abdSDoug Rabson 					rt->rt_srtt = rtt;
604a9148abdSDoug Rabson 					rt->rt_deviate = rtt / 2;
605a9148abdSDoug Rabson 				} else {
606a9148abdSDoug Rabson 					int32_t error = rtt - rt->rt_srtt;
607a9148abdSDoug Rabson 					rt->rt_srtt += error / 8;
608a9148abdSDoug Rabson 					error = abs(error) - rt->rt_deviate;
609a9148abdSDoug Rabson 					rt->rt_deviate += error / 4;
610a9148abdSDoug Rabson 				}
611a9148abdSDoug Rabson 				rt->rt_rtxcur = rt->rt_srtt + 4*rt->rt_deviate;
612a9148abdSDoug Rabson 			}
613a9148abdSDoug Rabson 
614dfdcada3SDoug Rabson 			break;
615dfdcada3SDoug Rabson 		}
616dfdcada3SDoug Rabson 
617dfdcada3SDoug Rabson 		/*
618dfdcada3SDoug Rabson 		 * The sleep returned an error so our request is still
619dfdcada3SDoug Rabson 		 * on the list. If we got EWOULDBLOCK, we may want to
620dfdcada3SDoug Rabson 		 * re-send the request.
621dfdcada3SDoug Rabson 		 */
622dfdcada3SDoug Rabson 		if (error != EWOULDBLOCK) {
623a9148abdSDoug Rabson 			errp->re_errno = error;
624dfdcada3SDoug Rabson 			if (error == EINTR)
625a9148abdSDoug Rabson 				errp->re_status = stat = RPC_INTR;
626dfdcada3SDoug Rabson 			else
627a9148abdSDoug Rabson 				errp->re_status = stat = RPC_CANTRECV;
628dfdcada3SDoug Rabson 			goto out;
629dfdcada3SDoug Rabson 		}
630dfdcada3SDoug Rabson 
631c675522fSDoug Rabson 		time_waited = ticks - starttime;
632dfdcada3SDoug Rabson 
633dfdcada3SDoug Rabson 		/* Check for timeout. */
634c675522fSDoug Rabson 		if (time_waited > timeout) {
635a9148abdSDoug Rabson 			errp->re_errno = EWOULDBLOCK;
636a9148abdSDoug Rabson 			errp->re_status = stat = RPC_TIMEDOUT;
637dfdcada3SDoug Rabson 			goto out;
638dfdcada3SDoug Rabson 		}
639dfdcada3SDoug Rabson 
640dfdcada3SDoug Rabson 		/* Retransmit if necessary. */
641c675522fSDoug Rabson 		if (time_waited >= next_sendtime) {
642a9148abdSDoug Rabson 			cu->cu_cwnd /= 2;
643a9148abdSDoug Rabson 			if (cu->cu_cwnd < CWNDSCALE)
644a9148abdSDoug Rabson 				cu->cu_cwnd = CWNDSCALE;
645c675522fSDoug Rabson 			if (ext && ext->rc_feedback) {
646c675522fSDoug Rabson 				mtx_unlock(&cs->cs_lock);
647c675522fSDoug Rabson 				if (retrans == 0)
648c675522fSDoug Rabson 					ext->rc_feedback(FEEDBACK_REXMIT1,
649c675522fSDoug Rabson 					    proc, ext->rc_feedback_arg);
650c675522fSDoug Rabson 				else
651c675522fSDoug Rabson 					ext->rc_feedback(FEEDBACK_REXMIT2,
652c675522fSDoug Rabson 					    proc, ext->rc_feedback_arg);
653c675522fSDoug Rabson 				mtx_lock(&cs->cs_lock);
654c675522fSDoug Rabson 			}
655a9148abdSDoug Rabson 			if (cu->cu_closing || cu->cu_closed) {
656a9148abdSDoug Rabson 				errp->re_errno = ESHUTDOWN;
657a9148abdSDoug Rabson 				errp->re_status = stat = RPC_CANTRECV;
658c675522fSDoug Rabson 				goto out;
659c675522fSDoug Rabson 			}
660c675522fSDoug Rabson 			retrans++;
661dfdcada3SDoug Rabson 			/* update retransmit_time */
662c675522fSDoug Rabson 			if (retransmit_time < RPC_MAX_BACKOFF * hz)
663c675522fSDoug Rabson 				retransmit_time = 2 * retransmit_time;
664c675522fSDoug Rabson 			next_sendtime += retransmit_time;
665dfdcada3SDoug Rabson 			goto send_again;
666dfdcada3SDoug Rabson 		}
667c675522fSDoug Rabson 		TAILQ_INSERT_TAIL(&cs->cs_pending, cr, cr_link);
668dfdcada3SDoug Rabson 	}
669dfdcada3SDoug Rabson 
670dfdcada3SDoug Rabson got_reply:
671dfdcada3SDoug Rabson 	/*
672dfdcada3SDoug Rabson 	 * Now decode and validate the response. We need to drop the
673dfdcada3SDoug Rabson 	 * lock since xdr_replymsg may end up sleeping in malloc.
674dfdcada3SDoug Rabson 	 */
675dfdcada3SDoug Rabson 	mtx_unlock(&cs->cs_lock);
676dfdcada3SDoug Rabson 
677c675522fSDoug Rabson 	if (ext && ext->rc_feedback)
678c675522fSDoug Rabson 		ext->rc_feedback(FEEDBACK_OK, proc, ext->rc_feedback_arg);
679c675522fSDoug Rabson 
680c675522fSDoug Rabson 	xdrmbuf_create(&xdrs, cr->cr_mrep, XDR_DECODE);
681dfdcada3SDoug Rabson 	ok = xdr_replymsg(&xdrs, &reply_msg);
682c675522fSDoug Rabson 	cr->cr_mrep = NULL;
683dfdcada3SDoug Rabson 
684dfdcada3SDoug Rabson 	if (ok) {
685dfdcada3SDoug Rabson 		if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
686dfdcada3SDoug Rabson 		    (reply_msg.acpted_rply.ar_stat == SUCCESS))
687a9148abdSDoug Rabson 			errp->re_status = stat = RPC_SUCCESS;
688dfdcada3SDoug Rabson 		else
689a9148abdSDoug Rabson 			stat = _seterr_reply(&reply_msg, &(cu->cu_error));
690dfdcada3SDoug Rabson 
691a9148abdSDoug Rabson 		if (errp->re_status == RPC_SUCCESS) {
692a9148abdSDoug Rabson 			results = xdrmbuf_getall(&xdrs);
693a9148abdSDoug Rabson 			if (! AUTH_VALIDATE(auth, xid,
694a9148abdSDoug Rabson 				&reply_msg.acpted_rply.ar_verf,
695a9148abdSDoug Rabson 				&results)) {
696a9148abdSDoug Rabson 				errp->re_status = stat = RPC_AUTHERROR;
697a9148abdSDoug Rabson 				errp->re_why = AUTH_INVALIDRESP;
698a9148abdSDoug Rabson 				if (retrans &&
699a9148abdSDoug Rabson 				    auth->ah_cred.oa_flavor == RPCSEC_GSS) {
700a9148abdSDoug Rabson 					/*
701a9148abdSDoug Rabson 					 * If we retransmitted, its
702a9148abdSDoug Rabson 					 * possible that we will
703a9148abdSDoug Rabson 					 * receive a reply for one of
704a9148abdSDoug Rabson 					 * the earlier transmissions
705a9148abdSDoug Rabson 					 * (which will use an older
706a9148abdSDoug Rabson 					 * RPCSEC_GSS sequence
707a9148abdSDoug Rabson 					 * number). In this case, just
708a9148abdSDoug Rabson 					 * go back and listen for a
709a9148abdSDoug Rabson 					 * new reply. We could keep a
710a9148abdSDoug Rabson 					 * record of all the seq
711a9148abdSDoug Rabson 					 * numbers we have transmitted
712a9148abdSDoug Rabson 					 * so far so that we could
713a9148abdSDoug Rabson 					 * accept a reply for any of
714a9148abdSDoug Rabson 					 * them here.
715a9148abdSDoug Rabson 					 */
716a9148abdSDoug Rabson 					XDR_DESTROY(&xdrs);
717a9148abdSDoug Rabson 					mtx_lock(&cs->cs_lock);
718a9148abdSDoug Rabson 					TAILQ_INSERT_TAIL(&cs->cs_pending,
719a9148abdSDoug Rabson 					    cr, cr_link);
720a9148abdSDoug Rabson 					cr->cr_mrep = NULL;
721a9148abdSDoug Rabson 					goto get_reply;
722dfdcada3SDoug Rabson 				}
723a9148abdSDoug Rabson 			} else {
724a9148abdSDoug Rabson 				*resultsp = results;
725dfdcada3SDoug Rabson 			}
726dfdcada3SDoug Rabson 		}		/* end successful completion */
727dfdcada3SDoug Rabson 		/*
728dfdcada3SDoug Rabson 		 * If unsuccesful AND error is an authentication error
729dfdcada3SDoug Rabson 		 * then refresh credentials and try again, else break
730dfdcada3SDoug Rabson 		 */
731a9148abdSDoug Rabson 		else if (stat == RPC_AUTHERROR)
732dfdcada3SDoug Rabson 			/* maybe our credentials need to be refreshed ... */
733dfdcada3SDoug Rabson 			if (nrefreshes > 0 &&
734a9148abdSDoug Rabson 			    AUTH_REFRESH(auth, &reply_msg)) {
735dfdcada3SDoug Rabson 				nrefreshes--;
736a9148abdSDoug Rabson 				XDR_DESTROY(&xdrs);
737a9148abdSDoug Rabson 				mtx_lock(&cs->cs_lock);
738dfdcada3SDoug Rabson 				goto call_again;
739dfdcada3SDoug Rabson 			}
740dfdcada3SDoug Rabson 		/* end of unsuccessful completion */
741dfdcada3SDoug Rabson 	}	/* end of valid reply message */
742dfdcada3SDoug Rabson 	else {
743a9148abdSDoug Rabson 		errp->re_status = stat = RPC_CANTDECODERES;
744dfdcada3SDoug Rabson 
745dfdcada3SDoug Rabson 	}
746a9148abdSDoug Rabson 	XDR_DESTROY(&xdrs);
747a9148abdSDoug Rabson 	mtx_lock(&cs->cs_lock);
748dfdcada3SDoug Rabson out:
749dfdcada3SDoug Rabson 	mtx_assert(&cs->cs_lock, MA_OWNED);
750dfdcada3SDoug Rabson 
751dfdcada3SDoug Rabson 	if (mreq)
752dfdcada3SDoug Rabson 		m_freem(mreq);
753c675522fSDoug Rabson 	if (cr->cr_mrep)
754c675522fSDoug Rabson 		m_freem(cr->cr_mrep);
755c675522fSDoug Rabson 
756c675522fSDoug Rabson 	cu->cu_threads--;
757c675522fSDoug Rabson 	if (cu->cu_closing)
758c675522fSDoug Rabson 		wakeup(cu);
759dfdcada3SDoug Rabson 
760dfdcada3SDoug Rabson 	mtx_unlock(&cs->cs_lock);
761c675522fSDoug Rabson 
762a9148abdSDoug Rabson 	if (auth && stat != RPC_SUCCESS)
763a9148abdSDoug Rabson 		AUTH_VALIDATE(auth, xid, NULL, NULL);
764a9148abdSDoug Rabson 
765c675522fSDoug Rabson 	free(cr, M_RPC);
766c675522fSDoug Rabson 
767a9148abdSDoug Rabson 	return (stat);
768dfdcada3SDoug Rabson }
769dfdcada3SDoug Rabson 
770dfdcada3SDoug Rabson static void
771dfdcada3SDoug Rabson clnt_dg_geterr(CLIENT *cl, struct rpc_err *errp)
772dfdcada3SDoug Rabson {
773dfdcada3SDoug Rabson 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
774dfdcada3SDoug Rabson 
775dfdcada3SDoug Rabson 	*errp = cu->cu_error;
776dfdcada3SDoug Rabson }
777dfdcada3SDoug Rabson 
778dfdcada3SDoug Rabson static bool_t
779dfdcada3SDoug Rabson clnt_dg_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
780dfdcada3SDoug Rabson {
781dfdcada3SDoug Rabson 	XDR xdrs;
782dfdcada3SDoug Rabson 	bool_t dummy;
783dfdcada3SDoug Rabson 
784dfdcada3SDoug Rabson 	xdrs.x_op = XDR_FREE;
785dfdcada3SDoug Rabson 	dummy = (*xdr_res)(&xdrs, res_ptr);
786dfdcada3SDoug Rabson 
787dfdcada3SDoug Rabson 	return (dummy);
788dfdcada3SDoug Rabson }
789dfdcada3SDoug Rabson 
790dfdcada3SDoug Rabson /*ARGSUSED*/
791dfdcada3SDoug Rabson static void
792dfdcada3SDoug Rabson clnt_dg_abort(CLIENT *h)
793dfdcada3SDoug Rabson {
794dfdcada3SDoug Rabson }
795dfdcada3SDoug Rabson 
796dfdcada3SDoug Rabson static bool_t
797dfdcada3SDoug Rabson clnt_dg_control(CLIENT *cl, u_int request, void *info)
798dfdcada3SDoug Rabson {
799dfdcada3SDoug Rabson 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
800dfdcada3SDoug Rabson 	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
801dfdcada3SDoug Rabson 	struct sockaddr *addr;
802dfdcada3SDoug Rabson 
803dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
804dfdcada3SDoug Rabson 
805dfdcada3SDoug Rabson 	switch (request) {
806dfdcada3SDoug Rabson 	case CLSET_FD_CLOSE:
807dfdcada3SDoug Rabson 		cu->cu_closeit = TRUE;
808dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
809dfdcada3SDoug Rabson 		return (TRUE);
810dfdcada3SDoug Rabson 	case CLSET_FD_NCLOSE:
811dfdcada3SDoug Rabson 		cu->cu_closeit = FALSE;
812dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
813dfdcada3SDoug Rabson 		return (TRUE);
814dfdcada3SDoug Rabson 	}
815dfdcada3SDoug Rabson 
816dfdcada3SDoug Rabson 	/* for other requests which use info */
817dfdcada3SDoug Rabson 	if (info == NULL) {
818dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
819dfdcada3SDoug Rabson 		return (FALSE);
820dfdcada3SDoug Rabson 	}
821dfdcada3SDoug Rabson 	switch (request) {
822dfdcada3SDoug Rabson 	case CLSET_TIMEOUT:
823dfdcada3SDoug Rabson 		if (time_not_ok((struct timeval *)info)) {
824dfdcada3SDoug Rabson 			mtx_unlock(&cs->cs_lock);
825dfdcada3SDoug Rabson 			return (FALSE);
826dfdcada3SDoug Rabson 		}
827dfdcada3SDoug Rabson 		cu->cu_total = *(struct timeval *)info;
828dfdcada3SDoug Rabson 		break;
829dfdcada3SDoug Rabson 	case CLGET_TIMEOUT:
830dfdcada3SDoug Rabson 		*(struct timeval *)info = cu->cu_total;
831dfdcada3SDoug Rabson 		break;
832dfdcada3SDoug Rabson 	case CLSET_RETRY_TIMEOUT:
833dfdcada3SDoug Rabson 		if (time_not_ok((struct timeval *)info)) {
834dfdcada3SDoug Rabson 			mtx_unlock(&cs->cs_lock);
835dfdcada3SDoug Rabson 			return (FALSE);
836dfdcada3SDoug Rabson 		}
837dfdcada3SDoug Rabson 		cu->cu_wait = *(struct timeval *)info;
838dfdcada3SDoug Rabson 		break;
839dfdcada3SDoug Rabson 	case CLGET_RETRY_TIMEOUT:
840dfdcada3SDoug Rabson 		*(struct timeval *)info = cu->cu_wait;
841dfdcada3SDoug Rabson 		break;
842dfdcada3SDoug Rabson 	case CLGET_SVC_ADDR:
843dfdcada3SDoug Rabson 		/*
844dfdcada3SDoug Rabson 		 * Slightly different semantics to userland - we use
845dfdcada3SDoug Rabson 		 * sockaddr instead of netbuf.
846dfdcada3SDoug Rabson 		 */
847dfdcada3SDoug Rabson 		memcpy(info, &cu->cu_raddr, cu->cu_raddr.ss_len);
848dfdcada3SDoug Rabson 		break;
849dfdcada3SDoug Rabson 	case CLSET_SVC_ADDR:		/* set to new address */
850dfdcada3SDoug Rabson 		addr = (struct sockaddr *)info;
851dfdcada3SDoug Rabson 		(void) memcpy(&cu->cu_raddr, addr, addr->sa_len);
852dfdcada3SDoug Rabson 		break;
853dfdcada3SDoug Rabson 	case CLGET_XID:
854dfdcada3SDoug Rabson 		*(uint32_t *)info = cu->cu_xid;
855dfdcada3SDoug Rabson 		break;
856dfdcada3SDoug Rabson 
857dfdcada3SDoug Rabson 	case CLSET_XID:
858dfdcada3SDoug Rabson 		/* This will set the xid of the NEXT call */
859dfdcada3SDoug Rabson 		/* decrement by 1 as clnt_dg_call() increments once */
860dfdcada3SDoug Rabson 		cu->cu_xid = *(uint32_t *)info - 1;
861dfdcada3SDoug Rabson 		break;
862dfdcada3SDoug Rabson 
863dfdcada3SDoug Rabson 	case CLGET_VERS:
864dfdcada3SDoug Rabson 		/*
865dfdcada3SDoug Rabson 		 * This RELIES on the information that, in the call body,
866dfdcada3SDoug Rabson 		 * the version number field is the fifth field from the
867dfdcada3SDoug Rabson 		 * begining of the RPC header. MUST be changed if the
868dfdcada3SDoug Rabson 		 * call_struct is changed
869dfdcada3SDoug Rabson 		 */
870dfdcada3SDoug Rabson 		*(uint32_t *)info =
871dfdcada3SDoug Rabson 		    ntohl(*(uint32_t *)(void *)(cu->cu_mcallc +
872dfdcada3SDoug Rabson 		    4 * BYTES_PER_XDR_UNIT));
873dfdcada3SDoug Rabson 		break;
874dfdcada3SDoug Rabson 
875dfdcada3SDoug Rabson 	case CLSET_VERS:
876dfdcada3SDoug Rabson 		*(uint32_t *)(void *)(cu->cu_mcallc + 4 * BYTES_PER_XDR_UNIT)
877dfdcada3SDoug Rabson 			= htonl(*(uint32_t *)info);
878dfdcada3SDoug Rabson 		break;
879dfdcada3SDoug Rabson 
880dfdcada3SDoug Rabson 	case CLGET_PROG:
881dfdcada3SDoug Rabson 		/*
882dfdcada3SDoug Rabson 		 * This RELIES on the information that, in the call body,
883dfdcada3SDoug Rabson 		 * the program number field is the fourth field from the
884dfdcada3SDoug Rabson 		 * begining of the RPC header. MUST be changed if the
885dfdcada3SDoug Rabson 		 * call_struct is changed
886dfdcada3SDoug Rabson 		 */
887dfdcada3SDoug Rabson 		*(uint32_t *)info =
888dfdcada3SDoug Rabson 		    ntohl(*(uint32_t *)(void *)(cu->cu_mcallc +
889dfdcada3SDoug Rabson 		    3 * BYTES_PER_XDR_UNIT));
890dfdcada3SDoug Rabson 		break;
891dfdcada3SDoug Rabson 
892dfdcada3SDoug Rabson 	case CLSET_PROG:
893dfdcada3SDoug Rabson 		*(uint32_t *)(void *)(cu->cu_mcallc + 3 * BYTES_PER_XDR_UNIT)
894dfdcada3SDoug Rabson 			= htonl(*(uint32_t *)info);
895dfdcada3SDoug Rabson 		break;
896dfdcada3SDoug Rabson 	case CLSET_ASYNC:
897dfdcada3SDoug Rabson 		cu->cu_async = *(int *)info;
898dfdcada3SDoug Rabson 		break;
899dfdcada3SDoug Rabson 	case CLSET_CONNECT:
900dfdcada3SDoug Rabson 		cu->cu_connect = *(int *)info;
901dfdcada3SDoug Rabson 		break;
902dfdcada3SDoug Rabson 	case CLSET_WAITCHAN:
903a9148abdSDoug Rabson 		cu->cu_waitchan = (const char *)info;
904dfdcada3SDoug Rabson 		break;
905dfdcada3SDoug Rabson 	case CLGET_WAITCHAN:
906dfdcada3SDoug Rabson 		*(const char **) info = cu->cu_waitchan;
907dfdcada3SDoug Rabson 		break;
908dfdcada3SDoug Rabson 	case CLSET_INTERRUPTIBLE:
909dfdcada3SDoug Rabson 		if (*(int *) info)
910dfdcada3SDoug Rabson 			cu->cu_waitflag = PCATCH;
911dfdcada3SDoug Rabson 		else
912dfdcada3SDoug Rabson 			cu->cu_waitflag = 0;
913dfdcada3SDoug Rabson 		break;
914dfdcada3SDoug Rabson 	case CLGET_INTERRUPTIBLE:
915dfdcada3SDoug Rabson 		if (cu->cu_waitflag)
916dfdcada3SDoug Rabson 			*(int *) info = TRUE;
917dfdcada3SDoug Rabson 		else
918dfdcada3SDoug Rabson 			*(int *) info = FALSE;
919dfdcada3SDoug Rabson 		break;
920dfdcada3SDoug Rabson 	default:
921dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
922dfdcada3SDoug Rabson 		return (FALSE);
923dfdcada3SDoug Rabson 	}
924dfdcada3SDoug Rabson 	mtx_unlock(&cs->cs_lock);
925dfdcada3SDoug Rabson 	return (TRUE);
926dfdcada3SDoug Rabson }
927dfdcada3SDoug Rabson 
928dfdcada3SDoug Rabson static void
929a9148abdSDoug Rabson clnt_dg_close(CLIENT *cl)
930dfdcada3SDoug Rabson {
931dfdcada3SDoug Rabson 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
932dfdcada3SDoug Rabson 	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
933c675522fSDoug Rabson 	struct cu_request *cr;
934dfdcada3SDoug Rabson 
935dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
936c675522fSDoug Rabson 
937a9148abdSDoug Rabson 	if (cu->cu_closed) {
938a9148abdSDoug Rabson 		mtx_unlock(&cs->cs_lock);
939a9148abdSDoug Rabson 		return;
940a9148abdSDoug Rabson 	}
941a9148abdSDoug Rabson 
942a9148abdSDoug Rabson 	if (cu->cu_closing) {
943a9148abdSDoug Rabson 		while (cu->cu_closing)
944a9148abdSDoug Rabson 			msleep(cu, &cs->cs_lock, 0, "rpcclose", 0);
945a9148abdSDoug Rabson 		KASSERT(cu->cu_closed, ("client should be closed"));
946a9148abdSDoug Rabson 		mtx_unlock(&cs->cs_lock);
947a9148abdSDoug Rabson 		return;
948a9148abdSDoug Rabson 	}
949a9148abdSDoug Rabson 
950c675522fSDoug Rabson 	/*
951c675522fSDoug Rabson 	 * Abort any pending requests and wait until everyone
952c675522fSDoug Rabson 	 * has finished with clnt_vc_call.
953c675522fSDoug Rabson 	 */
954c675522fSDoug Rabson 	cu->cu_closing = TRUE;
955c675522fSDoug Rabson 	TAILQ_FOREACH(cr, &cs->cs_pending, cr_link) {
956c675522fSDoug Rabson 		if (cr->cr_client == cl) {
957c675522fSDoug Rabson 			cr->cr_xid = 0;
958c675522fSDoug Rabson 			cr->cr_error = ESHUTDOWN;
959c675522fSDoug Rabson 			wakeup(cr);
960c675522fSDoug Rabson 		}
961c675522fSDoug Rabson 	}
962c675522fSDoug Rabson 
963c675522fSDoug Rabson 	while (cu->cu_threads)
964c675522fSDoug Rabson 		msleep(cu, &cs->cs_lock, 0, "rpcclose", 0);
965c675522fSDoug Rabson 
966a9148abdSDoug Rabson 	cu->cu_closing = FALSE;
967a9148abdSDoug Rabson 	cu->cu_closed = TRUE;
968a9148abdSDoug Rabson 
969a9148abdSDoug Rabson 	mtx_unlock(&cs->cs_lock);
970a9148abdSDoug Rabson 	wakeup(cu);
971a9148abdSDoug Rabson }
972a9148abdSDoug Rabson 
973a9148abdSDoug Rabson static void
974a9148abdSDoug Rabson clnt_dg_destroy(CLIENT *cl)
975a9148abdSDoug Rabson {
976a9148abdSDoug Rabson 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
977a9148abdSDoug Rabson 	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
978a9148abdSDoug Rabson 	struct socket *so = NULL;
979a9148abdSDoug Rabson 	bool_t lastsocketref;
980a9148abdSDoug Rabson 
981a9148abdSDoug Rabson 	clnt_dg_close(cl);
982a9148abdSDoug Rabson 
983a9148abdSDoug Rabson 	mtx_lock(&cs->cs_lock);
984a9148abdSDoug Rabson 
985dfdcada3SDoug Rabson 	cs->cs_refs--;
986dfdcada3SDoug Rabson 	if (cs->cs_refs == 0) {
987c675522fSDoug Rabson 		mtx_destroy(&cs->cs_lock);
988c675522fSDoug Rabson 		SOCKBUF_LOCK(&cu->cu_socket->so_rcv);
989dfdcada3SDoug Rabson 		cu->cu_socket->so_upcallarg = NULL;
990dfdcada3SDoug Rabson 		cu->cu_socket->so_upcall = NULL;
991dfdcada3SDoug Rabson 		cu->cu_socket->so_rcv.sb_flags &= ~SB_UPCALL;
992dfdcada3SDoug Rabson 		SOCKBUF_UNLOCK(&cu->cu_socket->so_rcv);
993dfdcada3SDoug Rabson 		mem_free(cs, sizeof(*cs));
994dfdcada3SDoug Rabson 		lastsocketref = TRUE;
995dfdcada3SDoug Rabson 	} else {
996dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
997dfdcada3SDoug Rabson 		lastsocketref = FALSE;
998dfdcada3SDoug Rabson 	}
999dfdcada3SDoug Rabson 
1000c675522fSDoug Rabson 	if (cu->cu_closeit && lastsocketref) {
1001dfdcada3SDoug Rabson 		so = cu->cu_socket;
1002dfdcada3SDoug Rabson 		cu->cu_socket = NULL;
1003dfdcada3SDoug Rabson 	}
1004dfdcada3SDoug Rabson 
1005dfdcada3SDoug Rabson 	if (so)
1006dfdcada3SDoug Rabson 		soclose(so);
1007dfdcada3SDoug Rabson 
1008dfdcada3SDoug Rabson 	if (cl->cl_netid && cl->cl_netid[0])
1009dfdcada3SDoug Rabson 		mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
1010dfdcada3SDoug Rabson 	if (cl->cl_tp && cl->cl_tp[0])
1011dfdcada3SDoug Rabson 		mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
1012dfdcada3SDoug Rabson 	mem_free(cu, sizeof (*cu));
1013dfdcada3SDoug Rabson 	mem_free(cl, sizeof (CLIENT));
1014dfdcada3SDoug Rabson }
1015dfdcada3SDoug Rabson 
1016dfdcada3SDoug Rabson /*
1017dfdcada3SDoug Rabson  * Make sure that the time is not garbage.  -1 value is allowed.
1018dfdcada3SDoug Rabson  */
1019dfdcada3SDoug Rabson static bool_t
1020dfdcada3SDoug Rabson time_not_ok(struct timeval *t)
1021dfdcada3SDoug Rabson {
1022dfdcada3SDoug Rabson 	return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
1023dfdcada3SDoug Rabson 		t->tv_usec < -1 || t->tv_usec > 1000000);
1024dfdcada3SDoug Rabson }
1025dfdcada3SDoug Rabson 
1026dfdcada3SDoug Rabson void
1027dfdcada3SDoug Rabson clnt_dg_soupcall(struct socket *so, void *arg, int waitflag)
1028dfdcada3SDoug Rabson {
1029dfdcada3SDoug Rabson 	struct cu_socket *cs = (struct cu_socket *) arg;
1030dfdcada3SDoug Rabson 	struct uio uio;
1031dfdcada3SDoug Rabson 	struct mbuf *m;
1032dfdcada3SDoug Rabson 	struct mbuf *control;
1033dfdcada3SDoug Rabson 	struct cu_request *cr;
1034dfdcada3SDoug Rabson 	int error, rcvflag, foundreq;
1035dfdcada3SDoug Rabson 	uint32_t xid;
1036dfdcada3SDoug Rabson 
1037dfdcada3SDoug Rabson 	uio.uio_resid = 1000000000;
1038dfdcada3SDoug Rabson 	uio.uio_td = curthread;
1039dfdcada3SDoug Rabson 	do {
1040dfdcada3SDoug Rabson 		m = NULL;
1041dfdcada3SDoug Rabson 		control = NULL;
1042dfdcada3SDoug Rabson 		rcvflag = MSG_DONTWAIT;
1043dfdcada3SDoug Rabson 		error = soreceive(so, NULL, &uio, &m, &control, &rcvflag);
1044dfdcada3SDoug Rabson 		if (control)
1045dfdcada3SDoug Rabson 			m_freem(control);
1046dfdcada3SDoug Rabson 
1047dfdcada3SDoug Rabson 		if (error == EWOULDBLOCK)
1048dfdcada3SDoug Rabson 			break;
1049dfdcada3SDoug Rabson 
1050dfdcada3SDoug Rabson 		/*
1051dfdcada3SDoug Rabson 		 * If there was an error, wake up all pending
1052dfdcada3SDoug Rabson 		 * requests.
1053dfdcada3SDoug Rabson 		 */
1054dfdcada3SDoug Rabson 		if (error) {
1055dfdcada3SDoug Rabson 			mtx_lock(&cs->cs_lock);
1056dfdcada3SDoug Rabson 			TAILQ_FOREACH(cr, &cs->cs_pending, cr_link) {
1057c675522fSDoug Rabson 				cr->cr_xid = 0;
1058dfdcada3SDoug Rabson 				cr->cr_error = error;
1059dfdcada3SDoug Rabson 				wakeup(cr);
1060dfdcada3SDoug Rabson 			}
1061dfdcada3SDoug Rabson 			mtx_unlock(&cs->cs_lock);
1062dfdcada3SDoug Rabson 			break;
1063dfdcada3SDoug Rabson 		}
1064dfdcada3SDoug Rabson 
1065dfdcada3SDoug Rabson 		/*
1066dfdcada3SDoug Rabson 		 * The XID is in the first uint32_t of the reply.
1067dfdcada3SDoug Rabson 		 */
1068a9148abdSDoug Rabson 		if (m->m_len < sizeof(xid))
1069dfdcada3SDoug Rabson 			m = m_pullup(m, sizeof(xid));
1070dfdcada3SDoug Rabson 		if (!m)
1071c675522fSDoug Rabson 			/*
1072c675522fSDoug Rabson 			 * Should never happen.
1073c675522fSDoug Rabson 			 */
1074c675522fSDoug Rabson 			continue;
1075c675522fSDoug Rabson 
1076dfdcada3SDoug Rabson 		xid = ntohl(*mtod(m, uint32_t *));
1077dfdcada3SDoug Rabson 
1078dfdcada3SDoug Rabson 		/*
1079dfdcada3SDoug Rabson 		 * Attempt to match this reply with a pending request.
1080dfdcada3SDoug Rabson 		 */
1081dfdcada3SDoug Rabson 		mtx_lock(&cs->cs_lock);
1082dfdcada3SDoug Rabson 		foundreq = 0;
1083dfdcada3SDoug Rabson 		TAILQ_FOREACH(cr, &cs->cs_pending, cr_link) {
1084dfdcada3SDoug Rabson 			if (cr->cr_xid == xid) {
1085dfdcada3SDoug Rabson 				/*
1086c675522fSDoug Rabson 				 * This one matches. We leave the
1087dfdcada3SDoug Rabson 				 * reply mbuf in cr->cr_mrep. Set the
1088c675522fSDoug Rabson 				 * XID to zero so that we will ignore
1089c675522fSDoug Rabson 				 * any duplicated replies that arrive
1090c675522fSDoug Rabson 				 * before clnt_dg_call removes it from
1091c675522fSDoug Rabson 				 * the queue.
1092dfdcada3SDoug Rabson 				 */
1093dfdcada3SDoug Rabson 				cr->cr_xid = 0;
1094dfdcada3SDoug Rabson 				cr->cr_mrep = m;
1095dfdcada3SDoug Rabson 				cr->cr_error = 0;
1096dfdcada3SDoug Rabson 				foundreq = 1;
1097dfdcada3SDoug Rabson 				wakeup(cr);
1098dfdcada3SDoug Rabson 				break;
1099dfdcada3SDoug Rabson 			}
1100dfdcada3SDoug Rabson 		}
1101dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
1102dfdcada3SDoug Rabson 
1103dfdcada3SDoug Rabson 		/*
1104dfdcada3SDoug Rabson 		 * If we didn't find the matching request, just drop
1105dfdcada3SDoug Rabson 		 * it - its probably a repeated reply.
1106dfdcada3SDoug Rabson 		 */
1107dfdcada3SDoug Rabson 		if (!foundreq)
1108dfdcada3SDoug Rabson 			m_freem(m);
1109dfdcada3SDoug Rabson 	} while (m);
1110dfdcada3SDoug Rabson }
1111dfdcada3SDoug Rabson 
1112