clnt_dg.c revision 184588
1177633Sdfr/*	$NetBSD: clnt_dg.c,v 1.4 2000/07/14 08:40:41 fvdl Exp $	*/
2177633Sdfr
3177633Sdfr/*
4177633Sdfr * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5177633Sdfr * unrestricted use provided that this legend is included on all tape
6177633Sdfr * media and as a part of the software program in whole or part.  Users
7177633Sdfr * may copy or modify Sun RPC without charge, but are not authorized
8177633Sdfr * to license or distribute it to anyone else except as part of a product or
9177633Sdfr * program developed by the user.
10177633Sdfr *
11177633Sdfr * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12177633Sdfr * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13177633Sdfr * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14177633Sdfr *
15177633Sdfr * Sun RPC is provided with no support and without any obligation on the
16177633Sdfr * part of Sun Microsystems, Inc. to assist in its use, correction,
17177633Sdfr * modification or enhancement.
18177633Sdfr *
19177633Sdfr * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20177633Sdfr * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21177633Sdfr * OR ANY PART THEREOF.
22177633Sdfr *
23177633Sdfr * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24177633Sdfr * or profits or other special, indirect and consequential damages, even if
25177633Sdfr * Sun has been advised of the possibility of such damages.
26177633Sdfr *
27177633Sdfr * Sun Microsystems, Inc.
28177633Sdfr * 2550 Garcia Avenue
29177633Sdfr * Mountain View, California  94043
30177633Sdfr */
31177633Sdfr/*
32177633Sdfr * Copyright (c) 1986-1991 by Sun Microsystems Inc.
33177633Sdfr */
34177633Sdfr
35177633Sdfr#if defined(LIBC_SCCS) && !defined(lint)
36177633Sdfr#ident	"@(#)clnt_dg.c	1.23	94/04/22 SMI"
37177633Sdfrstatic char sccsid[] = "@(#)clnt_dg.c 1.19 89/03/16 Copyr 1988 Sun Micro";
38177633Sdfr#endif
39177633Sdfr#include <sys/cdefs.h>
40177633Sdfr__FBSDID("$FreeBSD: head/sys/rpc/clnt_dg.c 184588 2008-11-03 10:38:00Z dfr $");
41177633Sdfr
42177633Sdfr/*
43177633Sdfr * Implements a connectionless client side RPC.
44177633Sdfr */
45177633Sdfr
46177633Sdfr#include <sys/param.h>
47177633Sdfr#include <sys/systm.h>
48180025Sdfr#include <sys/kernel.h>
49177633Sdfr#include <sys/lock.h>
50177633Sdfr#include <sys/malloc.h>
51177633Sdfr#include <sys/mbuf.h>
52177633Sdfr#include <sys/mutex.h>
53177633Sdfr#include <sys/pcpu.h>
54177633Sdfr#include <sys/proc.h>
55177633Sdfr#include <sys/socket.h>
56177633Sdfr#include <sys/socketvar.h>
57177633Sdfr#include <sys/time.h>
58177633Sdfr#include <sys/uio.h>
59177633Sdfr
60177633Sdfr#include <rpc/rpc.h>
61177685Sdfr#include <rpc/rpc_com.h>
62177633Sdfr
63177633Sdfr
64177633Sdfr#ifdef _FREEFALL_CONFIG
65177633Sdfr/*
66177633Sdfr * Disable RPC exponential back-off for FreeBSD.org systems.
67177633Sdfr */
68177633Sdfr#define	RPC_MAX_BACKOFF		1 /* second */
69177633Sdfr#else
70177633Sdfr#define	RPC_MAX_BACKOFF		30 /* seconds */
71177633Sdfr#endif
72177633Sdfr
73177633Sdfrstatic bool_t time_not_ok(struct timeval *);
74180025Sdfrstatic enum clnt_stat clnt_dg_call(CLIENT *, struct rpc_callextra *,
75184588Sdfr    rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
76177633Sdfrstatic void clnt_dg_geterr(CLIENT *, struct rpc_err *);
77177633Sdfrstatic bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *);
78177633Sdfrstatic void clnt_dg_abort(CLIENT *);
79177633Sdfrstatic bool_t clnt_dg_control(CLIENT *, u_int, void *);
80184588Sdfrstatic void clnt_dg_close(CLIENT *);
81177633Sdfrstatic void clnt_dg_destroy(CLIENT *);
82177633Sdfrstatic void clnt_dg_soupcall(struct socket *so, void *arg, int waitflag);
83177633Sdfr
84177633Sdfrstatic struct clnt_ops clnt_dg_ops = {
85177633Sdfr	.cl_call =	clnt_dg_call,
86177633Sdfr	.cl_abort =	clnt_dg_abort,
87177633Sdfr	.cl_geterr =	clnt_dg_geterr,
88177633Sdfr	.cl_freeres =	clnt_dg_freeres,
89184588Sdfr	.cl_close =	clnt_dg_close,
90177633Sdfr	.cl_destroy =	clnt_dg_destroy,
91177633Sdfr	.cl_control =	clnt_dg_control
92177633Sdfr};
93177633Sdfr
94177633Sdfrstatic const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
95177633Sdfr
96177633Sdfr/*
97180025Sdfr * A pending RPC request which awaits a reply. Requests which have
98180025Sdfr * received their reply will have cr_xid set to zero and cr_mrep to
99180025Sdfr * the mbuf chain of the reply.
100177633Sdfr */
101177633Sdfrstruct cu_request {
102177633Sdfr	TAILQ_ENTRY(cu_request) cr_link;
103180025Sdfr	CLIENT			*cr_client;	/* owner */
104177633Sdfr	uint32_t		cr_xid;		/* XID of request */
105177633Sdfr	struct mbuf		*cr_mrep;	/* reply received by upcall */
106177633Sdfr	int			cr_error;	/* any error from upcall */
107184588Sdfr	char			cr_verf[MAX_AUTH_BYTES]; /* reply verf */
108177633Sdfr};
109177633Sdfr
110177633SdfrTAILQ_HEAD(cu_request_list, cu_request);
111177633Sdfr
112177633Sdfr#define MCALL_MSG_SIZE 24
113177633Sdfr
114177633Sdfr/*
115177633Sdfr * This structure is pointed to by the socket's so_upcallarg
116177633Sdfr * member. It is separate from the client private data to facilitate
117177633Sdfr * multiple clients sharing the same socket. The cs_lock mutex is used
118177633Sdfr * to protect all fields of this structure, the socket's receive
119177633Sdfr * buffer SOCKBUF_LOCK is used to ensure that exactly one of these
120177633Sdfr * structures is installed on the socket.
121177633Sdfr */
122177633Sdfrstruct cu_socket {
123177633Sdfr	struct mtx		cs_lock;
124177633Sdfr	int			cs_refs;	/* Count of clients */
125177633Sdfr	struct cu_request_list	cs_pending;	/* Requests awaiting replies */
126177633Sdfr};
127177633Sdfr
128177633Sdfr/*
129177633Sdfr * Private data kept per client handle
130177633Sdfr */
131177633Sdfrstruct cu_data {
132180025Sdfr	int			cu_threads;	/* # threads in clnt_vc_call */
133184588Sdfr	bool_t			cu_closing;	/* TRUE if we are closing */
134184588Sdfr	bool_t			cu_closed;	/* TRUE if we are closed */
135177633Sdfr	struct socket		*cu_socket;	/* connection socket */
136177633Sdfr	bool_t			cu_closeit;	/* opened by library */
137177633Sdfr	struct sockaddr_storage	cu_raddr;	/* remote address */
138177633Sdfr	int			cu_rlen;
139177633Sdfr	struct timeval		cu_wait;	/* retransmit interval */
140177633Sdfr	struct timeval		cu_total;	/* total time for the call */
141177633Sdfr	struct rpc_err		cu_error;
142177633Sdfr	uint32_t		cu_xid;
143177633Sdfr	char			cu_mcallc[MCALL_MSG_SIZE]; /* marshalled callmsg */
144177633Sdfr	size_t			cu_mcalllen;
145177633Sdfr	size_t			cu_sendsz;	/* send size */
146177633Sdfr	size_t			cu_recvsz;	/* recv size */
147177633Sdfr	int			cu_async;
148177633Sdfr	int			cu_connect;	/* Use connect(). */
149177633Sdfr	int			cu_connected;	/* Have done connect(). */
150177633Sdfr	const char		*cu_waitchan;
151177633Sdfr	int			cu_waitflag;
152184588Sdfr	int			cu_cwnd;	/* congestion window */
153184588Sdfr	int			cu_sent;	/* number of in-flight RPCs */
154184588Sdfr	bool_t			cu_cwnd_wait;
155177633Sdfr};
156177633Sdfr
157184588Sdfr#define CWNDSCALE	256
158184588Sdfr#define MAXCWND		(32 * CWNDSCALE)
159184588Sdfr
160177633Sdfr/*
161177633Sdfr * Connection less client creation returns with client handle parameters.
162177633Sdfr * Default options are set, which the user can change using clnt_control().
163177633Sdfr * fd should be open and bound.
164177633Sdfr * NB: The rpch->cl_auth is initialized to null authentication.
165177633Sdfr * 	Caller may wish to set this something more useful.
166177633Sdfr *
167177633Sdfr * sendsz and recvsz are the maximum allowable packet sizes that can be
168177633Sdfr * sent and received. Normally they are the same, but they can be
169177633Sdfr * changed to improve the program efficiency and buffer allocation.
170177633Sdfr * If they are 0, use the transport default.
171177633Sdfr *
172177633Sdfr * If svcaddr is NULL, returns NULL.
173177633Sdfr */
174177633SdfrCLIENT *
175177633Sdfrclnt_dg_create(
176177633Sdfr	struct socket *so,
177177633Sdfr	struct sockaddr *svcaddr,	/* servers address */
178177633Sdfr	rpcprog_t program,		/* program number */
179177633Sdfr	rpcvers_t version,		/* version number */
180177633Sdfr	size_t sendsz,			/* buffer recv size */
181177633Sdfr	size_t recvsz)			/* buffer send size */
182177633Sdfr{
183177633Sdfr	CLIENT *cl = NULL;		/* client handle */
184177633Sdfr	struct cu_data *cu = NULL;	/* private data */
185177633Sdfr	struct cu_socket *cs = NULL;
186177633Sdfr	struct timeval now;
187177633Sdfr	struct rpc_msg call_msg;
188177633Sdfr	struct __rpc_sockinfo si;
189177633Sdfr	XDR xdrs;
190177633Sdfr
191177633Sdfr	if (svcaddr == NULL) {
192177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
193177633Sdfr		return (NULL);
194177633Sdfr	}
195177633Sdfr
196177633Sdfr	if (!__rpc_socket2sockinfo(so, &si)) {
197177633Sdfr		rpc_createerr.cf_stat = RPC_TLIERROR;
198177633Sdfr		rpc_createerr.cf_error.re_errno = 0;
199177633Sdfr		return (NULL);
200177633Sdfr	}
201177633Sdfr
202177633Sdfr	/*
203177633Sdfr	 * Find the receive and the send size
204177633Sdfr	 */
205177633Sdfr	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
206177633Sdfr	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
207177633Sdfr	if ((sendsz == 0) || (recvsz == 0)) {
208177633Sdfr		rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */
209177633Sdfr		rpc_createerr.cf_error.re_errno = 0;
210177633Sdfr		return (NULL);
211177633Sdfr	}
212177633Sdfr
213177633Sdfr	cl = mem_alloc(sizeof (CLIENT));
214177633Sdfr
215177633Sdfr	/*
216177633Sdfr	 * Should be multiple of 4 for XDR.
217177633Sdfr	 */
218177633Sdfr	sendsz = ((sendsz + 3) / 4) * 4;
219177633Sdfr	recvsz = ((recvsz + 3) / 4) * 4;
220177633Sdfr	cu = mem_alloc(sizeof (*cu));
221180025Sdfr	cu->cu_threads = 0;
222180025Sdfr	cu->cu_closing = FALSE;
223184588Sdfr	cu->cu_closed = FALSE;
224177633Sdfr	(void) memcpy(&cu->cu_raddr, svcaddr, (size_t)svcaddr->sa_len);
225177633Sdfr	cu->cu_rlen = svcaddr->sa_len;
226177633Sdfr	/* Other values can also be set through clnt_control() */
227180025Sdfr	cu->cu_wait.tv_sec = 3;	/* heuristically chosen */
228177633Sdfr	cu->cu_wait.tv_usec = 0;
229177633Sdfr	cu->cu_total.tv_sec = -1;
230177633Sdfr	cu->cu_total.tv_usec = -1;
231177633Sdfr	cu->cu_sendsz = sendsz;
232177633Sdfr	cu->cu_recvsz = recvsz;
233177633Sdfr	cu->cu_async = FALSE;
234177633Sdfr	cu->cu_connect = FALSE;
235177633Sdfr	cu->cu_connected = FALSE;
236177633Sdfr	cu->cu_waitchan = "rpcrecv";
237177633Sdfr	cu->cu_waitflag = 0;
238184588Sdfr	cu->cu_cwnd = MAXCWND / 2;
239184588Sdfr	cu->cu_sent = 0;
240184588Sdfr	cu->cu_cwnd_wait = FALSE;
241177633Sdfr	(void) getmicrotime(&now);
242177633Sdfr	cu->cu_xid = __RPC_GETXID(&now);
243177633Sdfr	call_msg.rm_xid = cu->cu_xid;
244177633Sdfr	call_msg.rm_call.cb_prog = program;
245177633Sdfr	call_msg.rm_call.cb_vers = version;
246177633Sdfr	xdrmem_create(&xdrs, cu->cu_mcallc, MCALL_MSG_SIZE, XDR_ENCODE);
247177633Sdfr	if (! xdr_callhdr(&xdrs, &call_msg)) {
248177633Sdfr		rpc_createerr.cf_stat = RPC_CANTENCODEARGS;  /* XXX */
249177633Sdfr		rpc_createerr.cf_error.re_errno = 0;
250177633Sdfr		goto err2;
251177633Sdfr	}
252177633Sdfr	cu->cu_mcalllen = XDR_GETPOS(&xdrs);;
253177633Sdfr
254177633Sdfr	/*
255177633Sdfr	 * By default, closeit is always FALSE. It is users responsibility
256177633Sdfr	 * to do a close on it, else the user may use clnt_control
257177633Sdfr	 * to let clnt_destroy do it for him/her.
258177633Sdfr	 */
259177633Sdfr	cu->cu_closeit = FALSE;
260177633Sdfr	cu->cu_socket = so;
261180025Sdfr	soreserve(so, 256*1024, 256*1024);
262177633Sdfr
263177633Sdfr	SOCKBUF_LOCK(&so->so_rcv);
264177633Sdfrrecheck_socket:
265177633Sdfr	if (so->so_upcall) {
266177633Sdfr		if (so->so_upcall != clnt_dg_soupcall) {
267177633Sdfr			SOCKBUF_UNLOCK(&so->so_rcv);
268177633Sdfr			printf("clnt_dg_create(): socket already has an incompatible upcall\n");
269177633Sdfr			goto err2;
270177633Sdfr		}
271177633Sdfr		cs = (struct cu_socket *) so->so_upcallarg;
272177633Sdfr		mtx_lock(&cs->cs_lock);
273177633Sdfr		cs->cs_refs++;
274177633Sdfr		mtx_unlock(&cs->cs_lock);
275177633Sdfr	} else {
276177633Sdfr		/*
277177633Sdfr		 * We are the first on this socket - allocate the
278177633Sdfr		 * structure and install it in the socket.
279177633Sdfr		 */
280177633Sdfr		SOCKBUF_UNLOCK(&cu->cu_socket->so_rcv);
281177633Sdfr		cs = mem_alloc(sizeof(*cs));
282177633Sdfr		SOCKBUF_LOCK(&cu->cu_socket->so_rcv);
283177633Sdfr		if (so->so_upcall) {
284177633Sdfr			/*
285177633Sdfr			 * We have lost a race with some other client.
286177633Sdfr			 */
287177633Sdfr			mem_free(cs, sizeof(*cs));
288177633Sdfr			goto recheck_socket;
289177633Sdfr		}
290177633Sdfr		mtx_init(&cs->cs_lock, "cs->cs_lock", NULL, MTX_DEF);
291177633Sdfr		cs->cs_refs = 1;
292177633Sdfr		TAILQ_INIT(&cs->cs_pending);
293177633Sdfr		so->so_upcallarg = cs;
294177633Sdfr		so->so_upcall = clnt_dg_soupcall;
295177633Sdfr		so->so_rcv.sb_flags |= SB_UPCALL;
296177633Sdfr	}
297177633Sdfr	SOCKBUF_UNLOCK(&so->so_rcv);
298177633Sdfr
299180025Sdfr	cl->cl_refs = 1;
300177633Sdfr	cl->cl_ops = &clnt_dg_ops;
301177633Sdfr	cl->cl_private = (caddr_t)(void *)cu;
302177633Sdfr	cl->cl_auth = authnone_create();
303177633Sdfr	cl->cl_tp = NULL;
304177633Sdfr	cl->cl_netid = NULL;
305177633Sdfr	return (cl);
306177633Sdfrerr2:
307177633Sdfr	if (cl) {
308177633Sdfr		mem_free(cl, sizeof (CLIENT));
309177633Sdfr		if (cu)
310177633Sdfr			mem_free(cu, sizeof (*cu));
311177633Sdfr	}
312177633Sdfr	return (NULL);
313177633Sdfr}
314177633Sdfr
315177633Sdfrstatic enum clnt_stat
316177633Sdfrclnt_dg_call(
317180025Sdfr	CLIENT		*cl,		/* client handle */
318180025Sdfr	struct rpc_callextra *ext,	/* call metadata */
319177633Sdfr	rpcproc_t	proc,		/* procedure number */
320184588Sdfr	struct mbuf	*args,		/* pointer to args */
321184588Sdfr	struct mbuf	**resultsp,	/* pointer to results */
322177633Sdfr	struct timeval	utimeout)	/* seconds to wait before giving up */
323177633Sdfr{
324177633Sdfr	struct cu_data *cu = (struct cu_data *)cl->cl_private;
325177633Sdfr	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
326184588Sdfr	struct rpc_timers *rt;
327180025Sdfr	AUTH *auth;
328184588Sdfr	struct rpc_err *errp;
329184588Sdfr	enum clnt_stat stat;
330177633Sdfr	XDR xdrs;
331177633Sdfr	struct rpc_msg reply_msg;
332177633Sdfr	bool_t ok;
333180025Sdfr	int retrans;			/* number of re-transmits so far */
334177633Sdfr	int nrefreshes = 2;		/* number of times to refresh cred */
335180025Sdfr	struct timeval *tvp;
336180025Sdfr	int timeout;
337180025Sdfr	int retransmit_time;
338184588Sdfr	int next_sendtime, starttime, rtt, time_waited, tv = 0;
339177633Sdfr	struct sockaddr *sa;
340177633Sdfr	socklen_t salen;
341184588Sdfr	uint32_t xid = 0;
342184588Sdfr	struct mbuf *mreq = NULL, *results;
343180025Sdfr	struct cu_request *cr;
344177633Sdfr	int error;
345177633Sdfr
346180025Sdfr	cr = malloc(sizeof(struct cu_request), M_RPC, M_WAITOK);
347180025Sdfr
348177633Sdfr	mtx_lock(&cs->cs_lock);
349177633Sdfr
350184588Sdfr	if (cu->cu_closing || cu->cu_closed) {
351180025Sdfr		mtx_unlock(&cs->cs_lock);
352180025Sdfr		free(cr, M_RPC);
353180025Sdfr		return (RPC_CANTSEND);
354180025Sdfr	}
355180025Sdfr	cu->cu_threads++;
356177633Sdfr
357184588Sdfr	if (ext) {
358180025Sdfr		auth = ext->rc_auth;
359184588Sdfr		errp = &ext->rc_err;
360184588Sdfr	} else {
361180025Sdfr		auth = cl->cl_auth;
362184588Sdfr		errp = &cu->cu_error;
363184588Sdfr	}
364180025Sdfr
365180025Sdfr	cr->cr_client = cl;
366180025Sdfr	cr->cr_mrep = NULL;
367180025Sdfr	cr->cr_error = 0;
368180025Sdfr
369177633Sdfr	if (cu->cu_total.tv_usec == -1) {
370180025Sdfr		tvp = &utimeout; /* use supplied timeout */
371177633Sdfr	} else {
372180025Sdfr		tvp = &cu->cu_total; /* use default timeout */
373177633Sdfr	}
374180025Sdfr	if (tvp->tv_sec || tvp->tv_usec)
375180025Sdfr		timeout = tvtohz(tvp);
376180025Sdfr	else
377180025Sdfr		timeout = 0;
378177633Sdfr
379177633Sdfr	if (cu->cu_connect && !cu->cu_connected) {
380177633Sdfr		mtx_unlock(&cs->cs_lock);
381177633Sdfr		error = soconnect(cu->cu_socket,
382177633Sdfr		    (struct sockaddr *)&cu->cu_raddr, curthread);
383177633Sdfr		mtx_lock(&cs->cs_lock);
384177633Sdfr		if (error) {
385184588Sdfr			errp->re_errno = error;
386184588Sdfr			errp->re_status = stat = RPC_CANTSEND;
387177633Sdfr			goto out;
388177633Sdfr		}
389177633Sdfr		cu->cu_connected = 1;
390177633Sdfr	}
391177633Sdfr	if (cu->cu_connected) {
392177633Sdfr		sa = NULL;
393177633Sdfr		salen = 0;
394177633Sdfr	} else {
395177633Sdfr		sa = (struct sockaddr *)&cu->cu_raddr;
396177633Sdfr		salen = cu->cu_rlen;
397177633Sdfr	}
398180025Sdfr	time_waited = 0;
399180025Sdfr	retrans = 0;
400184588Sdfr	if (ext && ext->rc_timers) {
401184588Sdfr		rt = ext->rc_timers;
402184588Sdfr		if (!rt->rt_rtxcur)
403184588Sdfr			rt->rt_rtxcur = tvtohz(&cu->cu_wait);
404184588Sdfr		retransmit_time = next_sendtime = rt->rt_rtxcur;
405184588Sdfr	} else {
406184588Sdfr		rt = NULL;
407184588Sdfr		retransmit_time = next_sendtime = tvtohz(&cu->cu_wait);
408184588Sdfr	}
409177633Sdfr
410180025Sdfr	starttime = ticks;
411177633Sdfr
412177633Sdfrcall_again:
413177633Sdfr	mtx_assert(&cs->cs_lock, MA_OWNED);
414177633Sdfr
415177633Sdfr	cu->cu_xid++;
416177633Sdfr	xid = cu->cu_xid;
417177633Sdfr
418177633Sdfrsend_again:
419177633Sdfr	mtx_unlock(&cs->cs_lock);
420177633Sdfr
421177633Sdfr	MGETHDR(mreq, M_WAIT, MT_DATA);
422184588Sdfr	KASSERT(cu->cu_mcalllen <= MHLEN, ("RPC header too big"));
423184588Sdfr	bcopy(cu->cu_mcallc, mreq->m_data, cu->cu_mcalllen);
424184588Sdfr	mreq->m_len = cu->cu_mcalllen;
425177633Sdfr
426177633Sdfr	/*
427177633Sdfr	 * The XID is the first thing in the request.
428177633Sdfr	 */
429177633Sdfr	*mtod(mreq, uint32_t *) = htonl(xid);
430177633Sdfr
431177633Sdfr	xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
432177633Sdfr
433184588Sdfr	if (cu->cu_async == TRUE && args == NULL)
434177633Sdfr		goto get_reply;
435177633Sdfr
436177633Sdfr	if ((! XDR_PUTINT32(&xdrs, &proc)) ||
437184588Sdfr	    (! AUTH_MARSHALL(auth, xid, &xdrs,
438184588Sdfr		m_copym(args, 0, M_COPYALL, M_WAITOK)))) {
439184588Sdfr		errp->re_status = stat = RPC_CANTENCODEARGS;
440177633Sdfr		mtx_lock(&cs->cs_lock);
441177633Sdfr		goto out;
442177633Sdfr	}
443184588Sdfr	mreq->m_pkthdr.len = m_length(mreq, NULL);
444177633Sdfr
445180025Sdfr	cr->cr_xid = xid;
446177633Sdfr	mtx_lock(&cs->cs_lock);
447184588Sdfr
448184588Sdfr	/*
449184588Sdfr	 * Try to get a place in the congestion window.
450184588Sdfr	 */
451184588Sdfr	while (cu->cu_sent >= cu->cu_cwnd) {
452184588Sdfr		cu->cu_cwnd_wait = TRUE;
453184588Sdfr		error = msleep(&cu->cu_cwnd_wait, &cs->cs_lock,
454184588Sdfr		    cu->cu_waitflag, "rpccwnd", 0);
455184588Sdfr		if (error) {
456184588Sdfr			errp->re_errno = error;
457184588Sdfr			errp->re_status = stat = RPC_CANTSEND;
458184588Sdfr			goto out;
459184588Sdfr		}
460184588Sdfr	}
461184588Sdfr	cu->cu_sent += CWNDSCALE;
462184588Sdfr
463180025Sdfr	TAILQ_INSERT_TAIL(&cs->cs_pending, cr, cr_link);
464177633Sdfr	mtx_unlock(&cs->cs_lock);
465177633Sdfr
466177633Sdfr	/*
467177633Sdfr	 * sosend consumes mreq.
468177633Sdfr	 */
469177633Sdfr	error = sosend(cu->cu_socket, sa, NULL, mreq, NULL, 0, curthread);
470177633Sdfr	mreq = NULL;
471177633Sdfr
472177633Sdfr	/*
473177633Sdfr	 * sub-optimal code appears here because we have
474177633Sdfr	 * some clock time to spare while the packets are in flight.
475177633Sdfr	 * (We assume that this is actually only executed once.)
476177633Sdfr	 */
477184588Sdfr	reply_msg.acpted_rply.ar_verf.oa_flavor = AUTH_NULL;
478184588Sdfr	reply_msg.acpted_rply.ar_verf.oa_base = cr->cr_verf;
479184588Sdfr	reply_msg.acpted_rply.ar_verf.oa_length = 0;
480184588Sdfr	reply_msg.acpted_rply.ar_results.where = NULL;
481184588Sdfr	reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
482177633Sdfr
483177633Sdfr	mtx_lock(&cs->cs_lock);
484177633Sdfr	if (error) {
485180025Sdfr		TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
486184588Sdfr		errp->re_errno = error;
487184588Sdfr		errp->re_status = stat = RPC_CANTSEND;
488184588Sdfr		cu->cu_sent -= CWNDSCALE;
489184588Sdfr		if (cu->cu_cwnd_wait) {
490184588Sdfr			cu->cu_cwnd_wait = FALSE;
491184588Sdfr			wakeup(&cu->cu_cwnd_wait);
492184588Sdfr		}
493177633Sdfr		goto out;
494177633Sdfr	}
495177633Sdfr
496177633Sdfr	/*
497177633Sdfr	 * Check to see if we got an upcall while waiting for the
498180025Sdfr	 * lock.
499177633Sdfr	 */
500180025Sdfr	if (cr->cr_error) {
501180025Sdfr		TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
502184588Sdfr		errp->re_errno = cr->cr_error;
503184588Sdfr		errp->re_status = stat = RPC_CANTRECV;
504184588Sdfr		cu->cu_sent -= CWNDSCALE;
505184588Sdfr		if (cu->cu_cwnd_wait) {
506184588Sdfr			cu->cu_cwnd_wait = FALSE;
507184588Sdfr			wakeup(&cu->cu_cwnd_wait);
508184588Sdfr		}
509177633Sdfr		goto out;
510177633Sdfr	}
511180025Sdfr	if (cr->cr_mrep) {
512180025Sdfr		TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
513184588Sdfr		cu->cu_sent -= CWNDSCALE;
514184588Sdfr		if (cu->cu_cwnd_wait) {
515184588Sdfr			cu->cu_cwnd_wait = FALSE;
516184588Sdfr			wakeup(&cu->cu_cwnd_wait);
517184588Sdfr		}
518177633Sdfr		goto got_reply;
519177633Sdfr	}
520177633Sdfr
521177633Sdfr	/*
522177633Sdfr	 * Hack to provide rpc-based message passing
523177633Sdfr	 */
524180025Sdfr	if (timeout == 0) {
525180025Sdfr		TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
526184588Sdfr		errp->re_status = stat = RPC_TIMEDOUT;
527184588Sdfr		cu->cu_sent -= CWNDSCALE;
528184588Sdfr		if (cu->cu_cwnd_wait) {
529184588Sdfr			cu->cu_cwnd_wait = FALSE;
530184588Sdfr			wakeup(&cu->cu_cwnd_wait);
531184588Sdfr		}
532177633Sdfr		goto out;
533177633Sdfr	}
534177633Sdfr
535177633Sdfrget_reply:
536177633Sdfr	for (;;) {
537177633Sdfr		/* Decide how long to wait. */
538180025Sdfr		if (next_sendtime < timeout)
539177633Sdfr			tv = next_sendtime;
540180025Sdfr		else
541180025Sdfr			tv = timeout;
542180025Sdfr		tv -= time_waited;
543180025Sdfr
544180025Sdfr		if (tv > 0) {
545184588Sdfr			if (cu->cu_closing || cu->cu_closed)
546180025Sdfr				error = 0;
547180025Sdfr			else
548180025Sdfr				error = msleep(cr, &cs->cs_lock,
549180025Sdfr				    cu->cu_waitflag, cu->cu_waitchan, tv);
550177633Sdfr		} else {
551180025Sdfr			error = EWOULDBLOCK;
552177633Sdfr		}
553177633Sdfr
554180025Sdfr		TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
555184588Sdfr		cu->cu_sent -= CWNDSCALE;
556184588Sdfr		if (cu->cu_cwnd_wait) {
557184588Sdfr			cu->cu_cwnd_wait = FALSE;
558184588Sdfr			wakeup(&cu->cu_cwnd_wait);
559184588Sdfr		}
560177633Sdfr
561177633Sdfr		if (!error) {
562177633Sdfr			/*
563177633Sdfr			 * We were woken up by the upcall.  If the
564177633Sdfr			 * upcall had a receive error, report that,
565177633Sdfr			 * otherwise we have a reply.
566177633Sdfr			 */
567180025Sdfr			if (cr->cr_error) {
568184588Sdfr				errp->re_errno = cr->cr_error;
569184588Sdfr				errp->re_status = stat = RPC_CANTRECV;
570177633Sdfr				goto out;
571177633Sdfr			}
572184588Sdfr
573184588Sdfr			cu->cu_cwnd += (CWNDSCALE * CWNDSCALE
574184588Sdfr			    + cu->cu_cwnd / 2) / cu->cu_cwnd;
575184588Sdfr			if (cu->cu_cwnd > MAXCWND)
576184588Sdfr				cu->cu_cwnd = MAXCWND;
577184588Sdfr
578184588Sdfr			if (rt) {
579184588Sdfr				/*
580184588Sdfr				 * Add one to the time since a tick
581184588Sdfr				 * count of N means that the actual
582184588Sdfr				 * time taken was somewhere between N
583184588Sdfr				 * and N+1.
584184588Sdfr				 */
585184588Sdfr				rtt = ticks - starttime + 1;
586184588Sdfr
587184588Sdfr				/*
588184588Sdfr				 * Update our estimate of the round
589184588Sdfr				 * trip time using roughly the
590184588Sdfr				 * algorithm described in RFC
591184588Sdfr				 * 2988. Given an RTT sample R:
592184588Sdfr				 *
593184588Sdfr				 * RTTVAR = (1-beta) * RTTVAR + beta * |SRTT-R|
594184588Sdfr				 * SRTT = (1-alpha) * SRTT + alpha * R
595184588Sdfr				 *
596184588Sdfr				 * where alpha = 0.125 and beta = 0.25.
597184588Sdfr				 *
598184588Sdfr				 * The initial retransmit timeout is
599184588Sdfr				 * SRTT + 4*RTTVAR and doubles on each
600184588Sdfr				 * retransmision.
601184588Sdfr				 */
602184588Sdfr				if (rt->rt_srtt == 0) {
603184588Sdfr					rt->rt_srtt = rtt;
604184588Sdfr					rt->rt_deviate = rtt / 2;
605184588Sdfr				} else {
606184588Sdfr					int32_t error = rtt - rt->rt_srtt;
607184588Sdfr					rt->rt_srtt += error / 8;
608184588Sdfr					error = abs(error) - rt->rt_deviate;
609184588Sdfr					rt->rt_deviate += error / 4;
610184588Sdfr				}
611184588Sdfr				rt->rt_rtxcur = rt->rt_srtt + 4*rt->rt_deviate;
612184588Sdfr			}
613184588Sdfr
614177633Sdfr			break;
615177633Sdfr		}
616177633Sdfr
617177633Sdfr		/*
618177633Sdfr		 * The sleep returned an error so our request is still
619177633Sdfr		 * on the list. If we got EWOULDBLOCK, we may want to
620177633Sdfr		 * re-send the request.
621177633Sdfr		 */
622177633Sdfr		if (error != EWOULDBLOCK) {
623184588Sdfr			errp->re_errno = error;
624177633Sdfr			if (error == EINTR)
625184588Sdfr				errp->re_status = stat = RPC_INTR;
626177633Sdfr			else
627184588Sdfr				errp->re_status = stat = RPC_CANTRECV;
628177633Sdfr			goto out;
629177633Sdfr		}
630177633Sdfr
631180025Sdfr		time_waited = ticks - starttime;
632177633Sdfr
633177633Sdfr		/* Check for timeout. */
634180025Sdfr		if (time_waited > timeout) {
635184588Sdfr			errp->re_errno = EWOULDBLOCK;
636184588Sdfr			errp->re_status = stat = RPC_TIMEDOUT;
637177633Sdfr			goto out;
638177633Sdfr		}
639177633Sdfr
640177633Sdfr		/* Retransmit if necessary. */
641180025Sdfr		if (time_waited >= next_sendtime) {
642184588Sdfr			cu->cu_cwnd /= 2;
643184588Sdfr			if (cu->cu_cwnd < CWNDSCALE)
644184588Sdfr				cu->cu_cwnd = CWNDSCALE;
645180025Sdfr			if (ext && ext->rc_feedback) {
646180025Sdfr				mtx_unlock(&cs->cs_lock);
647180025Sdfr				if (retrans == 0)
648180025Sdfr					ext->rc_feedback(FEEDBACK_REXMIT1,
649180025Sdfr					    proc, ext->rc_feedback_arg);
650180025Sdfr				else
651180025Sdfr					ext->rc_feedback(FEEDBACK_REXMIT2,
652180025Sdfr					    proc, ext->rc_feedback_arg);
653180025Sdfr				mtx_lock(&cs->cs_lock);
654180025Sdfr			}
655184588Sdfr			if (cu->cu_closing || cu->cu_closed) {
656184588Sdfr				errp->re_errno = ESHUTDOWN;
657184588Sdfr				errp->re_status = stat = RPC_CANTRECV;
658180025Sdfr				goto out;
659180025Sdfr			}
660180025Sdfr			retrans++;
661177633Sdfr			/* update retransmit_time */
662180025Sdfr			if (retransmit_time < RPC_MAX_BACKOFF * hz)
663180025Sdfr				retransmit_time = 2 * retransmit_time;
664180025Sdfr			next_sendtime += retransmit_time;
665177633Sdfr			goto send_again;
666177633Sdfr		}
667180025Sdfr		TAILQ_INSERT_TAIL(&cs->cs_pending, cr, cr_link);
668177633Sdfr	}
669177633Sdfr
670177633Sdfrgot_reply:
671177633Sdfr	/*
672177633Sdfr	 * Now decode and validate the response. We need to drop the
673177633Sdfr	 * lock since xdr_replymsg may end up sleeping in malloc.
674177633Sdfr	 */
675177633Sdfr	mtx_unlock(&cs->cs_lock);
676177633Sdfr
677180025Sdfr	if (ext && ext->rc_feedback)
678180025Sdfr		ext->rc_feedback(FEEDBACK_OK, proc, ext->rc_feedback_arg);
679180025Sdfr
680180025Sdfr	xdrmbuf_create(&xdrs, cr->cr_mrep, XDR_DECODE);
681177633Sdfr	ok = xdr_replymsg(&xdrs, &reply_msg);
682180025Sdfr	cr->cr_mrep = NULL;
683177633Sdfr
684177633Sdfr	if (ok) {
685177633Sdfr		if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
686184588Sdfr		    (reply_msg.acpted_rply.ar_stat == SUCCESS))
687184588Sdfr			errp->re_status = stat = RPC_SUCCESS;
688177633Sdfr		else
689184588Sdfr			stat = _seterr_reply(&reply_msg, &(cu->cu_error));
690177633Sdfr
691184588Sdfr		if (errp->re_status == RPC_SUCCESS) {
692184588Sdfr			results = xdrmbuf_getall(&xdrs);
693184588Sdfr			if (! AUTH_VALIDATE(auth, xid,
694184588Sdfr				&reply_msg.acpted_rply.ar_verf,
695184588Sdfr				&results)) {
696184588Sdfr				errp->re_status = stat = RPC_AUTHERROR;
697184588Sdfr				errp->re_why = AUTH_INVALIDRESP;
698184588Sdfr				if (retrans &&
699184588Sdfr				    auth->ah_cred.oa_flavor == RPCSEC_GSS) {
700184588Sdfr					/*
701184588Sdfr					 * If we retransmitted, its
702184588Sdfr					 * possible that we will
703184588Sdfr					 * receive a reply for one of
704184588Sdfr					 * the earlier transmissions
705184588Sdfr					 * (which will use an older
706184588Sdfr					 * RPCSEC_GSS sequence
707184588Sdfr					 * number). In this case, just
708184588Sdfr					 * go back and listen for a
709184588Sdfr					 * new reply. We could keep a
710184588Sdfr					 * record of all the seq
711184588Sdfr					 * numbers we have transmitted
712184588Sdfr					 * so far so that we could
713184588Sdfr					 * accept a reply for any of
714184588Sdfr					 * them here.
715184588Sdfr					 */
716184588Sdfr					XDR_DESTROY(&xdrs);
717184588Sdfr					mtx_lock(&cs->cs_lock);
718184588Sdfr					TAILQ_INSERT_TAIL(&cs->cs_pending,
719184588Sdfr					    cr, cr_link);
720184588Sdfr					cr->cr_mrep = NULL;
721184588Sdfr					goto get_reply;
722184588Sdfr				}
723184588Sdfr			} else {
724184588Sdfr				*resultsp = results;
725177633Sdfr			}
726177633Sdfr		}		/* end successful completion */
727177633Sdfr		/*
728177633Sdfr		 * If unsuccesful AND error is an authentication error
729177633Sdfr		 * then refresh credentials and try again, else break
730177633Sdfr		 */
731184588Sdfr		else if (stat == RPC_AUTHERROR)
732177633Sdfr			/* maybe our credentials need to be refreshed ... */
733177633Sdfr			if (nrefreshes > 0 &&
734184588Sdfr			    AUTH_REFRESH(auth, &reply_msg)) {
735177633Sdfr				nrefreshes--;
736184588Sdfr				XDR_DESTROY(&xdrs);
737184588Sdfr				mtx_lock(&cs->cs_lock);
738177633Sdfr				goto call_again;
739177633Sdfr			}
740177633Sdfr		/* end of unsuccessful completion */
741177633Sdfr	}	/* end of valid reply message */
742177633Sdfr	else {
743184588Sdfr		errp->re_status = stat = RPC_CANTDECODERES;
744177633Sdfr
745177633Sdfr	}
746184588Sdfr	XDR_DESTROY(&xdrs);
747184588Sdfr	mtx_lock(&cs->cs_lock);
748177633Sdfrout:
749177633Sdfr	mtx_assert(&cs->cs_lock, MA_OWNED);
750177633Sdfr
751177633Sdfr	if (mreq)
752177633Sdfr		m_freem(mreq);
753180025Sdfr	if (cr->cr_mrep)
754180025Sdfr		m_freem(cr->cr_mrep);
755177633Sdfr
756180025Sdfr	cu->cu_threads--;
757180025Sdfr	if (cu->cu_closing)
758180025Sdfr		wakeup(cu);
759180025Sdfr
760177633Sdfr	mtx_unlock(&cs->cs_lock);
761180025Sdfr
762184588Sdfr	if (auth && stat != RPC_SUCCESS)
763184588Sdfr		AUTH_VALIDATE(auth, xid, NULL, NULL);
764184588Sdfr
765180025Sdfr	free(cr, M_RPC);
766180025Sdfr
767184588Sdfr	return (stat);
768177633Sdfr}
769177633Sdfr
770177633Sdfrstatic void
771177633Sdfrclnt_dg_geterr(CLIENT *cl, struct rpc_err *errp)
772177633Sdfr{
773177633Sdfr	struct cu_data *cu = (struct cu_data *)cl->cl_private;
774177633Sdfr
775177633Sdfr	*errp = cu->cu_error;
776177633Sdfr}
777177633Sdfr
778177633Sdfrstatic bool_t
779177633Sdfrclnt_dg_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
780177633Sdfr{
781177633Sdfr	XDR xdrs;
782177633Sdfr	bool_t dummy;
783177633Sdfr
784177633Sdfr	xdrs.x_op = XDR_FREE;
785177633Sdfr	dummy = (*xdr_res)(&xdrs, res_ptr);
786177633Sdfr
787177633Sdfr	return (dummy);
788177633Sdfr}
789177633Sdfr
790177633Sdfr/*ARGSUSED*/
791177633Sdfrstatic void
792177633Sdfrclnt_dg_abort(CLIENT *h)
793177633Sdfr{
794177633Sdfr}
795177633Sdfr
796177633Sdfrstatic bool_t
797177633Sdfrclnt_dg_control(CLIENT *cl, u_int request, void *info)
798177633Sdfr{
799177633Sdfr	struct cu_data *cu = (struct cu_data *)cl->cl_private;
800177633Sdfr	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
801177633Sdfr	struct sockaddr *addr;
802177633Sdfr
803177633Sdfr	mtx_lock(&cs->cs_lock);
804177633Sdfr
805177633Sdfr	switch (request) {
806177633Sdfr	case CLSET_FD_CLOSE:
807177633Sdfr		cu->cu_closeit = TRUE;
808177633Sdfr		mtx_unlock(&cs->cs_lock);
809177633Sdfr		return (TRUE);
810177633Sdfr	case CLSET_FD_NCLOSE:
811177633Sdfr		cu->cu_closeit = FALSE;
812177633Sdfr		mtx_unlock(&cs->cs_lock);
813177633Sdfr		return (TRUE);
814177633Sdfr	}
815177633Sdfr
816177633Sdfr	/* for other requests which use info */
817177633Sdfr	if (info == NULL) {
818177633Sdfr		mtx_unlock(&cs->cs_lock);
819177633Sdfr		return (FALSE);
820177633Sdfr	}
821177633Sdfr	switch (request) {
822177633Sdfr	case CLSET_TIMEOUT:
823177633Sdfr		if (time_not_ok((struct timeval *)info)) {
824177633Sdfr			mtx_unlock(&cs->cs_lock);
825177633Sdfr			return (FALSE);
826177633Sdfr		}
827177633Sdfr		cu->cu_total = *(struct timeval *)info;
828177633Sdfr		break;
829177633Sdfr	case CLGET_TIMEOUT:
830177633Sdfr		*(struct timeval *)info = cu->cu_total;
831177633Sdfr		break;
832177633Sdfr	case CLSET_RETRY_TIMEOUT:
833177633Sdfr		if (time_not_ok((struct timeval *)info)) {
834177633Sdfr			mtx_unlock(&cs->cs_lock);
835177633Sdfr			return (FALSE);
836177633Sdfr		}
837177633Sdfr		cu->cu_wait = *(struct timeval *)info;
838177633Sdfr		break;
839177633Sdfr	case CLGET_RETRY_TIMEOUT:
840177633Sdfr		*(struct timeval *)info = cu->cu_wait;
841177633Sdfr		break;
842177633Sdfr	case CLGET_SVC_ADDR:
843177633Sdfr		/*
844177633Sdfr		 * Slightly different semantics to userland - we use
845177633Sdfr		 * sockaddr instead of netbuf.
846177633Sdfr		 */
847177633Sdfr		memcpy(info, &cu->cu_raddr, cu->cu_raddr.ss_len);
848177633Sdfr		break;
849177633Sdfr	case CLSET_SVC_ADDR:		/* set to new address */
850177633Sdfr		addr = (struct sockaddr *)info;
851177633Sdfr		(void) memcpy(&cu->cu_raddr, addr, addr->sa_len);
852177633Sdfr		break;
853177633Sdfr	case CLGET_XID:
854177633Sdfr		*(uint32_t *)info = cu->cu_xid;
855177633Sdfr		break;
856177633Sdfr
857177633Sdfr	case CLSET_XID:
858177633Sdfr		/* This will set the xid of the NEXT call */
859177633Sdfr		/* decrement by 1 as clnt_dg_call() increments once */
860177633Sdfr		cu->cu_xid = *(uint32_t *)info - 1;
861177633Sdfr		break;
862177633Sdfr
863177633Sdfr	case CLGET_VERS:
864177633Sdfr		/*
865177633Sdfr		 * This RELIES on the information that, in the call body,
866177633Sdfr		 * the version number field is the fifth field from the
867177633Sdfr		 * begining of the RPC header. MUST be changed if the
868177633Sdfr		 * call_struct is changed
869177633Sdfr		 */
870177633Sdfr		*(uint32_t *)info =
871177633Sdfr		    ntohl(*(uint32_t *)(void *)(cu->cu_mcallc +
872177633Sdfr		    4 * BYTES_PER_XDR_UNIT));
873177633Sdfr		break;
874177633Sdfr
875177633Sdfr	case CLSET_VERS:
876177633Sdfr		*(uint32_t *)(void *)(cu->cu_mcallc + 4 * BYTES_PER_XDR_UNIT)
877177633Sdfr			= htonl(*(uint32_t *)info);
878177633Sdfr		break;
879177633Sdfr
880177633Sdfr	case CLGET_PROG:
881177633Sdfr		/*
882177633Sdfr		 * This RELIES on the information that, in the call body,
883177633Sdfr		 * the program number field is the fourth field from the
884177633Sdfr		 * begining of the RPC header. MUST be changed if the
885177633Sdfr		 * call_struct is changed
886177633Sdfr		 */
887177633Sdfr		*(uint32_t *)info =
888177633Sdfr		    ntohl(*(uint32_t *)(void *)(cu->cu_mcallc +
889177633Sdfr		    3 * BYTES_PER_XDR_UNIT));
890177633Sdfr		break;
891177633Sdfr
892177633Sdfr	case CLSET_PROG:
893177633Sdfr		*(uint32_t *)(void *)(cu->cu_mcallc + 3 * BYTES_PER_XDR_UNIT)
894177633Sdfr			= htonl(*(uint32_t *)info);
895177633Sdfr		break;
896177633Sdfr	case CLSET_ASYNC:
897177633Sdfr		cu->cu_async = *(int *)info;
898177633Sdfr		break;
899177633Sdfr	case CLSET_CONNECT:
900177633Sdfr		cu->cu_connect = *(int *)info;
901177633Sdfr		break;
902177633Sdfr	case CLSET_WAITCHAN:
903184588Sdfr		cu->cu_waitchan = (const char *)info;
904177633Sdfr		break;
905177633Sdfr	case CLGET_WAITCHAN:
906177633Sdfr		*(const char **) info = cu->cu_waitchan;
907177633Sdfr		break;
908177633Sdfr	case CLSET_INTERRUPTIBLE:
909177633Sdfr		if (*(int *) info)
910177633Sdfr			cu->cu_waitflag = PCATCH;
911177633Sdfr		else
912177633Sdfr			cu->cu_waitflag = 0;
913177633Sdfr		break;
914177633Sdfr	case CLGET_INTERRUPTIBLE:
915177633Sdfr		if (cu->cu_waitflag)
916177633Sdfr			*(int *) info = TRUE;
917177633Sdfr		else
918177633Sdfr			*(int *) info = FALSE;
919177633Sdfr		break;
920177633Sdfr	default:
921177633Sdfr		mtx_unlock(&cs->cs_lock);
922177633Sdfr		return (FALSE);
923177633Sdfr	}
924177633Sdfr	mtx_unlock(&cs->cs_lock);
925177633Sdfr	return (TRUE);
926177633Sdfr}
927177633Sdfr
928177633Sdfrstatic void
929184588Sdfrclnt_dg_close(CLIENT *cl)
930177633Sdfr{
931177633Sdfr	struct cu_data *cu = (struct cu_data *)cl->cl_private;
932177633Sdfr	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
933180025Sdfr	struct cu_request *cr;
934177633Sdfr
935180025Sdfr	mtx_lock(&cs->cs_lock);
936177633Sdfr
937184588Sdfr	if (cu->cu_closed) {
938184588Sdfr		mtx_unlock(&cs->cs_lock);
939184588Sdfr		return;
940184588Sdfr	}
941184588Sdfr
942184588Sdfr	if (cu->cu_closing) {
943184588Sdfr		while (cu->cu_closing)
944184588Sdfr			msleep(cu, &cs->cs_lock, 0, "rpcclose", 0);
945184588Sdfr		KASSERT(cu->cu_closed, ("client should be closed"));
946184588Sdfr		mtx_unlock(&cs->cs_lock);
947184588Sdfr		return;
948184588Sdfr	}
949184588Sdfr
950180025Sdfr	/*
951180025Sdfr	 * Abort any pending requests and wait until everyone
952180025Sdfr	 * has finished with clnt_vc_call.
953180025Sdfr	 */
954180025Sdfr	cu->cu_closing = TRUE;
955180025Sdfr	TAILQ_FOREACH(cr, &cs->cs_pending, cr_link) {
956180025Sdfr		if (cr->cr_client == cl) {
957180025Sdfr			cr->cr_xid = 0;
958180025Sdfr			cr->cr_error = ESHUTDOWN;
959180025Sdfr			wakeup(cr);
960180025Sdfr		}
961180025Sdfr	}
962180025Sdfr
963180025Sdfr	while (cu->cu_threads)
964180025Sdfr		msleep(cu, &cs->cs_lock, 0, "rpcclose", 0);
965180025Sdfr
966184588Sdfr	cu->cu_closing = FALSE;
967184588Sdfr	cu->cu_closed = TRUE;
968184588Sdfr
969184588Sdfr	mtx_unlock(&cs->cs_lock);
970184588Sdfr	wakeup(cu);
971184588Sdfr}
972184588Sdfr
973184588Sdfrstatic void
974184588Sdfrclnt_dg_destroy(CLIENT *cl)
975184588Sdfr{
976184588Sdfr	struct cu_data *cu = (struct cu_data *)cl->cl_private;
977184588Sdfr	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
978184588Sdfr	struct socket *so = NULL;
979184588Sdfr	bool_t lastsocketref;
980184588Sdfr
981184588Sdfr	clnt_dg_close(cl);
982184588Sdfr
983184588Sdfr	mtx_lock(&cs->cs_lock);
984184588Sdfr
985177633Sdfr	cs->cs_refs--;
986177633Sdfr	if (cs->cs_refs == 0) {
987180025Sdfr		mtx_destroy(&cs->cs_lock);
988180025Sdfr		SOCKBUF_LOCK(&cu->cu_socket->so_rcv);
989177633Sdfr		cu->cu_socket->so_upcallarg = NULL;
990177633Sdfr		cu->cu_socket->so_upcall = NULL;
991177633Sdfr		cu->cu_socket->so_rcv.sb_flags &= ~SB_UPCALL;
992177633Sdfr		SOCKBUF_UNLOCK(&cu->cu_socket->so_rcv);
993177633Sdfr		mem_free(cs, sizeof(*cs));
994177633Sdfr		lastsocketref = TRUE;
995177633Sdfr	} else {
996177633Sdfr		mtx_unlock(&cs->cs_lock);
997177633Sdfr		lastsocketref = FALSE;
998177633Sdfr	}
999177633Sdfr
1000180025Sdfr	if (cu->cu_closeit && lastsocketref) {
1001177633Sdfr		so = cu->cu_socket;
1002177633Sdfr		cu->cu_socket = NULL;
1003177633Sdfr	}
1004177633Sdfr
1005177633Sdfr	if (so)
1006177633Sdfr		soclose(so);
1007177633Sdfr
1008177633Sdfr	if (cl->cl_netid && cl->cl_netid[0])
1009177633Sdfr		mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
1010177633Sdfr	if (cl->cl_tp && cl->cl_tp[0])
1011177633Sdfr		mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
1012177633Sdfr	mem_free(cu, sizeof (*cu));
1013177633Sdfr	mem_free(cl, sizeof (CLIENT));
1014177633Sdfr}
1015177633Sdfr
1016177633Sdfr/*
1017177633Sdfr * Make sure that the time is not garbage.  -1 value is allowed.
1018177633Sdfr */
1019177633Sdfrstatic bool_t
1020177633Sdfrtime_not_ok(struct timeval *t)
1021177633Sdfr{
1022177633Sdfr	return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
1023177633Sdfr		t->tv_usec < -1 || t->tv_usec > 1000000);
1024177633Sdfr}
1025177633Sdfr
1026177633Sdfrvoid
1027177633Sdfrclnt_dg_soupcall(struct socket *so, void *arg, int waitflag)
1028177633Sdfr{
1029177633Sdfr	struct cu_socket *cs = (struct cu_socket *) arg;
1030177633Sdfr	struct uio uio;
1031177633Sdfr	struct mbuf *m;
1032177633Sdfr	struct mbuf *control;
1033177633Sdfr	struct cu_request *cr;
1034177633Sdfr	int error, rcvflag, foundreq;
1035177633Sdfr	uint32_t xid;
1036177633Sdfr
1037177633Sdfr	uio.uio_resid = 1000000000;
1038177633Sdfr	uio.uio_td = curthread;
1039177633Sdfr	do {
1040177633Sdfr		m = NULL;
1041177633Sdfr		control = NULL;
1042177633Sdfr		rcvflag = MSG_DONTWAIT;
1043177633Sdfr		error = soreceive(so, NULL, &uio, &m, &control, &rcvflag);
1044177633Sdfr		if (control)
1045177633Sdfr			m_freem(control);
1046177633Sdfr
1047177633Sdfr		if (error == EWOULDBLOCK)
1048177633Sdfr			break;
1049177633Sdfr
1050177633Sdfr		/*
1051177633Sdfr		 * If there was an error, wake up all pending
1052177633Sdfr		 * requests.
1053177633Sdfr		 */
1054177633Sdfr		if (error) {
1055177633Sdfr			mtx_lock(&cs->cs_lock);
1056177633Sdfr			TAILQ_FOREACH(cr, &cs->cs_pending, cr_link) {
1057180025Sdfr				cr->cr_xid = 0;
1058177633Sdfr				cr->cr_error = error;
1059177633Sdfr				wakeup(cr);
1060177633Sdfr			}
1061177633Sdfr			mtx_unlock(&cs->cs_lock);
1062177633Sdfr			break;
1063177633Sdfr		}
1064177633Sdfr
1065177633Sdfr		/*
1066177633Sdfr		 * The XID is in the first uint32_t of the reply.
1067177633Sdfr		 */
1068184588Sdfr		if (m->m_len < sizeof(xid))
1069184588Sdfr			m = m_pullup(m, sizeof(xid));
1070177633Sdfr		if (!m)
1071180025Sdfr			/*
1072180025Sdfr			 * Should never happen.
1073180025Sdfr			 */
1074180025Sdfr			continue;
1075180025Sdfr
1076177633Sdfr		xid = ntohl(*mtod(m, uint32_t *));
1077177633Sdfr
1078177633Sdfr		/*
1079177633Sdfr		 * Attempt to match this reply with a pending request.
1080177633Sdfr		 */
1081177633Sdfr		mtx_lock(&cs->cs_lock);
1082177633Sdfr		foundreq = 0;
1083177633Sdfr		TAILQ_FOREACH(cr, &cs->cs_pending, cr_link) {
1084177633Sdfr			if (cr->cr_xid == xid) {
1085177633Sdfr				/*
1086180025Sdfr				 * This one matches. We leave the
1087177633Sdfr				 * reply mbuf in cr->cr_mrep. Set the
1088180025Sdfr				 * XID to zero so that we will ignore
1089180025Sdfr				 * any duplicated replies that arrive
1090180025Sdfr				 * before clnt_dg_call removes it from
1091180025Sdfr				 * the queue.
1092177633Sdfr				 */
1093177633Sdfr				cr->cr_xid = 0;
1094177633Sdfr				cr->cr_mrep = m;
1095177633Sdfr				cr->cr_error = 0;
1096177633Sdfr				foundreq = 1;
1097177633Sdfr				wakeup(cr);
1098177633Sdfr				break;
1099177633Sdfr			}
1100177633Sdfr		}
1101177633Sdfr		mtx_unlock(&cs->cs_lock);
1102177633Sdfr
1103177633Sdfr		/*
1104177633Sdfr		 * If we didn't find the matching request, just drop
1105177633Sdfr		 * it - its probably a repeated reply.
1106177633Sdfr		 */
1107177633Sdfr		if (!foundreq)
1108177633Sdfr			m_freem(m);
1109177633Sdfr	} while (m);
1110177633Sdfr}
1111177633Sdfr
1112