clnt_rc.c revision 181684
1177633Sdfr/*-
2177633Sdfr * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3177633Sdfr * Authors: Doug Rabson <dfr@rabson.org>
4177633Sdfr * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5177633Sdfr *
6177633Sdfr * Redistribution and use in source and binary forms, with or without
7177633Sdfr * modification, are permitted provided that the following conditions
8177633Sdfr * are met:
9177633Sdfr * 1. Redistributions of source code must retain the above copyright
10177633Sdfr *    notice, this list of conditions and the following disclaimer.
11177633Sdfr * 2. Redistributions in binary form must reproduce the above copyright
12177633Sdfr *    notice, this list of conditions and the following disclaimer in the
13177633Sdfr *    documentation and/or other materials provided with the distribution.
14177633Sdfr *
15177633Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16177633Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17177633Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18177633Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19177633Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20177633Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21177633Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22177633Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23177633Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24177633Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25177633Sdfr * SUCH DAMAGE.
26177633Sdfr */
27177633Sdfr
28177633Sdfr#include <sys/cdefs.h>
29177633Sdfr__FBSDID("$FreeBSD: head/sys/rpc/clnt_rc.c 181684 2008-08-13 12:04:54Z dfr $");
30177633Sdfr
31177633Sdfr#include <sys/param.h>
32177633Sdfr#include <sys/systm.h>
33180025Sdfr#include <sys/limits.h>
34177633Sdfr#include <sys/lock.h>
35177633Sdfr#include <sys/malloc.h>
36177633Sdfr#include <sys/mbuf.h>
37177633Sdfr#include <sys/mutex.h>
38177633Sdfr#include <sys/pcpu.h>
39177633Sdfr#include <sys/proc.h>
40177633Sdfr#include <sys/socket.h>
41177633Sdfr#include <sys/socketvar.h>
42177633Sdfr#include <sys/time.h>
43177633Sdfr#include <sys/uio.h>
44177633Sdfr
45177633Sdfr#include <rpc/rpc.h>
46177685Sdfr#include <rpc/rpc_com.h>
47177633Sdfr
48180025Sdfrstatic enum clnt_stat clnt_reconnect_call(CLIENT *, struct rpc_callextra *,
49180025Sdfr    rpcproc_t, xdrproc_t, void *, xdrproc_t, void *, struct timeval);
50177633Sdfrstatic void clnt_reconnect_geterr(CLIENT *, struct rpc_err *);
51177633Sdfrstatic bool_t clnt_reconnect_freeres(CLIENT *, xdrproc_t, void *);
52177633Sdfrstatic void clnt_reconnect_abort(CLIENT *);
53177633Sdfrstatic bool_t clnt_reconnect_control(CLIENT *, u_int, void *);
54177633Sdfrstatic void clnt_reconnect_destroy(CLIENT *);
55177633Sdfr
56177633Sdfrstatic struct clnt_ops clnt_reconnect_ops = {
57177633Sdfr	.cl_call =	clnt_reconnect_call,
58177633Sdfr	.cl_abort =	clnt_reconnect_abort,
59177633Sdfr	.cl_geterr =	clnt_reconnect_geterr,
60177633Sdfr	.cl_freeres =	clnt_reconnect_freeres,
61177633Sdfr	.cl_destroy =	clnt_reconnect_destroy,
62177633Sdfr	.cl_control =	clnt_reconnect_control
63177633Sdfr};
64177633Sdfr
65177633Sdfrstruct rc_data {
66180025Sdfr	struct mtx		rc_lock;
67177633Sdfr	struct sockaddr_storage	rc_addr; /* server address */
68177633Sdfr	struct netconfig*	rc_nconf; /* network type */
69177633Sdfr	rpcprog_t		rc_prog;  /* program number */
70177633Sdfr	rpcvers_t		rc_vers;  /* version number */
71177633Sdfr	size_t			rc_sendsz;
72177633Sdfr	size_t			rc_recvsz;
73177633Sdfr	struct timeval		rc_timeout;
74177633Sdfr	struct timeval		rc_retry;
75180025Sdfr	int			rc_retries;
76177633Sdfr	const char		*rc_waitchan;
77177633Sdfr	int			rc_intr;
78180025Sdfr	int			rc_connecting;
79177633Sdfr	CLIENT*			rc_client; /* underlying RPC client */
80177633Sdfr};
81177633Sdfr
82177633SdfrCLIENT *
83177633Sdfrclnt_reconnect_create(
84177633Sdfr	struct netconfig *nconf,	/* network type */
85177633Sdfr	struct sockaddr *svcaddr,	/* servers address */
86177633Sdfr	rpcprog_t program,		/* program number */
87177633Sdfr	rpcvers_t version,		/* version number */
88177633Sdfr	size_t sendsz,			/* buffer recv size */
89177633Sdfr	size_t recvsz)			/* buffer send size */
90177633Sdfr{
91177633Sdfr	CLIENT *cl = NULL;		/* client handle */
92177633Sdfr	struct rc_data *rc = NULL;	/* private data */
93177633Sdfr
94177633Sdfr	if (svcaddr == NULL) {
95177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
96177633Sdfr		return (NULL);
97177633Sdfr	}
98177633Sdfr
99177633Sdfr	cl = mem_alloc(sizeof (CLIENT));
100177633Sdfr	rc = mem_alloc(sizeof (*rc));
101180025Sdfr	mtx_init(&rc->rc_lock, "rc->rc_lock", NULL, MTX_DEF);
102177633Sdfr	(void) memcpy(&rc->rc_addr, svcaddr, (size_t)svcaddr->sa_len);
103177633Sdfr	rc->rc_nconf = nconf;
104177633Sdfr	rc->rc_prog = program;
105177633Sdfr	rc->rc_vers = version;
106177633Sdfr	rc->rc_sendsz = sendsz;
107177633Sdfr	rc->rc_recvsz = recvsz;
108177633Sdfr	rc->rc_timeout.tv_sec = -1;
109177633Sdfr	rc->rc_timeout.tv_usec = -1;
110180025Sdfr	rc->rc_retry.tv_sec = 3;
111177633Sdfr	rc->rc_retry.tv_usec = 0;
112180025Sdfr	rc->rc_retries = INT_MAX;
113177633Sdfr	rc->rc_waitchan = "rpcrecv";
114177633Sdfr	rc->rc_intr = 0;
115180025Sdfr	rc->rc_connecting = FALSE;
116177633Sdfr	rc->rc_client = NULL;
117177633Sdfr
118180025Sdfr	cl->cl_refs = 1;
119177633Sdfr	cl->cl_ops = &clnt_reconnect_ops;
120177633Sdfr	cl->cl_private = (caddr_t)(void *)rc;
121177633Sdfr	cl->cl_auth = authnone_create();
122177633Sdfr	cl->cl_tp = NULL;
123177633Sdfr	cl->cl_netid = NULL;
124177633Sdfr	return (cl);
125177633Sdfr}
126177633Sdfr
127177633Sdfrstatic enum clnt_stat
128177633Sdfrclnt_reconnect_connect(CLIENT *cl)
129177633Sdfr{
130177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
131177633Sdfr	struct socket *so;
132180025Sdfr	enum clnt_stat stat;
133180025Sdfr	int error;
134177633Sdfr	int one = 1;
135177633Sdfr
136180025Sdfr	mtx_lock(&rc->rc_lock);
137180025Sdfragain:
138180025Sdfr	if (rc->rc_connecting) {
139180025Sdfr		while (!rc->rc_client) {
140180025Sdfr			error = msleep(rc, &rc->rc_lock,
141180025Sdfr			    rc->rc_intr ? PCATCH : 0, "rpcrecon", 0);
142180025Sdfr			if (error) {
143180025Sdfr				mtx_unlock(&rc->rc_lock);
144180025Sdfr				return (RPC_INTR);
145180025Sdfr			}
146180025Sdfr		}
147180025Sdfr		/*
148180025Sdfr		 * If the other guy failed to connect, we might as
149180025Sdfr		 * well have another go.
150180025Sdfr		 */
151180025Sdfr		if (!rc->rc_client && !rc->rc_connecting)
152180025Sdfr			goto again;
153180025Sdfr		mtx_unlock(&rc->rc_lock);
154180025Sdfr		return (RPC_SUCCESS);
155180025Sdfr	} else {
156180025Sdfr		rc->rc_connecting = TRUE;
157180025Sdfr	}
158180025Sdfr	mtx_unlock(&rc->rc_lock);
159180025Sdfr
160177633Sdfr	so = __rpc_nconf2socket(rc->rc_nconf);
161177633Sdfr	if (!so) {
162180025Sdfr		stat = rpc_createerr.cf_stat = RPC_TLIERROR;
163177633Sdfr		rpc_createerr.cf_error.re_errno = 0;
164180025Sdfr		goto out;
165177633Sdfr	}
166177633Sdfr
167177633Sdfr	if (rc->rc_nconf->nc_semantics == NC_TPI_CLTS)
168177633Sdfr		rc->rc_client = clnt_dg_create(so,
169177633Sdfr		    (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
170177633Sdfr		    rc->rc_sendsz, rc->rc_recvsz);
171177633Sdfr	else
172177633Sdfr		rc->rc_client = clnt_vc_create(so,
173177633Sdfr		    (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
174177633Sdfr		    rc->rc_sendsz, rc->rc_recvsz);
175177633Sdfr
176180025Sdfr	if (!rc->rc_client) {
177180025Sdfr		stat = rpc_createerr.cf_stat;
178180025Sdfr		goto out;
179180025Sdfr	}
180178112Sdfr
181177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_FD_CLOSE, 0);
182177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_CONNECT, &one);
183177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_TIMEOUT, &rc->rc_timeout);
184177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_RETRY_TIMEOUT, &rc->rc_retry);
185177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_WAITCHAN, &rc->rc_waitchan);
186177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_INTERRUPTIBLE, &rc->rc_intr);
187180025Sdfr	stat = RPC_SUCCESS;
188177633Sdfr
189180025Sdfrout:
190180025Sdfr	mtx_lock(&rc->rc_lock);
191180025Sdfr	rc->rc_connecting = FALSE;
192180025Sdfr	wakeup(rc);
193180025Sdfr	mtx_unlock(&rc->rc_lock);
194180025Sdfr
195180025Sdfr	return (stat);
196177633Sdfr}
197177633Sdfr
198177633Sdfrstatic enum clnt_stat
199177633Sdfrclnt_reconnect_call(
200180025Sdfr	CLIENT		*cl,		/* client handle */
201180025Sdfr	struct rpc_callextra *ext,	/* call metadata */
202177633Sdfr	rpcproc_t	proc,		/* procedure number */
203177633Sdfr	xdrproc_t	xargs,		/* xdr routine for args */
204177633Sdfr	void		*argsp,		/* pointer to args */
205177633Sdfr	xdrproc_t	xresults,	/* xdr routine for results */
206177633Sdfr	void		*resultsp,	/* pointer to results */
207177633Sdfr	struct timeval	utimeout)	/* seconds to wait before giving up */
208177633Sdfr{
209177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
210180025Sdfr	CLIENT *client;
211177633Sdfr	enum clnt_stat stat;
212180025Sdfr	int tries;
213177633Sdfr
214180025Sdfr	tries = 0;
215177633Sdfr	do {
216178112Sdfr		if (!rc->rc_client) {
217178112Sdfr			stat = clnt_reconnect_connect(cl);
218178112Sdfr			if (stat != RPC_SUCCESS)
219178112Sdfr				return (stat);
220178112Sdfr		}
221177633Sdfr
222180025Sdfr		mtx_lock(&rc->rc_lock);
223180025Sdfr		CLNT_ACQUIRE(rc->rc_client);
224180025Sdfr		client = rc->rc_client;
225180025Sdfr		mtx_unlock(&rc->rc_lock);
226180025Sdfr		stat = CLNT_CALL_EXT(client, ext, proc, xargs, argsp,
227177633Sdfr		    xresults, resultsp, utimeout);
228177633Sdfr
229180025Sdfr		CLNT_RELEASE(client);
230177633Sdfr		if (stat == RPC_TIMEDOUT) {
231177633Sdfr			/*
232177633Sdfr			 * Check for async send misfeature for NLM
233177633Sdfr			 * protocol.
234177633Sdfr			 */
235177633Sdfr			if ((rc->rc_timeout.tv_sec == 0
236177633Sdfr				&& rc->rc_timeout.tv_usec == 0)
237177633Sdfr			    || (rc->rc_timeout.tv_sec == -1
238177633Sdfr				&& utimeout.tv_sec == 0
239180025Sdfr				&& utimeout.tv_usec == 0)) {
240177633Sdfr				break;
241180025Sdfr			}
242177633Sdfr		}
243177633Sdfr
244177633Sdfr		if (stat == RPC_INTR)
245177633Sdfr			break;
246177633Sdfr
247177633Sdfr		if (stat != RPC_SUCCESS) {
248180025Sdfr			tries++;
249180025Sdfr			if (tries >= rc->rc_retries)
250180025Sdfr				break;
251180025Sdfr
252180025Sdfr			if (ext && ext->rc_feedback)
253180025Sdfr				ext->rc_feedback(FEEDBACK_RECONNECT, proc,
254180025Sdfr				    ext->rc_feedback_arg);
255180025Sdfr
256180025Sdfr			mtx_lock(&rc->rc_lock);
257180025Sdfr			/*
258180025Sdfr			 * Make sure that someone else hasn't already
259180025Sdfr			 * reconnected.
260180025Sdfr			 */
261180025Sdfr			if (rc->rc_client == client) {
262180025Sdfr				CLNT_RELEASE(rc->rc_client);
263180025Sdfr				rc->rc_client = NULL;
264180025Sdfr			}
265180025Sdfr			mtx_unlock(&rc->rc_lock);
266177633Sdfr		}
267177633Sdfr	} while (stat != RPC_SUCCESS);
268177633Sdfr
269177633Sdfr	return (stat);
270177633Sdfr}
271177633Sdfr
272177633Sdfrstatic void
273177633Sdfrclnt_reconnect_geterr(CLIENT *cl, struct rpc_err *errp)
274177633Sdfr{
275177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
276177633Sdfr
277177633Sdfr	if (rc->rc_client)
278177633Sdfr		CLNT_GETERR(rc->rc_client, errp);
279177633Sdfr	else
280177633Sdfr		memset(errp, 0, sizeof(*errp));
281177633Sdfr}
282177633Sdfr
283177633Sdfrstatic bool_t
284177633Sdfrclnt_reconnect_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
285177633Sdfr{
286177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
287177633Sdfr
288177633Sdfr	return (CLNT_FREERES(rc->rc_client, xdr_res, res_ptr));
289177633Sdfr}
290177633Sdfr
291177633Sdfr/*ARGSUSED*/
292177633Sdfrstatic void
293177633Sdfrclnt_reconnect_abort(CLIENT *h)
294177633Sdfr{
295177633Sdfr}
296177633Sdfr
297177633Sdfrstatic bool_t
298177633Sdfrclnt_reconnect_control(CLIENT *cl, u_int request, void *info)
299177633Sdfr{
300177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
301177633Sdfr
302177633Sdfr	if (info == NULL) {
303177633Sdfr		return (FALSE);
304177633Sdfr	}
305177633Sdfr	switch (request) {
306177633Sdfr	case CLSET_TIMEOUT:
307177633Sdfr		rc->rc_timeout = *(struct timeval *)info;
308177633Sdfr		if (rc->rc_client)
309177633Sdfr			CLNT_CONTROL(rc->rc_client, request, info);
310177633Sdfr		break;
311177633Sdfr
312177633Sdfr	case CLGET_TIMEOUT:
313177633Sdfr		*(struct timeval *)info = rc->rc_timeout;
314177633Sdfr		break;
315177633Sdfr
316177633Sdfr	case CLSET_RETRY_TIMEOUT:
317177633Sdfr		rc->rc_retry = *(struct timeval *)info;
318177633Sdfr		if (rc->rc_client)
319177633Sdfr			CLNT_CONTROL(rc->rc_client, request, info);
320177633Sdfr		break;
321177633Sdfr
322177633Sdfr	case CLGET_RETRY_TIMEOUT:
323177633Sdfr		*(struct timeval *)info = rc->rc_retry;
324177633Sdfr		break;
325177633Sdfr
326177633Sdfr	case CLGET_VERS:
327177633Sdfr		*(uint32_t *)info = rc->rc_vers;
328177633Sdfr		break;
329177633Sdfr
330177633Sdfr	case CLSET_VERS:
331177633Sdfr		rc->rc_vers = *(uint32_t *) info;
332177633Sdfr		if (rc->rc_client)
333177633Sdfr			CLNT_CONTROL(rc->rc_client, CLSET_VERS, info);
334177633Sdfr		break;
335177633Sdfr
336177633Sdfr	case CLGET_PROG:
337177633Sdfr		*(uint32_t *)info = rc->rc_prog;
338177633Sdfr		break;
339177633Sdfr
340177633Sdfr	case CLSET_PROG:
341177633Sdfr		rc->rc_prog = *(uint32_t *) info;
342177633Sdfr		if (rc->rc_client)
343177633Sdfr			CLNT_CONTROL(rc->rc_client, request, info);
344177633Sdfr		break;
345177633Sdfr
346177633Sdfr	case CLSET_WAITCHAN:
347177633Sdfr		rc->rc_waitchan = *(const char **)info;
348177633Sdfr		if (rc->rc_client)
349177633Sdfr			CLNT_CONTROL(rc->rc_client, request, info);
350177633Sdfr		break;
351177633Sdfr
352177633Sdfr	case CLGET_WAITCHAN:
353177633Sdfr		*(const char **) info = rc->rc_waitchan;
354177633Sdfr		break;
355177633Sdfr
356177633Sdfr	case CLSET_INTERRUPTIBLE:
357177633Sdfr		rc->rc_intr = *(int *) info;
358177633Sdfr		if (rc->rc_client)
359177633Sdfr			CLNT_CONTROL(rc->rc_client, request, info);
360177633Sdfr		break;
361177633Sdfr
362177633Sdfr	case CLGET_INTERRUPTIBLE:
363177633Sdfr		*(int *) info = rc->rc_intr;
364177633Sdfr		break;
365177633Sdfr
366180025Sdfr	case CLSET_RETRIES:
367180025Sdfr		rc->rc_retries = *(int *) info;
368180025Sdfr		break;
369180025Sdfr
370180025Sdfr	case CLGET_RETRIES:
371180025Sdfr		*(int *) info = rc->rc_retries;
372180025Sdfr		break;
373180025Sdfr
374177633Sdfr	default:
375177633Sdfr		return (FALSE);
376177633Sdfr	}
377177633Sdfr
378177633Sdfr	return (TRUE);
379177633Sdfr}
380177633Sdfr
381177633Sdfrstatic void
382177633Sdfrclnt_reconnect_destroy(CLIENT *cl)
383177633Sdfr{
384177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
385177633Sdfr
386177633Sdfr	if (rc->rc_client)
387177633Sdfr		CLNT_DESTROY(rc->rc_client);
388181684Sdfr	mtx_destroy(&rc->rc_lock);
389177633Sdfr	mem_free(rc, sizeof(*rc));
390177633Sdfr	mem_free(cl, sizeof (CLIENT));
391177633Sdfr}
392