xref: /freebsd/sys/rpc/clnt_rc.c (revision f9bc005b8ef3b507a5ecbc2d7fe4411ef70eff38)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
5  * Authors: Doug Rabson <dfr@rabson.org>
6  * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/limits.h>
34 #include <sys/lock.h>
35 #include <sys/malloc.h>
36 #include <sys/mbuf.h>
37 #include <sys/mutex.h>
38 #include <sys/pcpu.h>
39 #include <sys/proc.h>
40 #include <sys/socket.h>
41 #include <sys/socketvar.h>
42 #include <sys/time.h>
43 #include <sys/uio.h>
44 
45 #include <netinet/tcp.h>
46 
47 #include <rpc/rpc.h>
48 #include <rpc/rpc_com.h>
49 #include <rpc/krpc.h>
50 #include <rpc/rpcsec_tls.h>
51 
52 xprt_rdma_check_route_ftype *rdma_check_route = NULL;
53 clnt_rdma_create_ftype *clnt_rdma_create_call = NULL;
54 svc_rdma_create_backchannel_ftype *svc_rdma_create_backchannel_call = NULL;
55 
56 static enum clnt_stat clnt_reconnect_call(CLIENT *, struct rpc_callextra *,
57     rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
58 static void clnt_reconnect_geterr(CLIENT *, struct rpc_err *);
59 static bool_t clnt_reconnect_freeres(CLIENT *, xdrproc_t, void *);
60 static void clnt_reconnect_abort(CLIENT *);
61 static bool_t clnt_reconnect_control(CLIENT *, u_int, void *);
62 static void clnt_reconnect_close(CLIENT *);
63 static void clnt_reconnect_destroy(CLIENT *);
64 
65 static const struct clnt_ops clnt_reconnect_ops = {
66 	.cl_call =	clnt_reconnect_call,
67 	.cl_abort =	clnt_reconnect_abort,
68 	.cl_geterr =	clnt_reconnect_geterr,
69 	.cl_freeres =	clnt_reconnect_freeres,
70 	.cl_close =	clnt_reconnect_close,
71 	.cl_destroy =	clnt_reconnect_destroy,
72 	.cl_control =	clnt_reconnect_control
73 };
74 
75 static int	fake_wchan;
76 
77 CLIENT *
78 clnt_reconnect_create(
79 	struct netconfig *nconf,	/* network type */
80 	struct sockaddr *svcaddr,	/* servers address */
81 	rpcprog_t program,		/* program number */
82 	rpcvers_t version,		/* version number */
83 	size_t sendsz,			/* buffer recv size */
84 	size_t recvsz)			/* buffer send size */
85 {
86 	CLIENT *cl = NULL;		/* client handle */
87 	struct rc_data *rc = NULL;	/* private data */
88 
89 	if (svcaddr == NULL) {
90 		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
91 		return (NULL);
92 	}
93 
94 	cl = mem_alloc(sizeof (CLIENT));
95 	rc = mem_alloc(sizeof (*rc));
96 	mtx_init(&rc->rc_lock, "rc->rc_lock", NULL, MTX_DEF);
97 	(void) memcpy(&rc->rc_addr, svcaddr, (size_t)svcaddr->sa_len);
98 	rc->rc_nconf = nconf;
99 	rc->rc_prog = program;
100 	rc->rc_vers = version;
101 	rc->rc_sendsz = sendsz;
102 	rc->rc_recvsz = recvsz;
103 	rc->rc_timeout.tv_sec = -1;
104 	rc->rc_timeout.tv_usec = -1;
105 	rc->rc_retry.tv_sec = 3;
106 	rc->rc_retry.tv_usec = 0;
107 	rc->rc_retries = INT_MAX;
108 	rc->rc_privport = FALSE;
109 	rc->rc_waitchan = "rpcrecv";
110 	rc->rc_intr = 0;
111 	rc->rc_connecting = FALSE;
112 	rc->rc_closed = FALSE;
113 	rc->rc_ucred = crdup(curthread->td_ucred);
114 	rc->rc_client = NULL;
115 	rc->rc_tls = false;
116 	rc->rc_tlscertname = NULL;
117 	rc->rc_reconcall = NULL;
118 	rc->rc_reconarg = NULL;
119 
120 	cl->cl_refs = 1;
121 	cl->cl_ops = &clnt_reconnect_ops;
122 	cl->cl_private = (caddr_t)(void *)rc;
123 	cl->cl_auth = authnone_create();
124 	cl->cl_tp = NULL;
125 	cl->cl_netid = NULL;
126 	return (cl);
127 }
128 
129 static enum clnt_stat
130 clnt_reconnect_connect(CLIENT *cl)
131 {
132 	struct thread *td = curthread;
133 	struct rc_data *rc = (struct rc_data *)cl->cl_private;
134 	struct socket *so;
135 	enum clnt_stat stat;
136 	int error;
137 	int one = 1;
138 	struct ucred *oldcred;
139 	CLIENT *newclient = NULL;
140 	uint32_t reterr;
141 	bool dordma;
142 
143 	dordma = false;
144 	mtx_lock(&rc->rc_lock);
145 	while (rc->rc_connecting) {
146 		error = msleep(rc, &rc->rc_lock,
147 		    rc->rc_intr ? PCATCH : 0, "rpcrecon", 0);
148 		if (error) {
149 			mtx_unlock(&rc->rc_lock);
150 			return (RPC_INTR);
151 		}
152 	}
153 	if (rc->rc_closed) {
154 		mtx_unlock(&rc->rc_lock);
155 		return (RPC_CANTSEND);
156 	}
157 	if (rc->rc_client) {
158 		mtx_unlock(&rc->rc_lock);
159 		return (RPC_SUCCESS);
160 	}
161 
162 	/*
163 	 * My turn to attempt a connect. The rc_connecting variable
164 	 * serializes the following code sequence, so it is guaranteed
165 	 * that rc_client will still be NULL after it is re-locked below,
166 	 * since that is the only place it is set non-NULL.
167 	 */
168 	rc->rc_connecting = TRUE;
169 	mtx_unlock(&rc->rc_lock);
170 
171 	oldcred = td->td_ucred;
172 	td->td_ucred = rc->rc_ucred;
173 
174 	/* Handle RDMA. */
175 	if (strcmp("rdma", rc->rc_nconf->nc_netid) == 0 ||
176 	    strcmp("rdma6", rc->rc_nconf->nc_netid) == 0)
177 		dordma = true;
178 	if (dordma) {
179 		if (clnt_rdma_create_call != NULL) {
180 			/* Set up the QP. */
181 			stat = RPC_SUCCESS;
182 			newclient = clnt_rdma_create_call(
183 			    (struct sockaddr *)&rc->rc_addr, rc->rc_prog,
184 			    rc->rc_vers, rc->rc_intr, rc->rc_rdmasmall_reply,
185 			    rc->rc_rdmamax_io, rc->rc_rdma_cbslots,
186 			    &rc->rc_err);
187 		} else {
188 			rc->rc_err.re_status = stat = RPC_FAILED;
189 			newclient = NULL;
190 		}
191 	} else {
192 		so = __rpc_nconf2socket(rc->rc_nconf);
193 		if (!so) {
194 			stat = rpc_createerr.cf_stat = RPC_TLIERROR;
195 			rpc_createerr.cf_error.re_errno = 0;
196 			td->td_ucred = oldcred;
197 			goto out;
198 		}
199 
200 		if (rc->rc_privport)
201 			bindresvport(so, NULL);
202 
203 		if (rc->rc_nconf->nc_semantics == NC_TPI_CLTS)
204 			newclient = clnt_dg_create(so,
205 			    (struct sockaddr *) &rc->rc_addr, rc->rc_prog,
206 			    rc->rc_vers, rc->rc_sendsz, rc->rc_recvsz);
207 		else {
208 			/*
209 			 * I do not believe a timeout of less than 1sec would
210 			 * make sense here since short delays can occur when a
211 			 * server is temporarily overloaded.
212 			 */
213 			if (rc->rc_timeout.tv_sec > 0 &&
214 			    rc->rc_timeout.tv_usec >= 0) {
215 				error = so_setsockopt(so, SOL_SOCKET,
216 				    SO_SNDTIMEO, &rc->rc_timeout,
217 				    sizeof(struct timeval));
218 				if (error != 0) {
219 					stat = rpc_createerr.cf_stat =
220 					    RPC_CANTSEND;
221 					rpc_createerr.cf_error.re_errno = error;
222 					td->td_ucred = oldcred;
223 					goto out;
224 				}
225 			}
226 			newclient = clnt_vc_create(so,
227 			    (struct sockaddr *) &rc->rc_addr, rc->rc_prog,
228 			    rc->rc_vers, rc->rc_sendsz, rc->rc_recvsz,
229 			    rc->rc_intr);
230 			/*
231 			 * CLSET_FD_CLOSE must be done now, in case
232 			 * rpctls_connect() fails just below.
233 			 */
234 			if (newclient != NULL)
235 				CLNT_CONTROL(newclient, CLSET_FD_CLOSE, 0);
236 			if (rc->rc_tls && newclient != NULL) {
237 				CURVNET_SET(so->so_vnet);
238 				stat = rpctls_connect(newclient,
239 				    rc->rc_tlscertname, so, &reterr);
240 				CURVNET_RESTORE();
241 				if (stat != RPC_SUCCESS ||
242 				    reterr != RPCTLSERR_OK) {
243 					if (stat == RPC_SUCCESS)
244 						stat = RPC_FAILED;
245 					stat = rpc_createerr.cf_stat = stat;
246 					rpc_createerr.cf_error.re_errno = 0;
247 					CLNT_CLOSE(newclient);
248 					CLNT_RELEASE(newclient);
249 					newclient = NULL;
250 					td->td_ucred = oldcred;
251 					goto out;
252 				}
253 				CLNT_CONTROL(newclient, CLSET_TLS,
254 				    &(int){RPCTLS_COMPLETE});
255 			}
256 			if (newclient != NULL) {
257 				int optval = 1;
258 
259 				(void)so_setsockopt(so, IPPROTO_TCP,
260 				    TCP_USE_DDP, &optval, sizeof(optval));
261 			}
262 		}
263 	}
264 	if (newclient != NULL && rc->rc_reconcall != NULL)
265 		(*rc->rc_reconcall)(newclient, rc->rc_reconarg,
266 		    rc->rc_ucred);
267 	td->td_ucred = oldcred;
268 
269 	if (!newclient) {
270 		if (!dordma) {
271 			soclose(so);
272 			rc->rc_err = rpc_createerr.cf_error;
273 			stat = rpc_createerr.cf_stat;
274 		} else {
275 			stat = rc->rc_err.re_status;
276 		}
277 		goto out;
278 	}
279 
280 	CLNT_CONTROL(newclient, CLSET_CONNECT, &one);
281 	CLNT_CONTROL(newclient, CLSET_TIMEOUT, &rc->rc_timeout);
282 	CLNT_CONTROL(newclient, CLSET_RETRY_TIMEOUT, &rc->rc_retry);
283 	CLNT_CONTROL(newclient, CLSET_WAITCHAN, rc->rc_waitchan);
284 	CLNT_CONTROL(newclient, CLSET_INTERRUPTIBLE, &rc->rc_intr);
285 	if (rc->rc_backchannel != NULL)
286 		CLNT_CONTROL(newclient, CLSET_BACKCHANNEL, rc->rc_backchannel);
287 	stat = RPC_SUCCESS;
288 
289 out:
290 	mtx_lock(&rc->rc_lock);
291 	KASSERT(rc->rc_client == NULL, ("rc_client not null"));
292 	if (!rc->rc_closed) {
293 		rc->rc_client = newclient;
294 		newclient = NULL;
295 	}
296 	rc->rc_connecting = FALSE;
297 	wakeup(rc);
298 	mtx_unlock(&rc->rc_lock);
299 
300 	if (newclient) {
301 		/*
302 		 * It has been closed, so discard the new client.
303 		 * nb: clnt_[dg|vc]_close()/clnt_[dg|vc]_destroy() cannot
304 		 * be called with the rc_lock mutex held, since they may
305 		 * msleep() while holding a different mutex.
306 		 */
307 		CLNT_CLOSE(newclient);
308 		CLNT_RELEASE(newclient);
309 	}
310 
311 	return (stat);
312 }
313 
314 static enum clnt_stat
315 clnt_reconnect_call(
316 	CLIENT		*cl,		/* client handle */
317 	struct rpc_callextra *ext,	/* call metadata */
318 	rpcproc_t	proc,		/* procedure number */
319 	struct mbuf	*args,		/* pointer to args */
320 	struct mbuf	**resultsp,	/* pointer to results */
321 	struct timeval	utimeout)
322 {
323 	struct rc_data *rc = (struct rc_data *)cl->cl_private;
324 	CLIENT *client;
325 	enum clnt_stat stat;
326 	int tries, error;
327 
328 	tries = 0;
329 	do {
330 		mtx_lock(&rc->rc_lock);
331 		if (rc->rc_closed) {
332 			mtx_unlock(&rc->rc_lock);
333 			return (RPC_CANTSEND);
334 		}
335 
336 		if (!rc->rc_client) {
337 			mtx_unlock(&rc->rc_lock);
338 			stat = clnt_reconnect_connect(cl);
339 			if (stat == RPC_SYSTEMERROR) {
340 				error = tsleep(&fake_wchan,
341 				    rc->rc_intr ? PCATCH : 0, "rpccon", hz);
342 				if (error == EINTR || error == ERESTART)
343 					return (RPC_INTR);
344 				tries++;
345 				if (tries >= rc->rc_retries)
346 					return (stat);
347 				continue;
348 			}
349 			if (stat != RPC_SUCCESS)
350 				return (stat);
351 			mtx_lock(&rc->rc_lock);
352 		}
353 
354 		if (!rc->rc_client) {
355 			mtx_unlock(&rc->rc_lock);
356 			stat = RPC_FAILED;
357 			continue;
358 		}
359 		CLNT_ACQUIRE(rc->rc_client);
360 		client = rc->rc_client;
361 		mtx_unlock(&rc->rc_lock);
362 		stat = CLNT_CALL_MBUF(client, ext, proc, args,
363 		    resultsp, utimeout);
364 
365 		if (stat != RPC_SUCCESS) {
366 			if (!ext)
367 				CLNT_GETERR(client, &rc->rc_err);
368 		}
369 
370 		if (stat == RPC_TIMEDOUT) {
371 			/*
372 			 * Check for async send misfeature for NLM
373 			 * protocol.
374 			 */
375 			if ((rc->rc_timeout.tv_sec == 0
376 				&& rc->rc_timeout.tv_usec == 0)
377 			    || (rc->rc_timeout.tv_sec == -1
378 				&& utimeout.tv_sec == 0
379 				&& utimeout.tv_usec == 0)) {
380 				CLNT_RELEASE(client);
381 				break;
382 			}
383 		}
384 
385 		if (stat == RPC_TIMEDOUT || stat == RPC_CANTSEND
386 		    || stat == RPC_CANTRECV) {
387 			tries++;
388 			if (tries >= rc->rc_retries) {
389 				CLNT_RELEASE(client);
390 				break;
391 			}
392 
393 			if (ext && ext->rc_feedback)
394 				ext->rc_feedback(FEEDBACK_RECONNECT, proc,
395 				    ext->rc_feedback_arg);
396 
397 			mtx_lock(&rc->rc_lock);
398 			/*
399 			 * Make sure that someone else hasn't already
400 			 * reconnected by checking if rc_client has changed.
401 			 * If not, we are done with the client and must
402 			 * do CLNT_RELEASE(client) twice to dispose of it,
403 			 * because there is both an initial refcnt and one
404 			 * acquired by CLNT_ACQUIRE() above.
405 			 */
406 			if (rc->rc_client == client) {
407 				rc->rc_client = NULL;
408 				mtx_unlock(&rc->rc_lock);
409 				CLNT_RELEASE(client);
410 			} else {
411 				mtx_unlock(&rc->rc_lock);
412 			}
413 			CLNT_RELEASE(client);
414 		} else {
415 			CLNT_RELEASE(client);
416 			break;
417 		}
418 	} while (stat != RPC_SUCCESS);
419 
420 	KASSERT(stat != RPC_SUCCESS || *resultsp,
421 	    ("RPC_SUCCESS without reply"));
422 
423 	return (stat);
424 }
425 
426 static void
427 clnt_reconnect_geterr(CLIENT *cl, struct rpc_err *errp)
428 {
429 	struct rc_data *rc = (struct rc_data *)cl->cl_private;
430 
431 	*errp = rc->rc_err;
432 }
433 
434 /*
435  * Since this function requires that rc_client be valid, it can
436  * only be called when that is guaranteed to be the case.
437  */
438 static bool_t
439 clnt_reconnect_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
440 {
441 	struct rc_data *rc = (struct rc_data *)cl->cl_private;
442 
443 	return (CLNT_FREERES(rc->rc_client, xdr_res, res_ptr));
444 }
445 
446 /*ARGSUSED*/
447 static void
448 clnt_reconnect_abort(CLIENT *h)
449 {
450 }
451 
452 /*
453  * CLNT_CONTROL() on the client returned by clnt_reconnect_create() must
454  * always be called before CLNT_CALL_MBUF() by a single thread only.
455  */
456 static bool_t
457 clnt_reconnect_control(CLIENT *cl, u_int request, void *info)
458 {
459 	struct rc_data *rc = (struct rc_data *)cl->cl_private;
460 	SVCXPRT *xprt;
461 	size_t slen;
462 	struct rpc_reconupcall *upcp;
463 
464 	if (info == NULL) {
465 		return (FALSE);
466 	}
467 	switch (request) {
468 	case CLSET_TIMEOUT:
469 		rc->rc_timeout = *(struct timeval *)info;
470 		if (rc->rc_client)
471 			CLNT_CONTROL(rc->rc_client, request, info);
472 		break;
473 
474 	case CLGET_TIMEOUT:
475 		*(struct timeval *)info = rc->rc_timeout;
476 		break;
477 
478 	case CLSET_RETRY_TIMEOUT:
479 		rc->rc_retry = *(struct timeval *)info;
480 		if (rc->rc_client)
481 			CLNT_CONTROL(rc->rc_client, request, info);
482 		break;
483 
484 	case CLGET_RETRY_TIMEOUT:
485 		*(struct timeval *)info = rc->rc_retry;
486 		break;
487 
488 	case CLGET_VERS:
489 		*(uint32_t *)info = rc->rc_vers;
490 		break;
491 
492 	case CLSET_VERS:
493 		rc->rc_vers = *(uint32_t *) info;
494 		if (rc->rc_client)
495 			CLNT_CONTROL(rc->rc_client, CLSET_VERS, info);
496 		break;
497 
498 	case CLGET_PROG:
499 		*(uint32_t *)info = rc->rc_prog;
500 		break;
501 
502 	case CLSET_PROG:
503 		rc->rc_prog = *(uint32_t *) info;
504 		if (rc->rc_client)
505 			CLNT_CONTROL(rc->rc_client, request, info);
506 		break;
507 
508 	case CLSET_WAITCHAN:
509 		rc->rc_waitchan = (char *)info;
510 		if (rc->rc_client)
511 			CLNT_CONTROL(rc->rc_client, request, info);
512 		break;
513 
514 	case CLGET_WAITCHAN:
515 		*(const char **) info = rc->rc_waitchan;
516 		break;
517 
518 	case CLSET_INTERRUPTIBLE:
519 		rc->rc_intr = *(int *) info;
520 		if (rc->rc_client)
521 			CLNT_CONTROL(rc->rc_client, request, info);
522 		break;
523 
524 	case CLGET_INTERRUPTIBLE:
525 		*(int *) info = rc->rc_intr;
526 		break;
527 
528 	case CLSET_RETRIES:
529 		rc->rc_retries = *(int *) info;
530 		break;
531 
532 	case CLGET_RETRIES:
533 		*(int *) info = rc->rc_retries;
534 		break;
535 
536 	case CLSET_PRIVPORT:
537 		rc->rc_privport = *(int *) info;
538 		break;
539 
540 	case CLGET_PRIVPORT:
541 		*(int *) info = rc->rc_privport;
542 		break;
543 
544 	case CLSET_BACKCHANNEL:
545 		xprt = (SVCXPRT *)info;
546 		xprt_register(xprt);
547 		rc->rc_backchannel = info;
548 		break;
549 
550 	case CLSET_TLS:
551 		rc->rc_tls = true;
552 		break;
553 
554 	case CLSET_TLSCERTNAME:
555 		slen = strlen(info) + 1;
556 		/*
557 		 * tlscertname with "key.pem" appended to it forms a file
558 		 * name.  As such, the maximum allowable strlen(info) is
559 		 * NAME_MAX - 7. However, "slen" includes the nul termination
560 		 * byte so it can be up to NAME_MAX - 6.
561 		 */
562 		if (slen <= 1 || slen > NAME_MAX - 6)
563 			return (FALSE);
564 		rc->rc_tlscertname = mem_alloc(slen);
565 		strlcpy(rc->rc_tlscertname, info, slen);
566 		break;
567 
568 	case CLSET_RECONUPCALL:
569 		upcp = (struct rpc_reconupcall *)info;
570 		rc->rc_reconcall = upcp->call;
571 		rc->rc_reconarg = upcp->arg;
572 		break;
573 
574 	case CLSET_RDMASMALL_REPLY:
575 		rc->rc_rdmasmall_reply = *(int *)info;
576 		break;
577 
578 	case CLSET_RDMA_CBSLOTS:
579 		rc->rc_rdma_cbslots = *(int *)info;
580 		break;
581 
582 
583 	case CLSET_RDMAMAX_IO:
584 		rc->rc_rdmamax_io = *(int *)info;
585 		if (rc->rc_client)
586 			CLNT_CONTROL(rc->rc_client, request, info);
587 		break;
588 
589 	case CLGET_RDMAMAX_IO:
590 		if (rc->rc_client)
591 			CLNT_CONTROL(rc->rc_client, request, info);
592 		break;
593 
594 	default:
595 		return (FALSE);
596 	}
597 
598 	return (TRUE);
599 }
600 
601 static void
602 clnt_reconnect_close(CLIENT *cl)
603 {
604 	struct rc_data *rc = (struct rc_data *)cl->cl_private;
605 	CLIENT *client;
606 
607 	mtx_lock(&rc->rc_lock);
608 
609 	if (rc->rc_closed) {
610 		mtx_unlock(&rc->rc_lock);
611 		return;
612 	}
613 
614 	rc->rc_closed = TRUE;
615 	client = rc->rc_client;
616 	rc->rc_client = NULL;
617 
618 	mtx_unlock(&rc->rc_lock);
619 
620 	if (client) {
621 		CLNT_CLOSE(client);
622 		CLNT_RELEASE(client);
623 	}
624 }
625 
626 static void
627 clnt_reconnect_destroy(CLIENT *cl)
628 {
629 	struct rc_data *rc = (struct rc_data *)cl->cl_private;
630 	SVCXPRT *xprt;
631 
632 	if (rc->rc_client)
633 		CLNT_DESTROY(rc->rc_client);
634 	if (rc->rc_backchannel) {
635 		xprt = (SVCXPRT *)rc->rc_backchannel;
636 		KASSERT(xprt->xp_socket == NULL,
637 		    ("clnt_reconnect_destroy: xp_socket not NULL"));
638 		xprt_unregister(xprt);
639 		SVC_RELEASE(xprt);
640 	}
641 	crfree(rc->rc_ucred);
642 	mtx_destroy(&rc->rc_lock);
643 	mem_free(rc->rc_tlscertname, 0);	/* 0 ok, since arg. ignored. */
644 	mem_free(rc->rc_reconarg, 0);
645 	mem_free(rc, sizeof(*rc));
646 	mem_free(cl, sizeof (CLIENT));
647 }
648