clnt_rc.c revision 261058
1219820Sjeff/*-
2219820Sjeff * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3219820Sjeff * Authors: Doug Rabson <dfr@rabson.org>
4219820Sjeff * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5219820Sjeff *
6219820Sjeff * Redistribution and use in source and binary forms, with or without
7219820Sjeff * modification, are permitted provided that the following conditions
8219820Sjeff * are met:
9219820Sjeff * 1. Redistributions of source code must retain the above copyright
10219820Sjeff *    notice, this list of conditions and the following disclaimer.
11219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
12219820Sjeff *    notice, this list of conditions and the following disclaimer in the
13219820Sjeff *    documentation and/or other materials provided with the distribution.
14219820Sjeff *
15219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18219820Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25219820Sjeff * SUCH DAMAGE.
26219820Sjeff */
27219820Sjeff
28219820Sjeff#include <sys/cdefs.h>
29219820Sjeff__FBSDID("$FreeBSD: stable/9/sys/rpc/clnt_rc.c 261058 2014-01-23 00:35:41Z mav $");
30219820Sjeff
31219820Sjeff#include <sys/param.h>
32219820Sjeff#include <sys/systm.h>
33219820Sjeff#include <sys/kernel.h>
34219820Sjeff#include <sys/limits.h>
35219820Sjeff#include <sys/lock.h>
36219820Sjeff#include <sys/malloc.h>
37219820Sjeff#include <sys/mbuf.h>
38219820Sjeff#include <sys/mutex.h>
39219820Sjeff#include <sys/pcpu.h>
40219820Sjeff#include <sys/proc.h>
41219820Sjeff#include <sys/socket.h>
42219820Sjeff#include <sys/socketvar.h>
43219820Sjeff#include <sys/time.h>
44219820Sjeff#include <sys/uio.h>
45219820Sjeff
46219820Sjeff#include <rpc/rpc.h>
47219820Sjeff#include <rpc/rpc_com.h>
48219820Sjeff#include <rpc/krpc.h>
49219820Sjeff
50219820Sjeffstatic enum clnt_stat clnt_reconnect_call(CLIENT *, struct rpc_callextra *,
51219820Sjeff    rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
52219820Sjeffstatic void clnt_reconnect_geterr(CLIENT *, struct rpc_err *);
53219820Sjeffstatic bool_t clnt_reconnect_freeres(CLIENT *, xdrproc_t, void *);
54219820Sjeffstatic void clnt_reconnect_abort(CLIENT *);
55219820Sjeffstatic bool_t clnt_reconnect_control(CLIENT *, u_int, void *);
56219820Sjeffstatic void clnt_reconnect_close(CLIENT *);
57219820Sjeffstatic void clnt_reconnect_destroy(CLIENT *);
58219820Sjeff
59219820Sjeffstatic struct clnt_ops clnt_reconnect_ops = {
60219820Sjeff	.cl_call =	clnt_reconnect_call,
61219820Sjeff	.cl_abort =	clnt_reconnect_abort,
62219820Sjeff	.cl_geterr =	clnt_reconnect_geterr,
63219820Sjeff	.cl_freeres =	clnt_reconnect_freeres,
64219820Sjeff	.cl_close =	clnt_reconnect_close,
65219820Sjeff	.cl_destroy =	clnt_reconnect_destroy,
66219820Sjeff	.cl_control =	clnt_reconnect_control
67219820Sjeff};
68219820Sjeff
69219820Sjeffstatic int	fake_wchan;
70219820Sjeff
71219820SjeffCLIENT *
72219820Sjeffclnt_reconnect_create(
73219820Sjeff	struct netconfig *nconf,	/* network type */
74219820Sjeff	struct sockaddr *svcaddr,	/* servers address */
75219820Sjeff	rpcprog_t program,		/* program number */
76219820Sjeff	rpcvers_t version,		/* version number */
77219820Sjeff	size_t sendsz,			/* buffer recv size */
78219820Sjeff	size_t recvsz)			/* buffer send size */
79219820Sjeff{
80219820Sjeff	CLIENT *cl = NULL;		/* client handle */
81219820Sjeff	struct rc_data *rc = NULL;	/* private data */
82219820Sjeff
83219820Sjeff	if (svcaddr == NULL) {
84219820Sjeff		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
85219820Sjeff		return (NULL);
86219820Sjeff	}
87219820Sjeff
88219820Sjeff	cl = mem_alloc(sizeof (CLIENT));
89219820Sjeff	rc = mem_alloc(sizeof (*rc));
90219820Sjeff	mtx_init(&rc->rc_lock, "rc->rc_lock", NULL, MTX_DEF);
91219820Sjeff	(void) memcpy(&rc->rc_addr, svcaddr, (size_t)svcaddr->sa_len);
92219820Sjeff	rc->rc_nconf = nconf;
93219820Sjeff	rc->rc_prog = program;
94219820Sjeff	rc->rc_vers = version;
95219820Sjeff	rc->rc_sendsz = sendsz;
96219820Sjeff	rc->rc_recvsz = recvsz;
97219820Sjeff	rc->rc_timeout.tv_sec = -1;
98219820Sjeff	rc->rc_timeout.tv_usec = -1;
99219820Sjeff	rc->rc_retry.tv_sec = 3;
100219820Sjeff	rc->rc_retry.tv_usec = 0;
101219820Sjeff	rc->rc_retries = INT_MAX;
102219820Sjeff	rc->rc_privport = FALSE;
103219820Sjeff	rc->rc_waitchan = "rpcrecv";
104219820Sjeff	rc->rc_intr = 0;
105219820Sjeff	rc->rc_connecting = FALSE;
106219820Sjeff	rc->rc_closed = FALSE;
107219820Sjeff	rc->rc_ucred = crdup(curthread->td_ucred);
108219820Sjeff	rc->rc_client = NULL;
109219820Sjeff
110219820Sjeff	cl->cl_refs = 1;
111219820Sjeff	cl->cl_ops = &clnt_reconnect_ops;
112219820Sjeff	cl->cl_private = (caddr_t)(void *)rc;
113219820Sjeff	cl->cl_auth = authnone_create();
114219820Sjeff	cl->cl_tp = NULL;
115219820Sjeff	cl->cl_netid = NULL;
116219820Sjeff	return (cl);
117219820Sjeff}
118219820Sjeff
119219820Sjeffstatic enum clnt_stat
120219820Sjeffclnt_reconnect_connect(CLIENT *cl)
121219820Sjeff{
122219820Sjeff	struct thread *td = curthread;
123219820Sjeff	struct rc_data *rc = (struct rc_data *)cl->cl_private;
124219820Sjeff	struct socket *so;
125219820Sjeff	enum clnt_stat stat;
126219820Sjeff	int error;
127219820Sjeff	int one = 1;
128219820Sjeff	struct ucred *oldcred;
129219820Sjeff	CLIENT *newclient = NULL;
130219820Sjeff
131219820Sjeff	mtx_lock(&rc->rc_lock);
132219820Sjeff	while (rc->rc_connecting) {
133219820Sjeff		error = msleep(rc, &rc->rc_lock,
134219820Sjeff		    rc->rc_intr ? PCATCH : 0, "rpcrecon", 0);
135219820Sjeff		if (error) {
136219820Sjeff			mtx_unlock(&rc->rc_lock);
137219820Sjeff			return (RPC_INTR);
138219820Sjeff		}
139219820Sjeff	}
140219820Sjeff	if (rc->rc_closed) {
141219820Sjeff		mtx_unlock(&rc->rc_lock);
142219820Sjeff		return (RPC_CANTSEND);
143219820Sjeff	}
144219820Sjeff	if (rc->rc_client) {
145219820Sjeff		mtx_unlock(&rc->rc_lock);
146219820Sjeff		return (RPC_SUCCESS);
147219820Sjeff	}
148219820Sjeff
149219820Sjeff	/*
150219820Sjeff	 * My turn to attempt a connect. The rc_connecting variable
151219820Sjeff	 * serializes the following code sequence, so it is guaranteed
152219820Sjeff	 * that rc_client will still be NULL after it is re-locked below,
153219820Sjeff	 * since that is the only place it is set non-NULL.
154219820Sjeff	 */
155219820Sjeff	rc->rc_connecting = TRUE;
156219820Sjeff	mtx_unlock(&rc->rc_lock);
157219820Sjeff
158219820Sjeff	oldcred = td->td_ucred;
159219820Sjeff	td->td_ucred = rc->rc_ucred;
160219820Sjeff	so = __rpc_nconf2socket(rc->rc_nconf);
161219820Sjeff	if (!so) {
162219820Sjeff		stat = rpc_createerr.cf_stat = RPC_TLIERROR;
163219820Sjeff		rpc_createerr.cf_error.re_errno = 0;
164219820Sjeff		td->td_ucred = oldcred;
165219820Sjeff		goto out;
166219820Sjeff	}
167219820Sjeff
168219820Sjeff	if (rc->rc_privport)
169219820Sjeff		bindresvport(so, NULL);
170219820Sjeff
171219820Sjeff	if (rc->rc_nconf->nc_semantics == NC_TPI_CLTS)
172219820Sjeff		newclient = clnt_dg_create(so,
173219820Sjeff		    (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
174219820Sjeff		    rc->rc_sendsz, rc->rc_recvsz);
175219820Sjeff	else
176219820Sjeff		newclient = clnt_vc_create(so,
177219820Sjeff		    (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
178219820Sjeff		    rc->rc_sendsz, rc->rc_recvsz, rc->rc_intr);
179219820Sjeff	td->td_ucred = oldcred;
180219820Sjeff
181219820Sjeff	if (!newclient) {
182219820Sjeff		soclose(so);
183219820Sjeff		rc->rc_err = rpc_createerr.cf_error;
184219820Sjeff		stat = rpc_createerr.cf_stat;
185219820Sjeff		goto out;
186219820Sjeff	}
187219820Sjeff
188219820Sjeff	CLNT_CONTROL(newclient, CLSET_FD_CLOSE, 0);
189219820Sjeff	CLNT_CONTROL(newclient, CLSET_CONNECT, &one);
190219820Sjeff	CLNT_CONTROL(newclient, CLSET_TIMEOUT, &rc->rc_timeout);
191219820Sjeff	CLNT_CONTROL(newclient, CLSET_RETRY_TIMEOUT, &rc->rc_retry);
192219820Sjeff	CLNT_CONTROL(newclient, CLSET_WAITCHAN, rc->rc_waitchan);
193219820Sjeff	CLNT_CONTROL(newclient, CLSET_INTERRUPTIBLE, &rc->rc_intr);
194219820Sjeff	if (rc->rc_backchannel != NULL)
195219820Sjeff		CLNT_CONTROL(newclient, CLSET_BACKCHANNEL, rc->rc_backchannel);
196219820Sjeff	stat = RPC_SUCCESS;
197219820Sjeff
198219820Sjeffout:
199219820Sjeff	mtx_lock(&rc->rc_lock);
200219820Sjeff	KASSERT(rc->rc_client == NULL, ("rc_client not null"));
201219820Sjeff	if (!rc->rc_closed) {
202219820Sjeff		rc->rc_client = newclient;
203219820Sjeff		newclient = NULL;
204219820Sjeff	}
205219820Sjeff	rc->rc_connecting = FALSE;
206219820Sjeff	wakeup(rc);
207219820Sjeff	mtx_unlock(&rc->rc_lock);
208219820Sjeff
209219820Sjeff	if (newclient) {
210219820Sjeff		/*
211219820Sjeff		 * It has been closed, so discard the new client.
212219820Sjeff		 * nb: clnt_[dg|vc]_close()/clnt_[dg|vc]_destroy() cannot
213219820Sjeff		 * be called with the rc_lock mutex held, since they may
214219820Sjeff		 * msleep() while holding a different mutex.
215219820Sjeff		 */
216219820Sjeff		CLNT_CLOSE(newclient);
217219820Sjeff		CLNT_RELEASE(newclient);
218219820Sjeff	}
219219820Sjeff
220219820Sjeff	return (stat);
221219820Sjeff}
222219820Sjeff
223219820Sjeffstatic enum clnt_stat
224219820Sjeffclnt_reconnect_call(
225219820Sjeff	CLIENT		*cl,		/* client handle */
226219820Sjeff	struct rpc_callextra *ext,	/* call metadata */
227219820Sjeff	rpcproc_t	proc,		/* procedure number */
228219820Sjeff	struct mbuf	*args,		/* pointer to args */
229219820Sjeff	struct mbuf	**resultsp,	/* pointer to results */
230219820Sjeff	struct timeval	utimeout)
231219820Sjeff{
232219820Sjeff	struct rc_data *rc = (struct rc_data *)cl->cl_private;
233219820Sjeff	CLIENT *client;
234219820Sjeff	enum clnt_stat stat;
235219820Sjeff	int tries, error;
236219820Sjeff
237219820Sjeff	tries = 0;
238219820Sjeff	do {
239219820Sjeff		mtx_lock(&rc->rc_lock);
240219820Sjeff		if (rc->rc_closed) {
241219820Sjeff			mtx_unlock(&rc->rc_lock);
242219820Sjeff			return (RPC_CANTSEND);
243219820Sjeff		}
244219820Sjeff
245219820Sjeff		if (!rc->rc_client) {
246219820Sjeff			mtx_unlock(&rc->rc_lock);
247219820Sjeff			stat = clnt_reconnect_connect(cl);
248219820Sjeff			if (stat == RPC_SYSTEMERROR) {
249219820Sjeff				error = tsleep(&fake_wchan,
250219820Sjeff				    rc->rc_intr ? PCATCH | PBDRY : 0, "rpccon",
251219820Sjeff				    hz);
252219820Sjeff				if (error == EINTR || error == ERESTART)
253219820Sjeff					return (RPC_INTR);
254219820Sjeff				tries++;
255219820Sjeff				if (tries >= rc->rc_retries)
256219820Sjeff					return (stat);
257219820Sjeff				continue;
258219820Sjeff			}
259219820Sjeff			if (stat != RPC_SUCCESS)
260219820Sjeff				return (stat);
261219820Sjeff			mtx_lock(&rc->rc_lock);
262219820Sjeff		}
263219820Sjeff
264219820Sjeff		if (!rc->rc_client) {
265219820Sjeff			mtx_unlock(&rc->rc_lock);
266219820Sjeff			stat = RPC_FAILED;
267219820Sjeff			continue;
268219820Sjeff		}
269219820Sjeff		CLNT_ACQUIRE(rc->rc_client);
270219820Sjeff		client = rc->rc_client;
271219820Sjeff		mtx_unlock(&rc->rc_lock);
272219820Sjeff		stat = CLNT_CALL_MBUF(client, ext, proc, args,
273219820Sjeff		    resultsp, utimeout);
274219820Sjeff
275219820Sjeff		if (stat != RPC_SUCCESS) {
276219820Sjeff			if (!ext)
277219820Sjeff				CLNT_GETERR(client, &rc->rc_err);
278219820Sjeff		}
279219820Sjeff
280219820Sjeff		if (stat == RPC_TIMEDOUT) {
281219820Sjeff			/*
282219820Sjeff			 * Check for async send misfeature for NLM
283219820Sjeff			 * protocol.
284219820Sjeff			 */
285219820Sjeff			if ((rc->rc_timeout.tv_sec == 0
286219820Sjeff				&& rc->rc_timeout.tv_usec == 0)
287219820Sjeff			    || (rc->rc_timeout.tv_sec == -1
288219820Sjeff				&& utimeout.tv_sec == 0
289219820Sjeff				&& utimeout.tv_usec == 0)) {
290219820Sjeff				CLNT_RELEASE(client);
291219820Sjeff				break;
292219820Sjeff			}
293219820Sjeff		}
294219820Sjeff
295219820Sjeff		if (stat == RPC_TIMEDOUT || stat == RPC_CANTSEND
296219820Sjeff		    || stat == RPC_CANTRECV) {
297219820Sjeff			tries++;
298219820Sjeff			if (tries >= rc->rc_retries) {
299219820Sjeff				CLNT_RELEASE(client);
300219820Sjeff				break;
301219820Sjeff			}
302219820Sjeff
303219820Sjeff			if (ext && ext->rc_feedback)
304219820Sjeff				ext->rc_feedback(FEEDBACK_RECONNECT, proc,
305219820Sjeff				    ext->rc_feedback_arg);
306219820Sjeff
307219820Sjeff			mtx_lock(&rc->rc_lock);
308219820Sjeff			/*
309219820Sjeff			 * Make sure that someone else hasn't already
310219820Sjeff			 * reconnected by checking if rc_client has changed.
311219820Sjeff			 * If not, we are done with the client and must
312219820Sjeff			 * do CLNT_RELEASE(client) twice to dispose of it,
313219820Sjeff			 * because there is both an initial refcnt and one
314219820Sjeff			 * acquired by CLNT_ACQUIRE() above.
315219820Sjeff			 */
316219820Sjeff			if (rc->rc_client == client) {
317219820Sjeff				rc->rc_client = NULL;
318219820Sjeff				mtx_unlock(&rc->rc_lock);
319219820Sjeff				CLNT_RELEASE(client);
320219820Sjeff			} else {
321219820Sjeff				mtx_unlock(&rc->rc_lock);
322219820Sjeff			}
323219820Sjeff			CLNT_RELEASE(client);
324219820Sjeff		} else {
325219820Sjeff			CLNT_RELEASE(client);
326219820Sjeff			break;
327219820Sjeff		}
328219820Sjeff	} while (stat != RPC_SUCCESS);
329219820Sjeff
330219820Sjeff	KASSERT(stat != RPC_SUCCESS || *resultsp,
331219820Sjeff	    ("RPC_SUCCESS without reply"));
332219820Sjeff
333219820Sjeff	return (stat);
334219820Sjeff}
335219820Sjeff
336219820Sjeffstatic void
337219820Sjeffclnt_reconnect_geterr(CLIENT *cl, struct rpc_err *errp)
338219820Sjeff{
339219820Sjeff	struct rc_data *rc = (struct rc_data *)cl->cl_private;
340219820Sjeff
341219820Sjeff	*errp = rc->rc_err;
342219820Sjeff}
343219820Sjeff
344219820Sjeff/*
345219820Sjeff * Since this function requires that rc_client be valid, it can
346219820Sjeff * only be called when that is guaranteed to be the case.
347219820Sjeff */
348219820Sjeffstatic bool_t
349219820Sjeffclnt_reconnect_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
350219820Sjeff{
351219820Sjeff	struct rc_data *rc = (struct rc_data *)cl->cl_private;
352219820Sjeff
353219820Sjeff	return (CLNT_FREERES(rc->rc_client, xdr_res, res_ptr));
354219820Sjeff}
355219820Sjeff
356219820Sjeff/*ARGSUSED*/
357219820Sjeffstatic void
358219820Sjeffclnt_reconnect_abort(CLIENT *h)
359219820Sjeff{
360219820Sjeff}
361219820Sjeff
362219820Sjeff/*
363219820Sjeff * CLNT_CONTROL() on the client returned by clnt_reconnect_create() must
364219820Sjeff * always be called before CLNT_CALL_MBUF() by a single thread only.
365219820Sjeff */
366219820Sjeffstatic bool_t
367219820Sjeffclnt_reconnect_control(CLIENT *cl, u_int request, void *info)
368219820Sjeff{
369219820Sjeff	struct rc_data *rc = (struct rc_data *)cl->cl_private;
370219820Sjeff	SVCXPRT *xprt;
371219820Sjeff
372219820Sjeff	if (info == NULL) {
373219820Sjeff		return (FALSE);
374219820Sjeff	}
375219820Sjeff	switch (request) {
376219820Sjeff	case CLSET_TIMEOUT:
377219820Sjeff		rc->rc_timeout = *(struct timeval *)info;
378219820Sjeff		if (rc->rc_client)
379219820Sjeff			CLNT_CONTROL(rc->rc_client, request, info);
380219820Sjeff		break;
381219820Sjeff
382219820Sjeff	case CLGET_TIMEOUT:
383219820Sjeff		*(struct timeval *)info = rc->rc_timeout;
384219820Sjeff		break;
385219820Sjeff
386219820Sjeff	case CLSET_RETRY_TIMEOUT:
387219820Sjeff		rc->rc_retry = *(struct timeval *)info;
388219820Sjeff		if (rc->rc_client)
389219820Sjeff			CLNT_CONTROL(rc->rc_client, request, info);
390219820Sjeff		break;
391219820Sjeff
392219820Sjeff	case CLGET_RETRY_TIMEOUT:
393219820Sjeff		*(struct timeval *)info = rc->rc_retry;
394219820Sjeff		break;
395219820Sjeff
396219820Sjeff	case CLGET_VERS:
397219820Sjeff		*(uint32_t *)info = rc->rc_vers;
398219820Sjeff		break;
399219820Sjeff
400219820Sjeff	case CLSET_VERS:
401219820Sjeff		rc->rc_vers = *(uint32_t *) info;
402219820Sjeff		if (rc->rc_client)
403219820Sjeff			CLNT_CONTROL(rc->rc_client, CLSET_VERS, info);
404219820Sjeff		break;
405219820Sjeff
406219820Sjeff	case CLGET_PROG:
407219820Sjeff		*(uint32_t *)info = rc->rc_prog;
408219820Sjeff		break;
409219820Sjeff
410219820Sjeff	case CLSET_PROG:
411219820Sjeff		rc->rc_prog = *(uint32_t *) info;
412219820Sjeff		if (rc->rc_client)
413219820Sjeff			CLNT_CONTROL(rc->rc_client, request, info);
414219820Sjeff		break;
415219820Sjeff
416219820Sjeff	case CLSET_WAITCHAN:
417219820Sjeff		rc->rc_waitchan = (char *)info;
418219820Sjeff		if (rc->rc_client)
419219820Sjeff			CLNT_CONTROL(rc->rc_client, request, info);
420219820Sjeff		break;
421219820Sjeff
422219820Sjeff	case CLGET_WAITCHAN:
423219820Sjeff		*(const char **) info = rc->rc_waitchan;
424219820Sjeff		break;
425219820Sjeff
426219820Sjeff	case CLSET_INTERRUPTIBLE:
427219820Sjeff		rc->rc_intr = *(int *) info;
428219820Sjeff		if (rc->rc_client)
429219820Sjeff			CLNT_CONTROL(rc->rc_client, request, info);
430219820Sjeff		break;
431219820Sjeff
432219820Sjeff	case CLGET_INTERRUPTIBLE:
433219820Sjeff		*(int *) info = rc->rc_intr;
434219820Sjeff		break;
435219820Sjeff
436219820Sjeff	case CLSET_RETRIES:
437219820Sjeff		rc->rc_retries = *(int *) info;
438219820Sjeff		break;
439219820Sjeff
440219820Sjeff	case CLGET_RETRIES:
441219820Sjeff		*(int *) info = rc->rc_retries;
442219820Sjeff		break;
443219820Sjeff
444219820Sjeff	case CLSET_PRIVPORT:
445219820Sjeff		rc->rc_privport = *(int *) info;
446219820Sjeff		break;
447219820Sjeff
448219820Sjeff	case CLGET_PRIVPORT:
449219820Sjeff		*(int *) info = rc->rc_privport;
450219820Sjeff		break;
451219820Sjeff
452219820Sjeff	case CLSET_BACKCHANNEL:
453219820Sjeff		xprt = (SVCXPRT *)info;
454219820Sjeff		SVC_ACQUIRE(xprt);
455219820Sjeff		xprt_register(xprt);
456219820Sjeff		rc->rc_backchannel = info;
457219820Sjeff		break;
458219820Sjeff
459219820Sjeff	default:
460219820Sjeff		return (FALSE);
461219820Sjeff	}
462219820Sjeff
463219820Sjeff	return (TRUE);
464219820Sjeff}
465219820Sjeff
466219820Sjeffstatic void
467219820Sjeffclnt_reconnect_close(CLIENT *cl)
468219820Sjeff{
469219820Sjeff	struct rc_data *rc = (struct rc_data *)cl->cl_private;
470219820Sjeff	CLIENT *client;
471219820Sjeff
472219820Sjeff	mtx_lock(&rc->rc_lock);
473219820Sjeff
474219820Sjeff	if (rc->rc_closed) {
475219820Sjeff		mtx_unlock(&rc->rc_lock);
476219820Sjeff		return;
477219820Sjeff	}
478219820Sjeff
479219820Sjeff	rc->rc_closed = TRUE;
480219820Sjeff	client = rc->rc_client;
481219820Sjeff	rc->rc_client = NULL;
482219820Sjeff
483219820Sjeff	mtx_unlock(&rc->rc_lock);
484219820Sjeff
485219820Sjeff	if (client) {
486219820Sjeff		CLNT_CLOSE(client);
487219820Sjeff		CLNT_RELEASE(client);
488219820Sjeff	}
489219820Sjeff}
490219820Sjeff
491219820Sjeffstatic void
492219820Sjeffclnt_reconnect_destroy(CLIENT *cl)
493219820Sjeff{
494219820Sjeff	struct rc_data *rc = (struct rc_data *)cl->cl_private;
495219820Sjeff	SVCXPRT *xprt;
496219820Sjeff
497219820Sjeff	if (rc->rc_client)
498219820Sjeff		CLNT_DESTROY(rc->rc_client);
499219820Sjeff	if (rc->rc_backchannel) {
500219820Sjeff		xprt = (SVCXPRT *)rc->rc_backchannel;
501219820Sjeff		xprt_unregister(xprt);
502219820Sjeff		SVC_RELEASE(xprt);
503219820Sjeff	}
504219820Sjeff	crfree(rc->rc_ucred);
505219820Sjeff	mtx_destroy(&rc->rc_lock);
506219820Sjeff	mem_free(rc, sizeof(*rc));
507219820Sjeff	mem_free(cl, sizeof (CLIENT));
508219820Sjeff}
509219820Sjeff