clnt_rc.c revision 261058
1327952Sdim/*-
2286425Sdim * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3286425Sdim * Authors: Doug Rabson <dfr@rabson.org>
4286425Sdim * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5286425Sdim *
6286425Sdim * Redistribution and use in source and binary forms, with or without
7286425Sdim * modification, are permitted provided that the following conditions
8286425Sdim * are met:
9286425Sdim * 1. Redistributions of source code must retain the above copyright
10286425Sdim *    notice, this list of conditions and the following disclaimer.
11286425Sdim * 2. Redistributions in binary form must reproduce the above copyright
12314564Sdim *    notice, this list of conditions and the following disclaimer in the
13286425Sdim *    documentation and/or other materials provided with the distribution.
14327952Sdim *
15286425Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16314564Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17286425Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18286425Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19314564Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20314564Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21286425Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22314564Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23286425Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24286425Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25314564Sdim * SUCH DAMAGE.
26286425Sdim */
27314564Sdim
28314564Sdim#include <sys/cdefs.h>
29314564Sdim__FBSDID("$FreeBSD: stable/9/sys/rpc/clnt_rc.c 261058 2014-01-23 00:35:41Z mav $");
30314564Sdim
31286425Sdim#include <sys/param.h>
32314564Sdim#include <sys/systm.h>
33286425Sdim#include <sys/kernel.h>
34314564Sdim#include <sys/limits.h>
35286425Sdim#include <sys/lock.h>
36314564Sdim#include <sys/malloc.h>
37286425Sdim#include <sys/mbuf.h>
38286425Sdim#include <sys/mutex.h>
39286425Sdim#include <sys/pcpu.h>
40314564Sdim#include <sys/proc.h>
41314564Sdim#include <sys/socket.h>
42314564Sdim#include <sys/socketvar.h>
43314564Sdim#include <sys/time.h>
44314564Sdim#include <sys/uio.h>
45286425Sdim
46286425Sdim#include <rpc/rpc.h>
47314564Sdim#include <rpc/rpc_com.h>
48286425Sdim#include <rpc/krpc.h>
49286425Sdim
50286425Sdimstatic enum clnt_stat clnt_reconnect_call(CLIENT *, struct rpc_callextra *,
51286425Sdim    rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
52286425Sdimstatic void clnt_reconnect_geterr(CLIENT *, struct rpc_err *);
53286425Sdimstatic bool_t clnt_reconnect_freeres(CLIENT *, xdrproc_t, void *);
54286425Sdimstatic void clnt_reconnect_abort(CLIENT *);
55286425Sdimstatic bool_t clnt_reconnect_control(CLIENT *, u_int, void *);
56286425Sdimstatic void clnt_reconnect_close(CLIENT *);
57286425Sdimstatic void clnt_reconnect_destroy(CLIENT *);
58286425Sdim
59286425Sdimstatic struct clnt_ops clnt_reconnect_ops = {
60286425Sdim	.cl_call =	clnt_reconnect_call,
61286425Sdim	.cl_abort =	clnt_reconnect_abort,
62314564Sdim	.cl_geterr =	clnt_reconnect_geterr,
63286425Sdim	.cl_freeres =	clnt_reconnect_freeres,
64286425Sdim	.cl_close =	clnt_reconnect_close,
65314564Sdim	.cl_destroy =	clnt_reconnect_destroy,
66314564Sdim	.cl_control =	clnt_reconnect_control
67286425Sdim};
68314564Sdim
69286425Sdimstatic int	fake_wchan;
70327952Sdim
71327952SdimCLIENT *
72327952Sdimclnt_reconnect_create(
73327952Sdim	struct netconfig *nconf,	/* network type */
74327952Sdim	struct sockaddr *svcaddr,	/* servers address */
75327952Sdim	rpcprog_t program,		/* program number */
76286425Sdim	rpcvers_t version,		/* version number */
77286425Sdim	size_t sendsz,			/* buffer recv size */
78286425Sdim	size_t recvsz)			/* buffer send size */
79296417Sdim{
80314564Sdim	CLIENT *cl = NULL;		/* client handle */
81286425Sdim	struct rc_data *rc = NULL;	/* private data */
82296417Sdim
83296417Sdim	if (svcaddr == NULL) {
84296417Sdim		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
85296417Sdim		return (NULL);
86296417Sdim	}
87296417Sdim
88286425Sdim	cl = mem_alloc(sizeof (CLIENT));
89286425Sdim	rc = mem_alloc(sizeof (*rc));
90296417Sdim	mtx_init(&rc->rc_lock, "rc->rc_lock", NULL, MTX_DEF);
91286425Sdim	(void) memcpy(&rc->rc_addr, svcaddr, (size_t)svcaddr->sa_len);
92296417Sdim	rc->rc_nconf = nconf;
93314564Sdim	rc->rc_prog = program;
94286425Sdim	rc->rc_vers = version;
95286425Sdim	rc->rc_sendsz = sendsz;
96286425Sdim	rc->rc_recvsz = recvsz;
97286425Sdim	rc->rc_timeout.tv_sec = -1;
98286425Sdim	rc->rc_timeout.tv_usec = -1;
99314564Sdim	rc->rc_retry.tv_sec = 3;
100286425Sdim	rc->rc_retry.tv_usec = 0;
101286425Sdim	rc->rc_retries = INT_MAX;
102286425Sdim	rc->rc_privport = FALSE;
103286425Sdim	rc->rc_waitchan = "rpcrecv";
104314564Sdim	rc->rc_intr = 0;
105314564Sdim	rc->rc_connecting = FALSE;
106314564Sdim	rc->rc_closed = FALSE;
107314564Sdim	rc->rc_ucred = crdup(curthread->td_ucred);
108286425Sdim	rc->rc_client = NULL;
109286425Sdim
110309124Sdim	cl->cl_refs = 1;
111309124Sdim	cl->cl_ops = &clnt_reconnect_ops;
112286425Sdim	cl->cl_private = (caddr_t)(void *)rc;
113286425Sdim	cl->cl_auth = authnone_create();
114286425Sdim	cl->cl_tp = NULL;
115286425Sdim	cl->cl_netid = NULL;
116286425Sdim	return (cl);
117286425Sdim}
118327952Sdim
119327952Sdimstatic enum clnt_stat
120327952Sdimclnt_reconnect_connect(CLIENT *cl)
121286425Sdim{
122286425Sdim	struct thread *td = curthread;
123286425Sdim	struct rc_data *rc = (struct rc_data *)cl->cl_private;
124286425Sdim	struct socket *so;
125286425Sdim	enum clnt_stat stat;
126286425Sdim	int error;
127286425Sdim	int one = 1;
128286425Sdim	struct ucred *oldcred;
129286425Sdim	CLIENT *newclient = NULL;
130286425Sdim
131286425Sdim	mtx_lock(&rc->rc_lock);
132286425Sdim	while (rc->rc_connecting) {
133286425Sdim		error = msleep(rc, &rc->rc_lock,
134286425Sdim		    rc->rc_intr ? PCATCH : 0, "rpcrecon", 0);
135286425Sdim		if (error) {
136286425Sdim			mtx_unlock(&rc->rc_lock);
137286425Sdim			return (RPC_INTR);
138286425Sdim		}
139286425Sdim	}
140286425Sdim	if (rc->rc_closed) {
141286425Sdim		mtx_unlock(&rc->rc_lock);
142286425Sdim		return (RPC_CANTSEND);
143286425Sdim	}
144286425Sdim	if (rc->rc_client) {
145286425Sdim		mtx_unlock(&rc->rc_lock);
146286425Sdim		return (RPC_SUCCESS);
147286425Sdim	}
148286425Sdim
149286425Sdim	/*
150286425Sdim	 * My turn to attempt a connect. The rc_connecting variable
151286425Sdim	 * serializes the following code sequence, so it is guaranteed
152286425Sdim	 * that rc_client will still be NULL after it is re-locked below,
153286425Sdim	 * since that is the only place it is set non-NULL.
154286425Sdim	 */
155286425Sdim	rc->rc_connecting = TRUE;
156286425Sdim	mtx_unlock(&rc->rc_lock);
157286425Sdim
158286425Sdim	oldcred = td->td_ucred;
159286425Sdim	td->td_ucred = rc->rc_ucred;
160286425Sdim	so = __rpc_nconf2socket(rc->rc_nconf);
161314564Sdim	if (!so) {
162286425Sdim		stat = rpc_createerr.cf_stat = RPC_TLIERROR;
163286425Sdim		rpc_createerr.cf_error.re_errno = 0;
164327952Sdim		td->td_ucred = oldcred;
165286425Sdim		goto out;
166286425Sdim	}
167286425Sdim
168309124Sdim	if (rc->rc_privport)
169286425Sdim		bindresvport(so, NULL);
170286425Sdim
171286425Sdim	if (rc->rc_nconf->nc_semantics == NC_TPI_CLTS)
172286425Sdim		newclient = clnt_dg_create(so,
173286425Sdim		    (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
174314564Sdim		    rc->rc_sendsz, rc->rc_recvsz);
175286425Sdim	else
176286425Sdim		newclient = clnt_vc_create(so,
177286425Sdim		    (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
178286425Sdim		    rc->rc_sendsz, rc->rc_recvsz, rc->rc_intr);
179286425Sdim	td->td_ucred = oldcred;
180321369Sdim
181321369Sdim	if (!newclient) {
182286425Sdim		soclose(so);
183286425Sdim		rc->rc_err = rpc_createerr.cf_error;
184327952Sdim		stat = rpc_createerr.cf_stat;
185286425Sdim		goto out;
186286425Sdim	}
187286425Sdim
188286425Sdim	CLNT_CONTROL(newclient, CLSET_FD_CLOSE, 0);
189327952Sdim	CLNT_CONTROL(newclient, CLSET_CONNECT, &one);
190327952Sdim	CLNT_CONTROL(newclient, CLSET_TIMEOUT, &rc->rc_timeout);
191286425Sdim	CLNT_CONTROL(newclient, CLSET_RETRY_TIMEOUT, &rc->rc_retry);
192327952Sdim	CLNT_CONTROL(newclient, CLSET_WAITCHAN, rc->rc_waitchan);
193286425Sdim	CLNT_CONTROL(newclient, CLSET_INTERRUPTIBLE, &rc->rc_intr);
194286425Sdim	if (rc->rc_backchannel != NULL)
195286425Sdim		CLNT_CONTROL(newclient, CLSET_BACKCHANNEL, rc->rc_backchannel);
196286425Sdim	stat = RPC_SUCCESS;
197286425Sdim
198286425Sdimout:
199314564Sdim	mtx_lock(&rc->rc_lock);
200286425Sdim	KASSERT(rc->rc_client == NULL, ("rc_client not null"));
201286425Sdim	if (!rc->rc_closed) {
202286425Sdim		rc->rc_client = newclient;
203286425Sdim		newclient = NULL;
204314564Sdim	}
205314564Sdim	rc->rc_connecting = FALSE;
206286425Sdim	wakeup(rc);
207286425Sdim	mtx_unlock(&rc->rc_lock);
208286425Sdim
209286425Sdim	if (newclient) {
210286425Sdim		/*
211286425Sdim		 * It has been closed, so discard the new client.
212286425Sdim		 * nb: clnt_[dg|vc]_close()/clnt_[dg|vc]_destroy() cannot
213286425Sdim		 * be called with the rc_lock mutex held, since they may
214286425Sdim		 * msleep() while holding a different mutex.
215286425Sdim		 */
216286425Sdim		CLNT_CLOSE(newclient);
217286425Sdim		CLNT_RELEASE(newclient);
218286425Sdim	}
219286425Sdim
220286425Sdim	return (stat);
221286425Sdim}
222286425Sdim
223286425Sdimstatic enum clnt_stat
224286425Sdimclnt_reconnect_call(
225286425Sdim	CLIENT		*cl,		/* client handle */
226286425Sdim	struct rpc_callextra *ext,	/* call metadata */
227286425Sdim	rpcproc_t	proc,		/* procedure number */
228286425Sdim	struct mbuf	*args,		/* pointer to args */
229286425Sdim	struct mbuf	**resultsp,	/* pointer to results */
230286425Sdim	struct timeval	utimeout)
231286425Sdim{
232286425Sdim	struct rc_data *rc = (struct rc_data *)cl->cl_private;
233286425Sdim	CLIENT *client;
234286425Sdim	enum clnt_stat stat;
235286425Sdim	int tries, error;
236286425Sdim
237321369Sdim	tries = 0;
238321369Sdim	do {
239321369Sdim		mtx_lock(&rc->rc_lock);
240321369Sdim		if (rc->rc_closed) {
241321369Sdim			mtx_unlock(&rc->rc_lock);
242286425Sdim			return (RPC_CANTSEND);
243286425Sdim		}
244286425Sdim
245286425Sdim		if (!rc->rc_client) {
246286425Sdim			mtx_unlock(&rc->rc_lock);
247286425Sdim			stat = clnt_reconnect_connect(cl);
248286425Sdim			if (stat == RPC_SYSTEMERROR) {
249286425Sdim				error = tsleep(&fake_wchan,
250286425Sdim				    rc->rc_intr ? PCATCH | PBDRY : 0, "rpccon",
251286425Sdim				    hz);
252286425Sdim				if (error == EINTR || error == ERESTART)
253286425Sdim					return (RPC_INTR);
254286425Sdim				tries++;
255286425Sdim				if (tries >= rc->rc_retries)
256286425Sdim					return (stat);
257286425Sdim				continue;
258286425Sdim			}
259286425Sdim			if (stat != RPC_SUCCESS)
260286425Sdim				return (stat);
261286425Sdim			mtx_lock(&rc->rc_lock);
262286425Sdim		}
263286425Sdim
264286425Sdim		if (!rc->rc_client) {
265286425Sdim			mtx_unlock(&rc->rc_lock);
266286425Sdim			stat = RPC_FAILED;
267286425Sdim			continue;
268286425Sdim		}
269286425Sdim		CLNT_ACQUIRE(rc->rc_client);
270286425Sdim		client = rc->rc_client;
271286425Sdim		mtx_unlock(&rc->rc_lock);
272327952Sdim		stat = CLNT_CALL_MBUF(client, ext, proc, args,
273327952Sdim		    resultsp, utimeout);
274286425Sdim
275286425Sdim		if (stat != RPC_SUCCESS) {
276286425Sdim			if (!ext)
277286425Sdim				CLNT_GETERR(client, &rc->rc_err);
278286425Sdim		}
279286425Sdim
280286425Sdim		if (stat == RPC_TIMEDOUT) {
281286425Sdim			/*
282286425Sdim			 * Check for async send misfeature for NLM
283286425Sdim			 * protocol.
284286425Sdim			 */
285286425Sdim			if ((rc->rc_timeout.tv_sec == 0
286286425Sdim				&& rc->rc_timeout.tv_usec == 0)
287286425Sdim			    || (rc->rc_timeout.tv_sec == -1
288327952Sdim				&& utimeout.tv_sec == 0
289327952Sdim				&& utimeout.tv_usec == 0)) {
290286425Sdim				CLNT_RELEASE(client);
291286425Sdim				break;
292286425Sdim			}
293286425Sdim		}
294286425Sdim
295286425Sdim		if (stat == RPC_TIMEDOUT || stat == RPC_CANTSEND
296286425Sdim		    || stat == RPC_CANTRECV) {
297286425Sdim			tries++;
298286425Sdim			if (tries >= rc->rc_retries) {
299286425Sdim				CLNT_RELEASE(client);
300286425Sdim				break;
301286425Sdim			}
302286425Sdim
303286425Sdim			if (ext && ext->rc_feedback)
304286425Sdim				ext->rc_feedback(FEEDBACK_RECONNECT, proc,
305286425Sdim				    ext->rc_feedback_arg);
306286425Sdim
307327952Sdim			mtx_lock(&rc->rc_lock);
308286425Sdim			/*
309286425Sdim			 * Make sure that someone else hasn't already
310286425Sdim			 * reconnected by checking if rc_client has changed.
311314564Sdim			 * If not, we are done with the client and must
312286425Sdim			 * do CLNT_RELEASE(client) twice to dispose of it,
313286425Sdim			 * because there is both an initial refcnt and one
314286425Sdim			 * acquired by CLNT_ACQUIRE() above.
315286425Sdim			 */
316314564Sdim			if (rc->rc_client == client) {
317286425Sdim				rc->rc_client = NULL;
318286425Sdim				mtx_unlock(&rc->rc_lock);
319286425Sdim				CLNT_RELEASE(client);
320286425Sdim			} else {
321286425Sdim				mtx_unlock(&rc->rc_lock);
322286425Sdim			}
323286425Sdim			CLNT_RELEASE(client);
324286425Sdim		} else {
325286425Sdim			CLNT_RELEASE(client);
326286425Sdim			break;
327286425Sdim		}
328286425Sdim	} while (stat != RPC_SUCCESS);
329321369Sdim
330321369Sdim	KASSERT(stat != RPC_SUCCESS || *resultsp,
331286425Sdim	    ("RPC_SUCCESS without reply"));
332286425Sdim
333286425Sdim	return (stat);
334286425Sdim}
335286425Sdim
336286425Sdimstatic void
337286425Sdimclnt_reconnect_geterr(CLIENT *cl, struct rpc_err *errp)
338286425Sdim{
339286425Sdim	struct rc_data *rc = (struct rc_data *)cl->cl_private;
340286425Sdim
341286425Sdim	*errp = rc->rc_err;
342286425Sdim}
343286425Sdim
344286425Sdim/*
345286425Sdim * Since this function requires that rc_client be valid, it can
346286425Sdim * only be called when that is guaranteed to be the case.
347286425Sdim */
348321369Sdimstatic bool_t
349286425Sdimclnt_reconnect_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
350286425Sdim{
351286425Sdim	struct rc_data *rc = (struct rc_data *)cl->cl_private;
352321369Sdim
353286425Sdim	return (CLNT_FREERES(rc->rc_client, xdr_res, res_ptr));
354286425Sdim}
355286425Sdim
356286425Sdim/*ARGSUSED*/
357286425Sdimstatic void
358286425Sdimclnt_reconnect_abort(CLIENT *h)
359286425Sdim{
360286425Sdim}
361286425Sdim
362286425Sdim/*
363286425Sdim * CLNT_CONTROL() on the client returned by clnt_reconnect_create() must
364286425Sdim * always be called before CLNT_CALL_MBUF() by a single thread only.
365286425Sdim */
366286425Sdimstatic bool_t
367286425Sdimclnt_reconnect_control(CLIENT *cl, u_int request, void *info)
368286425Sdim{
369286425Sdim	struct rc_data *rc = (struct rc_data *)cl->cl_private;
370286425Sdim	SVCXPRT *xprt;
371286425Sdim
372286425Sdim	if (info == NULL) {
373286425Sdim		return (FALSE);
374286425Sdim	}
375286425Sdim	switch (request) {
376286425Sdim	case CLSET_TIMEOUT:
377286425Sdim		rc->rc_timeout = *(struct timeval *)info;
378286425Sdim		if (rc->rc_client)
379286425Sdim			CLNT_CONTROL(rc->rc_client, request, info);
380286425Sdim		break;
381286425Sdim
382286425Sdim	case CLGET_TIMEOUT:
383286425Sdim		*(struct timeval *)info = rc->rc_timeout;
384286425Sdim		break;
385286425Sdim
386286425Sdim	case CLSET_RETRY_TIMEOUT:
387286425Sdim		rc->rc_retry = *(struct timeval *)info;
388321369Sdim		if (rc->rc_client)
389286425Sdim			CLNT_CONTROL(rc->rc_client, request, info);
390286425Sdim		break;
391286425Sdim
392286425Sdim	case CLGET_RETRY_TIMEOUT:
393286425Sdim		*(struct timeval *)info = rc->rc_retry;
394286425Sdim		break;
395286425Sdim
396286425Sdim	case CLGET_VERS:
397286425Sdim		*(uint32_t *)info = rc->rc_vers;
398286425Sdim		break;
399286425Sdim
400286425Sdim	case CLSET_VERS:
401286425Sdim		rc->rc_vers = *(uint32_t *) info;
402286425Sdim		if (rc->rc_client)
403286425Sdim			CLNT_CONTROL(rc->rc_client, CLSET_VERS, info);
404286425Sdim		break;
405286425Sdim
406286425Sdim	case CLGET_PROG:
407286425Sdim		*(uint32_t *)info = rc->rc_prog;
408286425Sdim		break;
409286425Sdim
410286425Sdim	case CLSET_PROG:
411286425Sdim		rc->rc_prog = *(uint32_t *) info;
412296417Sdim		if (rc->rc_client)
413286425Sdim			CLNT_CONTROL(rc->rc_client, request, info);
414286425Sdim		break;
415286425Sdim
416286425Sdim	case CLSET_WAITCHAN:
417286425Sdim		rc->rc_waitchan = (char *)info;
418286425Sdim		if (rc->rc_client)
419286425Sdim			CLNT_CONTROL(rc->rc_client, request, info);
420286425Sdim		break;
421286425Sdim
422286425Sdim	case CLGET_WAITCHAN:
423286425Sdim		*(const char **) info = rc->rc_waitchan;
424286425Sdim		break;
425286425Sdim
426286425Sdim	case CLSET_INTERRUPTIBLE:
427286425Sdim		rc->rc_intr = *(int *) info;
428286425Sdim		if (rc->rc_client)
429286425Sdim			CLNT_CONTROL(rc->rc_client, request, info);
430286425Sdim		break;
431286425Sdim
432314564Sdim	case CLGET_INTERRUPTIBLE:
433314564Sdim		*(int *) info = rc->rc_intr;
434327952Sdim		break;
435327952Sdim
436286425Sdim	case CLSET_RETRIES:
437286425Sdim		rc->rc_retries = *(int *) info;
438286425Sdim		break;
439286425Sdim
440286425Sdim	case CLGET_RETRIES:
441286425Sdim		*(int *) info = rc->rc_retries;
442286425Sdim		break;
443286425Sdim
444286425Sdim	case CLSET_PRIVPORT:
445314564Sdim		rc->rc_privport = *(int *) info;
446286425Sdim		break;
447314564Sdim
448314564Sdim	case CLGET_PRIVPORT:
449286425Sdim		*(int *) info = rc->rc_privport;
450286425Sdim		break;
451286425Sdim
452286425Sdim	case CLSET_BACKCHANNEL:
453286425Sdim		xprt = (SVCXPRT *)info;
454286425Sdim		SVC_ACQUIRE(xprt);
455286425Sdim		xprt_register(xprt);
456286425Sdim		rc->rc_backchannel = info;
457286425Sdim		break;
458286425Sdim
459286425Sdim	default:
460286425Sdim		return (FALSE);
461286425Sdim	}
462286425Sdim
463286425Sdim	return (TRUE);
464286425Sdim}
465314564Sdim
466286425Sdimstatic void
467327952Sdimclnt_reconnect_close(CLIENT *cl)
468327952Sdim{
469327952Sdim	struct rc_data *rc = (struct rc_data *)cl->cl_private;
470286425Sdim	CLIENT *client;
471314564Sdim
472314564Sdim	mtx_lock(&rc->rc_lock);
473314564Sdim
474286425Sdim	if (rc->rc_closed) {
475286425Sdim		mtx_unlock(&rc->rc_lock);
476286425Sdim		return;
477314564Sdim	}
478314564Sdim
479286425Sdim	rc->rc_closed = TRUE;
480286425Sdim	client = rc->rc_client;
481286425Sdim	rc->rc_client = NULL;
482286425Sdim
483314564Sdim	mtx_unlock(&rc->rc_lock);
484286425Sdim
485286425Sdim	if (client) {
486286425Sdim		CLNT_CLOSE(client);
487286425Sdim		CLNT_RELEASE(client);
488314564Sdim	}
489286425Sdim}
490314564Sdim
491286425Sdimstatic void
492286425Sdimclnt_reconnect_destroy(CLIENT *cl)
493286425Sdim{
494286425Sdim	struct rc_data *rc = (struct rc_data *)cl->cl_private;
495286425Sdim	SVCXPRT *xprt;
496314564Sdim
497286425Sdim	if (rc->rc_client)
498314564Sdim		CLNT_DESTROY(rc->rc_client);
499314564Sdim	if (rc->rc_backchannel) {
500286425Sdim		xprt = (SVCXPRT *)rc->rc_backchannel;
501286425Sdim		xprt_unregister(xprt);
502286425Sdim		SVC_RELEASE(xprt);
503286425Sdim	}
504286425Sdim	crfree(rc->rc_ucred);
505286425Sdim	mtx_destroy(&rc->rc_lock);
506286425Sdim	mem_free(rc, sizeof(*rc));
507286425Sdim	mem_free(cl, sizeof (CLIENT));
508286425Sdim}
509286425Sdim