clnt_rc.c revision 177633
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 177633 2008-03-26 15:23:12Z dfr $");
30177633Sdfr
31177633Sdfr#include <sys/param.h>
32177633Sdfr#include <sys/systm.h>
33177633Sdfr#include <sys/lock.h>
34177633Sdfr#include <sys/malloc.h>
35177633Sdfr#include <sys/mbuf.h>
36177633Sdfr#include <sys/mutex.h>
37177633Sdfr#include <sys/pcpu.h>
38177633Sdfr#include <sys/proc.h>
39177633Sdfr#include <sys/socket.h>
40177633Sdfr#include <sys/socketvar.h>
41177633Sdfr#include <sys/time.h>
42177633Sdfr#include <sys/uio.h>
43177633Sdfr
44177633Sdfr#include <rpc/rpc.h>
45177633Sdfr#include "rpc_com.h"
46177633Sdfr
47177633Sdfrstatic enum clnt_stat clnt_reconnect_call(CLIENT *, rpcproc_t,
48177633Sdfr    xdrproc_t, void *, xdrproc_t, void *, struct timeval);
49177633Sdfrstatic void clnt_reconnect_geterr(CLIENT *, struct rpc_err *);
50177633Sdfrstatic bool_t clnt_reconnect_freeres(CLIENT *, xdrproc_t, void *);
51177633Sdfrstatic void clnt_reconnect_abort(CLIENT *);
52177633Sdfrstatic bool_t clnt_reconnect_control(CLIENT *, u_int, void *);
53177633Sdfrstatic void clnt_reconnect_destroy(CLIENT *);
54177633Sdfr
55177633Sdfrstatic struct clnt_ops clnt_reconnect_ops = {
56177633Sdfr	.cl_call =	clnt_reconnect_call,
57177633Sdfr	.cl_abort =	clnt_reconnect_abort,
58177633Sdfr	.cl_geterr =	clnt_reconnect_geterr,
59177633Sdfr	.cl_freeres =	clnt_reconnect_freeres,
60177633Sdfr	.cl_destroy =	clnt_reconnect_destroy,
61177633Sdfr	.cl_control =	clnt_reconnect_control
62177633Sdfr};
63177633Sdfr
64177633Sdfrstruct rc_data {
65177633Sdfr	struct sockaddr_storage	rc_addr; /* server address */
66177633Sdfr	struct netconfig*	rc_nconf; /* network type */
67177633Sdfr	rpcprog_t		rc_prog;  /* program number */
68177633Sdfr	rpcvers_t		rc_vers;  /* version number */
69177633Sdfr	size_t			rc_sendsz;
70177633Sdfr	size_t			rc_recvsz;
71177633Sdfr	struct timeval		rc_timeout;
72177633Sdfr	struct timeval		rc_retry;
73177633Sdfr	const char		*rc_waitchan;
74177633Sdfr	int			rc_intr;
75177633Sdfr	CLIENT*			rc_client; /* underlying RPC client */
76177633Sdfr};
77177633Sdfr
78177633SdfrCLIENT *
79177633Sdfrclnt_reconnect_create(
80177633Sdfr	struct netconfig *nconf,	/* network type */
81177633Sdfr	struct sockaddr *svcaddr,	/* servers address */
82177633Sdfr	rpcprog_t program,		/* program number */
83177633Sdfr	rpcvers_t version,		/* version number */
84177633Sdfr	size_t sendsz,			/* buffer recv size */
85177633Sdfr	size_t recvsz)			/* buffer send size */
86177633Sdfr{
87177633Sdfr	CLIENT *cl = NULL;		/* client handle */
88177633Sdfr	struct rc_data *rc = NULL;	/* private data */
89177633Sdfr
90177633Sdfr	if (svcaddr == NULL) {
91177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
92177633Sdfr		return (NULL);
93177633Sdfr	}
94177633Sdfr
95177633Sdfr	cl = mem_alloc(sizeof (CLIENT));
96177633Sdfr	rc = mem_alloc(sizeof (*rc));
97177633Sdfr	(void) memcpy(&rc->rc_addr, svcaddr, (size_t)svcaddr->sa_len);
98177633Sdfr	rc->rc_nconf = nconf;
99177633Sdfr	rc->rc_prog = program;
100177633Sdfr	rc->rc_vers = version;
101177633Sdfr	rc->rc_sendsz = sendsz;
102177633Sdfr	rc->rc_recvsz = recvsz;
103177633Sdfr	rc->rc_timeout.tv_sec = -1;
104177633Sdfr	rc->rc_timeout.tv_usec = -1;
105177633Sdfr	rc->rc_retry.tv_sec = 15;
106177633Sdfr	rc->rc_retry.tv_usec = 0;
107177633Sdfr	rc->rc_waitchan = "rpcrecv";
108177633Sdfr	rc->rc_intr = 0;
109177633Sdfr	rc->rc_client = NULL;
110177633Sdfr
111177633Sdfr	cl->cl_ops = &clnt_reconnect_ops;
112177633Sdfr	cl->cl_private = (caddr_t)(void *)rc;
113177633Sdfr	cl->cl_auth = authnone_create();
114177633Sdfr	cl->cl_tp = NULL;
115177633Sdfr	cl->cl_netid = NULL;
116177633Sdfr	return (cl);
117177633Sdfr}
118177633Sdfr
119177633Sdfrstatic enum clnt_stat
120177633Sdfrclnt_reconnect_connect(CLIENT *cl)
121177633Sdfr{
122177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
123177633Sdfr	struct socket *so;
124177633Sdfr	int one = 1;
125177633Sdfr
126177633Sdfr	so = __rpc_nconf2socket(rc->rc_nconf);
127177633Sdfr	if (!so) {
128177633Sdfr		rpc_createerr.cf_stat = RPC_TLIERROR;
129177633Sdfr		rpc_createerr.cf_error.re_errno = 0;
130177633Sdfr		return (RPC_TLIERROR);
131177633Sdfr	}
132177633Sdfr
133177633Sdfr	if (rc->rc_nconf->nc_semantics == NC_TPI_CLTS)
134177633Sdfr		rc->rc_client = clnt_dg_create(so,
135177633Sdfr		    (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
136177633Sdfr		    rc->rc_sendsz, rc->rc_recvsz);
137177633Sdfr	else
138177633Sdfr		rc->rc_client = clnt_vc_create(so,
139177633Sdfr		    (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
140177633Sdfr		    rc->rc_sendsz, rc->rc_recvsz);
141177633Sdfr
142177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_FD_CLOSE, 0);
143177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_CONNECT, &one);
144177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_TIMEOUT, &rc->rc_timeout);
145177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_RETRY_TIMEOUT, &rc->rc_retry);
146177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_WAITCHAN, &rc->rc_waitchan);
147177633Sdfr	CLNT_CONTROL(rc->rc_client, CLSET_INTERRUPTIBLE, &rc->rc_intr);
148177633Sdfr
149177633Sdfr	return (RPC_SUCCESS);
150177633Sdfr}
151177633Sdfr
152177633Sdfrstatic enum clnt_stat
153177633Sdfrclnt_reconnect_call(
154177633Sdfr	CLIENT	*cl,			/* client handle */
155177633Sdfr	rpcproc_t	proc,		/* procedure number */
156177633Sdfr	xdrproc_t	xargs,		/* xdr routine for args */
157177633Sdfr	void		*argsp,		/* pointer to args */
158177633Sdfr	xdrproc_t	xresults,	/* xdr routine for results */
159177633Sdfr	void		*resultsp,	/* pointer to results */
160177633Sdfr	struct timeval	utimeout)	/* seconds to wait before giving up */
161177633Sdfr{
162177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
163177633Sdfr	enum clnt_stat stat;
164177633Sdfr
165177633Sdfr	do {
166177633Sdfr		if (!rc->rc_client)
167177633Sdfr			clnt_reconnect_connect(cl);
168177633Sdfr
169177633Sdfr		stat = CLNT_CALL(rc->rc_client, proc, xargs, argsp,
170177633Sdfr		    xresults, resultsp, utimeout);
171177633Sdfr
172177633Sdfr		if (stat == RPC_TIMEDOUT) {
173177633Sdfr			/*
174177633Sdfr			 * Check for async send misfeature for NLM
175177633Sdfr			 * protocol.
176177633Sdfr			 */
177177633Sdfr			if ((rc->rc_timeout.tv_sec == 0
178177633Sdfr				&& rc->rc_timeout.tv_usec == 0)
179177633Sdfr			    || (rc->rc_timeout.tv_sec == -1
180177633Sdfr				&& utimeout.tv_sec == 0
181177633Sdfr				&& utimeout.tv_usec == 0))
182177633Sdfr				break;
183177633Sdfr		}
184177633Sdfr
185177633Sdfr		if (stat == RPC_INTR)
186177633Sdfr			break;
187177633Sdfr
188177633Sdfr		if (stat != RPC_SUCCESS) {
189177633Sdfr			CLNT_DESTROY(rc->rc_client);
190177633Sdfr			rc->rc_client = NULL;
191177633Sdfr		}
192177633Sdfr	} while (stat != RPC_SUCCESS);
193177633Sdfr
194177633Sdfr	return (stat);
195177633Sdfr}
196177633Sdfr
197177633Sdfrstatic void
198177633Sdfrclnt_reconnect_geterr(CLIENT *cl, struct rpc_err *errp)
199177633Sdfr{
200177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
201177633Sdfr
202177633Sdfr	if (rc->rc_client)
203177633Sdfr		CLNT_GETERR(rc->rc_client, errp);
204177633Sdfr	else
205177633Sdfr		memset(errp, 0, sizeof(*errp));
206177633Sdfr}
207177633Sdfr
208177633Sdfrstatic bool_t
209177633Sdfrclnt_reconnect_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
210177633Sdfr{
211177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
212177633Sdfr
213177633Sdfr	return (CLNT_FREERES(rc->rc_client, xdr_res, res_ptr));
214177633Sdfr}
215177633Sdfr
216177633Sdfr/*ARGSUSED*/
217177633Sdfrstatic void
218177633Sdfrclnt_reconnect_abort(CLIENT *h)
219177633Sdfr{
220177633Sdfr}
221177633Sdfr
222177633Sdfrstatic bool_t
223177633Sdfrclnt_reconnect_control(CLIENT *cl, u_int request, void *info)
224177633Sdfr{
225177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
226177633Sdfr
227177633Sdfr	if (info == NULL) {
228177633Sdfr		return (FALSE);
229177633Sdfr	}
230177633Sdfr	switch (request) {
231177633Sdfr	case CLSET_TIMEOUT:
232177633Sdfr		rc->rc_timeout = *(struct timeval *)info;
233177633Sdfr		if (rc->rc_client)
234177633Sdfr			CLNT_CONTROL(rc->rc_client, request, info);
235177633Sdfr		break;
236177633Sdfr
237177633Sdfr	case CLGET_TIMEOUT:
238177633Sdfr		*(struct timeval *)info = rc->rc_timeout;
239177633Sdfr		break;
240177633Sdfr
241177633Sdfr	case CLSET_RETRY_TIMEOUT:
242177633Sdfr		rc->rc_retry = *(struct timeval *)info;
243177633Sdfr		if (rc->rc_client)
244177633Sdfr			CLNT_CONTROL(rc->rc_client, request, info);
245177633Sdfr		break;
246177633Sdfr
247177633Sdfr	case CLGET_RETRY_TIMEOUT:
248177633Sdfr		*(struct timeval *)info = rc->rc_retry;
249177633Sdfr		break;
250177633Sdfr
251177633Sdfr	case CLGET_VERS:
252177633Sdfr		*(uint32_t *)info = rc->rc_vers;
253177633Sdfr		break;
254177633Sdfr
255177633Sdfr	case CLSET_VERS:
256177633Sdfr		rc->rc_vers = *(uint32_t *) info;
257177633Sdfr		if (rc->rc_client)
258177633Sdfr			CLNT_CONTROL(rc->rc_client, CLSET_VERS, info);
259177633Sdfr		break;
260177633Sdfr
261177633Sdfr	case CLGET_PROG:
262177633Sdfr		*(uint32_t *)info = rc->rc_prog;
263177633Sdfr		break;
264177633Sdfr
265177633Sdfr	case CLSET_PROG:
266177633Sdfr		rc->rc_prog = *(uint32_t *) info;
267177633Sdfr		if (rc->rc_client)
268177633Sdfr			CLNT_CONTROL(rc->rc_client, request, info);
269177633Sdfr		break;
270177633Sdfr
271177633Sdfr	case CLSET_WAITCHAN:
272177633Sdfr		rc->rc_waitchan = *(const char **)info;
273177633Sdfr		if (rc->rc_client)
274177633Sdfr			CLNT_CONTROL(rc->rc_client, request, info);
275177633Sdfr		break;
276177633Sdfr
277177633Sdfr	case CLGET_WAITCHAN:
278177633Sdfr		*(const char **) info = rc->rc_waitchan;
279177633Sdfr		break;
280177633Sdfr
281177633Sdfr	case CLSET_INTERRUPTIBLE:
282177633Sdfr		rc->rc_intr = *(int *) info;
283177633Sdfr		if (rc->rc_client)
284177633Sdfr			CLNT_CONTROL(rc->rc_client, request, info);
285177633Sdfr		break;
286177633Sdfr
287177633Sdfr	case CLGET_INTERRUPTIBLE:
288177633Sdfr		*(int *) info = rc->rc_intr;
289177633Sdfr		break;
290177633Sdfr
291177633Sdfr	default:
292177633Sdfr		return (FALSE);
293177633Sdfr	}
294177633Sdfr
295177633Sdfr	return (TRUE);
296177633Sdfr}
297177633Sdfr
298177633Sdfrstatic void
299177633Sdfrclnt_reconnect_destroy(CLIENT *cl)
300177633Sdfr{
301177633Sdfr	struct rc_data *rc = (struct rc_data *)cl->cl_private;
302177633Sdfr
303177633Sdfr	if (rc->rc_client)
304177633Sdfr		CLNT_DESTROY(rc->rc_client);
305177633Sdfr	mem_free(rc, sizeof(*rc));
306177633Sdfr	mem_free(cl, sizeof (CLIENT));
307177633Sdfr}
308