]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/rpc/clnt_rc.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / rpc / clnt_rc.c
1 /*-
2  * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3  * Authors: Doug Rabson <dfr@rabson.org>
4  * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/systm.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 <rpc/rpc.h>
46 #include <rpc/rpc_com.h>
47
48 static enum clnt_stat clnt_reconnect_call(CLIENT *, struct rpc_callextra *,
49     rpcproc_t, xdrproc_t, void *, xdrproc_t, void *, struct timeval);
50 static void clnt_reconnect_geterr(CLIENT *, struct rpc_err *);
51 static bool_t clnt_reconnect_freeres(CLIENT *, xdrproc_t, void *);
52 static void clnt_reconnect_abort(CLIENT *);
53 static bool_t clnt_reconnect_control(CLIENT *, u_int, void *);
54 static void clnt_reconnect_destroy(CLIENT *);
55
56 static struct clnt_ops clnt_reconnect_ops = {
57         .cl_call =      clnt_reconnect_call,
58         .cl_abort =     clnt_reconnect_abort,
59         .cl_geterr =    clnt_reconnect_geterr,
60         .cl_freeres =   clnt_reconnect_freeres,
61         .cl_destroy =   clnt_reconnect_destroy,
62         .cl_control =   clnt_reconnect_control
63 };
64
65 struct rc_data {
66         struct mtx              rc_lock;
67         struct sockaddr_storage rc_addr; /* server address */
68         struct netconfig*       rc_nconf; /* network type */
69         rpcprog_t               rc_prog;  /* program number */
70         rpcvers_t               rc_vers;  /* version number */
71         size_t                  rc_sendsz;
72         size_t                  rc_recvsz;
73         struct timeval          rc_timeout;
74         struct timeval          rc_retry;
75         int                     rc_retries;
76         const char              *rc_waitchan;
77         int                     rc_intr;
78         int                     rc_connecting;
79         CLIENT*                 rc_client; /* underlying RPC client */
80 };
81
82 CLIENT *
83 clnt_reconnect_create(
84         struct netconfig *nconf,        /* network type */
85         struct sockaddr *svcaddr,       /* servers address */
86         rpcprog_t program,              /* program number */
87         rpcvers_t version,              /* version number */
88         size_t sendsz,                  /* buffer recv size */
89         size_t recvsz)                  /* buffer send size */
90 {
91         CLIENT *cl = NULL;              /* client handle */
92         struct rc_data *rc = NULL;      /* private data */
93
94         if (svcaddr == NULL) {
95                 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
96                 return (NULL);
97         }
98
99         cl = mem_alloc(sizeof (CLIENT));
100         rc = mem_alloc(sizeof (*rc));
101         mtx_init(&rc->rc_lock, "rc->rc_lock", NULL, MTX_DEF);
102         (void) memcpy(&rc->rc_addr, svcaddr, (size_t)svcaddr->sa_len);
103         rc->rc_nconf = nconf;
104         rc->rc_prog = program;
105         rc->rc_vers = version;
106         rc->rc_sendsz = sendsz;
107         rc->rc_recvsz = recvsz;
108         rc->rc_timeout.tv_sec = -1;
109         rc->rc_timeout.tv_usec = -1;
110         rc->rc_retry.tv_sec = 3;
111         rc->rc_retry.tv_usec = 0;
112         rc->rc_retries = INT_MAX;
113         rc->rc_waitchan = "rpcrecv";
114         rc->rc_intr = 0;
115         rc->rc_connecting = FALSE;
116         rc->rc_client = NULL;
117
118         cl->cl_refs = 1;
119         cl->cl_ops = &clnt_reconnect_ops;
120         cl->cl_private = (caddr_t)(void *)rc;
121         cl->cl_auth = authnone_create();
122         cl->cl_tp = NULL;
123         cl->cl_netid = NULL;
124         return (cl);
125 }
126
127 static enum clnt_stat
128 clnt_reconnect_connect(CLIENT *cl)
129 {
130         struct rc_data *rc = (struct rc_data *)cl->cl_private;
131         struct socket *so;
132         enum clnt_stat stat;
133         int error;
134         int one = 1;
135
136         mtx_lock(&rc->rc_lock);
137 again:
138         if (rc->rc_connecting) {
139                 while (!rc->rc_client) {
140                         error = msleep(rc, &rc->rc_lock,
141                             rc->rc_intr ? PCATCH : 0, "rpcrecon", 0);
142                         if (error) {
143                                 mtx_unlock(&rc->rc_lock);
144                                 return (RPC_INTR);
145                         }
146                 }
147                 /*
148                  * If the other guy failed to connect, we might as
149                  * well have another go.
150                  */
151                 if (!rc->rc_client && !rc->rc_connecting)
152                         goto again;
153                 mtx_unlock(&rc->rc_lock);
154                 return (RPC_SUCCESS);
155         } else {
156                 rc->rc_connecting = TRUE;
157         }
158         mtx_unlock(&rc->rc_lock);
159
160         so = __rpc_nconf2socket(rc->rc_nconf);
161         if (!so) {
162                 stat = rpc_createerr.cf_stat = RPC_TLIERROR;
163                 rpc_createerr.cf_error.re_errno = 0;
164                 goto out;
165         }
166
167         if (rc->rc_nconf->nc_semantics == NC_TPI_CLTS)
168                 rc->rc_client = clnt_dg_create(so,
169                     (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
170                     rc->rc_sendsz, rc->rc_recvsz);
171         else
172                 rc->rc_client = clnt_vc_create(so,
173                     (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
174                     rc->rc_sendsz, rc->rc_recvsz);
175
176         if (!rc->rc_client) {
177                 stat = rpc_createerr.cf_stat;
178                 goto out;
179         }
180
181         CLNT_CONTROL(rc->rc_client, CLSET_FD_CLOSE, 0);
182         CLNT_CONTROL(rc->rc_client, CLSET_CONNECT, &one);
183         CLNT_CONTROL(rc->rc_client, CLSET_TIMEOUT, &rc->rc_timeout);
184         CLNT_CONTROL(rc->rc_client, CLSET_RETRY_TIMEOUT, &rc->rc_retry);
185         CLNT_CONTROL(rc->rc_client, CLSET_WAITCHAN, &rc->rc_waitchan);
186         CLNT_CONTROL(rc->rc_client, CLSET_INTERRUPTIBLE, &rc->rc_intr);
187         stat = RPC_SUCCESS;
188
189 out:
190         mtx_lock(&rc->rc_lock);
191         rc->rc_connecting = FALSE;
192         wakeup(rc);
193         mtx_unlock(&rc->rc_lock);
194
195         return (stat);
196 }
197
198 static enum clnt_stat
199 clnt_reconnect_call(
200         CLIENT          *cl,            /* client handle */
201         struct rpc_callextra *ext,      /* call metadata */
202         rpcproc_t       proc,           /* procedure number */
203         xdrproc_t       xargs,          /* xdr routine for args */
204         void            *argsp,         /* pointer to args */
205         xdrproc_t       xresults,       /* xdr routine for results */
206         void            *resultsp,      /* pointer to results */
207         struct timeval  utimeout)       /* seconds to wait before giving up */
208 {
209         struct rc_data *rc = (struct rc_data *)cl->cl_private;
210         CLIENT *client;
211         enum clnt_stat stat;
212         int tries;
213
214         tries = 0;
215         do {
216                 if (!rc->rc_client) {
217                         stat = clnt_reconnect_connect(cl);
218                         if (stat != RPC_SUCCESS)
219                                 return (stat);
220                 }
221
222                 mtx_lock(&rc->rc_lock);
223                 CLNT_ACQUIRE(rc->rc_client);
224                 client = rc->rc_client;
225                 mtx_unlock(&rc->rc_lock);
226                 stat = CLNT_CALL_EXT(client, ext, proc, xargs, argsp,
227                     xresults, resultsp, utimeout);
228
229                 CLNT_RELEASE(client);
230                 if (stat == RPC_TIMEDOUT) {
231                         /*
232                          * Check for async send misfeature for NLM
233                          * protocol.
234                          */
235                         if ((rc->rc_timeout.tv_sec == 0
236                                 && rc->rc_timeout.tv_usec == 0)
237                             || (rc->rc_timeout.tv_sec == -1
238                                 && utimeout.tv_sec == 0
239                                 && utimeout.tv_usec == 0)) {
240                                 break;
241                         }
242                 }
243
244                 if (stat == RPC_INTR)
245                         break;
246
247                 if (stat != RPC_SUCCESS) {
248                         tries++;
249                         if (tries >= rc->rc_retries)
250                                 break;
251
252                         if (ext && ext->rc_feedback)
253                                 ext->rc_feedback(FEEDBACK_RECONNECT, proc,
254                                     ext->rc_feedback_arg);
255
256                         mtx_lock(&rc->rc_lock);
257                         /*
258                          * Make sure that someone else hasn't already
259                          * reconnected.
260                          */
261                         if (rc->rc_client == client) {
262                                 CLNT_RELEASE(rc->rc_client);
263                                 rc->rc_client = NULL;
264                         }
265                         mtx_unlock(&rc->rc_lock);
266                 }
267         } while (stat != RPC_SUCCESS);
268
269         return (stat);
270 }
271
272 static void
273 clnt_reconnect_geterr(CLIENT *cl, struct rpc_err *errp)
274 {
275         struct rc_data *rc = (struct rc_data *)cl->cl_private;
276
277         if (rc->rc_client)
278                 CLNT_GETERR(rc->rc_client, errp);
279         else
280                 memset(errp, 0, sizeof(*errp));
281 }
282
283 static bool_t
284 clnt_reconnect_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
285 {
286         struct rc_data *rc = (struct rc_data *)cl->cl_private;
287
288         return (CLNT_FREERES(rc->rc_client, xdr_res, res_ptr));
289 }
290
291 /*ARGSUSED*/
292 static void
293 clnt_reconnect_abort(CLIENT *h)
294 {
295 }
296
297 static bool_t
298 clnt_reconnect_control(CLIENT *cl, u_int request, void *info)
299 {
300         struct rc_data *rc = (struct rc_data *)cl->cl_private;
301
302         if (info == NULL) {
303                 return (FALSE);
304         }
305         switch (request) {
306         case CLSET_TIMEOUT:
307                 rc->rc_timeout = *(struct timeval *)info;
308                 if (rc->rc_client)
309                         CLNT_CONTROL(rc->rc_client, request, info);
310                 break;
311
312         case CLGET_TIMEOUT:
313                 *(struct timeval *)info = rc->rc_timeout;
314                 break;
315
316         case CLSET_RETRY_TIMEOUT:
317                 rc->rc_retry = *(struct timeval *)info;
318                 if (rc->rc_client)
319                         CLNT_CONTROL(rc->rc_client, request, info);
320                 break;
321
322         case CLGET_RETRY_TIMEOUT:
323                 *(struct timeval *)info = rc->rc_retry;
324                 break;
325
326         case CLGET_VERS:
327                 *(uint32_t *)info = rc->rc_vers;
328                 break;
329
330         case CLSET_VERS:
331                 rc->rc_vers = *(uint32_t *) info;
332                 if (rc->rc_client)
333                         CLNT_CONTROL(rc->rc_client, CLSET_VERS, info);
334                 break;
335
336         case CLGET_PROG:
337                 *(uint32_t *)info = rc->rc_prog;
338                 break;
339
340         case CLSET_PROG:
341                 rc->rc_prog = *(uint32_t *) info;
342                 if (rc->rc_client)
343                         CLNT_CONTROL(rc->rc_client, request, info);
344                 break;
345
346         case CLSET_WAITCHAN:
347                 rc->rc_waitchan = *(const char **)info;
348                 if (rc->rc_client)
349                         CLNT_CONTROL(rc->rc_client, request, info);
350                 break;
351
352         case CLGET_WAITCHAN:
353                 *(const char **) info = rc->rc_waitchan;
354                 break;
355
356         case CLSET_INTERRUPTIBLE:
357                 rc->rc_intr = *(int *) info;
358                 if (rc->rc_client)
359                         CLNT_CONTROL(rc->rc_client, request, info);
360                 break;
361
362         case CLGET_INTERRUPTIBLE:
363                 *(int *) info = rc->rc_intr;
364                 break;
365
366         case CLSET_RETRIES:
367                 rc->rc_retries = *(int *) info;
368                 break;
369
370         case CLGET_RETRIES:
371                 *(int *) info = rc->rc_retries;
372                 break;
373
374         default:
375                 return (FALSE);
376         }
377
378         return (TRUE);
379 }
380
381 static void
382 clnt_reconnect_destroy(CLIENT *cl)
383 {
384         struct rc_data *rc = (struct rc_data *)cl->cl_private;
385
386         if (rc->rc_client)
387                 CLNT_DESTROY(rc->rc_client);
388         mtx_destroy(&rc->rc_lock);
389         mem_free(rc, sizeof(*rc));
390         mem_free(cl, sizeof (CLIENT));
391 }