]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/rpc/clnt_rc.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.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/kernel.h>
34 #include <sys/limits.h>
35 #include <sys/lock.h>
36 #include <sys/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/mutex.h>
39 #include <sys/pcpu.h>
40 #include <sys/proc.h>
41 #include <sys/socket.h>
42 #include <sys/socketvar.h>
43 #include <sys/time.h>
44 #include <sys/uio.h>
45
46 #include <rpc/rpc.h>
47 #include <rpc/rpc_com.h>
48
49 static enum clnt_stat clnt_reconnect_call(CLIENT *, struct rpc_callextra *,
50     rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
51 static void clnt_reconnect_geterr(CLIENT *, struct rpc_err *);
52 static bool_t clnt_reconnect_freeres(CLIENT *, xdrproc_t, void *);
53 static void clnt_reconnect_abort(CLIENT *);
54 static bool_t clnt_reconnect_control(CLIENT *, u_int, void *);
55 static void clnt_reconnect_close(CLIENT *);
56 static void clnt_reconnect_destroy(CLIENT *);
57
58 static struct clnt_ops clnt_reconnect_ops = {
59         .cl_call =      clnt_reconnect_call,
60         .cl_abort =     clnt_reconnect_abort,
61         .cl_geterr =    clnt_reconnect_geterr,
62         .cl_freeres =   clnt_reconnect_freeres,
63         .cl_close =     clnt_reconnect_close,
64         .cl_destroy =   clnt_reconnect_destroy,
65         .cl_control =   clnt_reconnect_control
66 };
67
68 static int      fake_wchan;
69
70 struct rc_data {
71         struct mtx              rc_lock;
72         struct sockaddr_storage rc_addr; /* server address */
73         struct netconfig*       rc_nconf; /* network type */
74         rpcprog_t               rc_prog;  /* program number */
75         rpcvers_t               rc_vers;  /* version number */
76         size_t                  rc_sendsz;
77         size_t                  rc_recvsz;
78         struct timeval          rc_timeout;
79         struct timeval          rc_retry;
80         int                     rc_retries;
81         int                     rc_privport;
82         char                    *rc_waitchan;
83         int                     rc_intr;
84         int                     rc_connecting;
85         int                     rc_closed;
86         struct ucred            *rc_ucred;
87         CLIENT*                 rc_client; /* underlying RPC client */
88         struct rpc_err          rc_err;
89 };
90
91 CLIENT *
92 clnt_reconnect_create(
93         struct netconfig *nconf,        /* network type */
94         struct sockaddr *svcaddr,       /* servers address */
95         rpcprog_t program,              /* program number */
96         rpcvers_t version,              /* version number */
97         size_t sendsz,                  /* buffer recv size */
98         size_t recvsz)                  /* buffer send size */
99 {
100         CLIENT *cl = NULL;              /* client handle */
101         struct rc_data *rc = NULL;      /* private data */
102
103         if (svcaddr == NULL) {
104                 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
105                 return (NULL);
106         }
107
108         cl = mem_alloc(sizeof (CLIENT));
109         rc = mem_alloc(sizeof (*rc));
110         mtx_init(&rc->rc_lock, "rc->rc_lock", NULL, MTX_DEF);
111         (void) memcpy(&rc->rc_addr, svcaddr, (size_t)svcaddr->sa_len);
112         rc->rc_nconf = nconf;
113         rc->rc_prog = program;
114         rc->rc_vers = version;
115         rc->rc_sendsz = sendsz;
116         rc->rc_recvsz = recvsz;
117         rc->rc_timeout.tv_sec = -1;
118         rc->rc_timeout.tv_usec = -1;
119         rc->rc_retry.tv_sec = 3;
120         rc->rc_retry.tv_usec = 0;
121         rc->rc_retries = INT_MAX;
122         rc->rc_privport = FALSE;
123         rc->rc_waitchan = "rpcrecv";
124         rc->rc_intr = 0;
125         rc->rc_connecting = FALSE;
126         rc->rc_closed = FALSE;
127         rc->rc_ucred = crdup(curthread->td_ucred);
128         rc->rc_client = NULL;
129
130         cl->cl_refs = 1;
131         cl->cl_ops = &clnt_reconnect_ops;
132         cl->cl_private = (caddr_t)(void *)rc;
133         cl->cl_auth = authnone_create();
134         cl->cl_tp = NULL;
135         cl->cl_netid = NULL;
136         return (cl);
137 }
138
139 static enum clnt_stat
140 clnt_reconnect_connect(CLIENT *cl)
141 {
142         struct thread *td = curthread;
143         struct rc_data *rc = (struct rc_data *)cl->cl_private;
144         struct socket *so;
145         enum clnt_stat stat;
146         int error;
147         int one = 1;
148         struct ucred *oldcred;
149         CLIENT *newclient = NULL;
150
151         mtx_lock(&rc->rc_lock);
152         while (rc->rc_connecting) {
153                 error = msleep(rc, &rc->rc_lock,
154                     rc->rc_intr ? PCATCH : 0, "rpcrecon", 0);
155                 if (error) {
156                         mtx_unlock(&rc->rc_lock);
157                         return (RPC_INTR);
158                 }
159         }
160         if (rc->rc_closed) {
161                 mtx_unlock(&rc->rc_lock);
162                 return (RPC_CANTSEND);
163         }
164         if (rc->rc_client) {
165                 mtx_unlock(&rc->rc_lock);
166                 return (RPC_SUCCESS);
167         }
168
169         /*
170          * My turn to attempt a connect. The rc_connecting variable
171          * serializes the following code sequence, so it is guaranteed
172          * that rc_client will still be NULL after it is re-locked below,
173          * since that is the only place it is set non-NULL.
174          */
175         rc->rc_connecting = TRUE;
176         mtx_unlock(&rc->rc_lock);
177
178         oldcred = td->td_ucred;
179         td->td_ucred = rc->rc_ucred;
180         so = __rpc_nconf2socket(rc->rc_nconf);
181         if (!so) {
182                 stat = rpc_createerr.cf_stat = RPC_TLIERROR;
183                 rpc_createerr.cf_error.re_errno = 0;
184                 td->td_ucred = oldcred;
185                 goto out;
186         }
187
188         if (rc->rc_privport)
189                 bindresvport(so, NULL);
190
191         if (rc->rc_nconf->nc_semantics == NC_TPI_CLTS)
192                 newclient = clnt_dg_create(so,
193                     (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
194                     rc->rc_sendsz, rc->rc_recvsz);
195         else
196                 newclient = clnt_vc_create(so,
197                     (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
198                     rc->rc_sendsz, rc->rc_recvsz, rc->rc_intr);
199         td->td_ucred = oldcred;
200
201         if (!newclient) {
202                 soclose(so);
203                 rc->rc_err = rpc_createerr.cf_error;
204                 stat = rpc_createerr.cf_stat;
205                 goto out;
206         }
207
208         CLNT_CONTROL(newclient, CLSET_FD_CLOSE, 0);
209         CLNT_CONTROL(newclient, CLSET_CONNECT, &one);
210         CLNT_CONTROL(newclient, CLSET_TIMEOUT, &rc->rc_timeout);
211         CLNT_CONTROL(newclient, CLSET_RETRY_TIMEOUT, &rc->rc_retry);
212         CLNT_CONTROL(newclient, CLSET_WAITCHAN, rc->rc_waitchan);
213         CLNT_CONTROL(newclient, CLSET_INTERRUPTIBLE, &rc->rc_intr);
214         stat = RPC_SUCCESS;
215
216 out:
217         mtx_lock(&rc->rc_lock);
218         KASSERT(rc->rc_client == NULL, ("rc_client not null"));
219         if (!rc->rc_closed) {
220                 rc->rc_client = newclient;
221                 newclient = NULL;
222         }
223         rc->rc_connecting = FALSE;
224         wakeup(rc);
225         mtx_unlock(&rc->rc_lock);
226
227         if (newclient) {
228                 /*
229                  * It has been closed, so discard the new client.
230                  * nb: clnt_[dg|vc]_close()/clnt_[dg|vc]_destroy() cannot
231                  * be called with the rc_lock mutex held, since they may
232                  * msleep() while holding a different mutex.
233                  */
234                 CLNT_CLOSE(newclient);
235                 CLNT_RELEASE(newclient);
236         }
237
238         return (stat);
239 }
240
241 static enum clnt_stat
242 clnt_reconnect_call(
243         CLIENT          *cl,            /* client handle */
244         struct rpc_callextra *ext,      /* call metadata */
245         rpcproc_t       proc,           /* procedure number */
246         struct mbuf     *args,          /* pointer to args */
247         struct mbuf     **resultsp,     /* pointer to results */
248         struct timeval  utimeout)
249 {
250         struct rc_data *rc = (struct rc_data *)cl->cl_private;
251         CLIENT *client;
252         enum clnt_stat stat;
253         int tries, error;
254
255         tries = 0;
256         do {
257                 mtx_lock(&rc->rc_lock);
258                 if (rc->rc_closed) {
259                         mtx_unlock(&rc->rc_lock);
260                         return (RPC_CANTSEND);
261                 }
262
263                 if (!rc->rc_client) {
264                         mtx_unlock(&rc->rc_lock);
265                         stat = clnt_reconnect_connect(cl);
266                         if (stat == RPC_SYSTEMERROR) {
267                                 error = tsleep(&fake_wchan,
268                                     rc->rc_intr ? PCATCH | PBDRY : 0, "rpccon",
269                                     hz);
270                                 if (error == EINTR || error == ERESTART)
271                                         return (RPC_INTR);
272                                 tries++;
273                                 if (tries >= rc->rc_retries)
274                                         return (stat);
275                                 continue;
276                         }
277                         if (stat != RPC_SUCCESS)
278                                 return (stat);
279                         mtx_lock(&rc->rc_lock);
280                 }
281
282                 if (!rc->rc_client) {
283                         mtx_unlock(&rc->rc_lock);
284                         stat = RPC_FAILED;
285                         continue;
286                 }
287                 CLNT_ACQUIRE(rc->rc_client);
288                 client = rc->rc_client;
289                 mtx_unlock(&rc->rc_lock);
290                 stat = CLNT_CALL_MBUF(client, ext, proc, args,
291                     resultsp, utimeout);
292
293                 if (stat != RPC_SUCCESS) {
294                         if (!ext)
295                                 CLNT_GETERR(client, &rc->rc_err);
296                 }
297
298                 if (stat == RPC_TIMEDOUT) {
299                         /*
300                          * Check for async send misfeature for NLM
301                          * protocol.
302                          */
303                         if ((rc->rc_timeout.tv_sec == 0
304                                 && rc->rc_timeout.tv_usec == 0)
305                             || (rc->rc_timeout.tv_sec == -1
306                                 && utimeout.tv_sec == 0
307                                 && utimeout.tv_usec == 0)) {
308                                 CLNT_RELEASE(client);
309                                 break;
310                         }
311                 }
312
313                 if (stat == RPC_TIMEDOUT || stat == RPC_CANTSEND
314                     || stat == RPC_CANTRECV) {
315                         tries++;
316                         if (tries >= rc->rc_retries) {
317                                 CLNT_RELEASE(client);
318                                 break;
319                         }
320
321                         if (ext && ext->rc_feedback)
322                                 ext->rc_feedback(FEEDBACK_RECONNECT, proc,
323                                     ext->rc_feedback_arg);
324
325                         mtx_lock(&rc->rc_lock);
326                         /*
327                          * Make sure that someone else hasn't already
328                          * reconnected by checking if rc_client has changed.
329                          * If not, we are done with the client and must
330                          * do CLNT_RELEASE(client) twice to dispose of it,
331                          * because there is both an initial refcnt and one
332                          * acquired by CLNT_ACQUIRE() above.
333                          */
334                         if (rc->rc_client == client) {
335                                 rc->rc_client = NULL;
336                                 mtx_unlock(&rc->rc_lock);
337                                 CLNT_RELEASE(client);
338                         } else {
339                                 mtx_unlock(&rc->rc_lock);
340                         }
341                         CLNT_RELEASE(client);
342                 } else {
343                         CLNT_RELEASE(client);
344                         break;
345                 }
346         } while (stat != RPC_SUCCESS);
347
348         KASSERT(stat != RPC_SUCCESS || *resultsp,
349             ("RPC_SUCCESS without reply"));
350
351         return (stat);
352 }
353
354 static void
355 clnt_reconnect_geterr(CLIENT *cl, struct rpc_err *errp)
356 {
357         struct rc_data *rc = (struct rc_data *)cl->cl_private;
358
359         *errp = rc->rc_err;
360 }
361
362 /*
363  * Since this function requires that rc_client be valid, it can
364  * only be called when that is guaranteed to be the case.
365  */
366 static bool_t
367 clnt_reconnect_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
368 {
369         struct rc_data *rc = (struct rc_data *)cl->cl_private;
370
371         return (CLNT_FREERES(rc->rc_client, xdr_res, res_ptr));
372 }
373
374 /*ARGSUSED*/
375 static void
376 clnt_reconnect_abort(CLIENT *h)
377 {
378 }
379
380 /*
381  * CLNT_CONTROL() on the client returned by clnt_reconnect_create() must
382  * always be called before CLNT_CALL_MBUF() by a single thread only.
383  */
384 static bool_t
385 clnt_reconnect_control(CLIENT *cl, u_int request, void *info)
386 {
387         struct rc_data *rc = (struct rc_data *)cl->cl_private;
388
389         if (info == NULL) {
390                 return (FALSE);
391         }
392         switch (request) {
393         case CLSET_TIMEOUT:
394                 rc->rc_timeout = *(struct timeval *)info;
395                 if (rc->rc_client)
396                         CLNT_CONTROL(rc->rc_client, request, info);
397                 break;
398
399         case CLGET_TIMEOUT:
400                 *(struct timeval *)info = rc->rc_timeout;
401                 break;
402
403         case CLSET_RETRY_TIMEOUT:
404                 rc->rc_retry = *(struct timeval *)info;
405                 if (rc->rc_client)
406                         CLNT_CONTROL(rc->rc_client, request, info);
407                 break;
408
409         case CLGET_RETRY_TIMEOUT:
410                 *(struct timeval *)info = rc->rc_retry;
411                 break;
412
413         case CLGET_VERS:
414                 *(uint32_t *)info = rc->rc_vers;
415                 break;
416
417         case CLSET_VERS:
418                 rc->rc_vers = *(uint32_t *) info;
419                 if (rc->rc_client)
420                         CLNT_CONTROL(rc->rc_client, CLSET_VERS, info);
421                 break;
422
423         case CLGET_PROG:
424                 *(uint32_t *)info = rc->rc_prog;
425                 break;
426
427         case CLSET_PROG:
428                 rc->rc_prog = *(uint32_t *) info;
429                 if (rc->rc_client)
430                         CLNT_CONTROL(rc->rc_client, request, info);
431                 break;
432
433         case CLSET_WAITCHAN:
434                 rc->rc_waitchan = (char *)info;
435                 if (rc->rc_client)
436                         CLNT_CONTROL(rc->rc_client, request, info);
437                 break;
438
439         case CLGET_WAITCHAN:
440                 *(const char **) info = rc->rc_waitchan;
441                 break;
442
443         case CLSET_INTERRUPTIBLE:
444                 rc->rc_intr = *(int *) info;
445                 if (rc->rc_client)
446                         CLNT_CONTROL(rc->rc_client, request, info);
447                 break;
448
449         case CLGET_INTERRUPTIBLE:
450                 *(int *) info = rc->rc_intr;
451                 break;
452
453         case CLSET_RETRIES:
454                 rc->rc_retries = *(int *) info;
455                 break;
456
457         case CLGET_RETRIES:
458                 *(int *) info = rc->rc_retries;
459                 break;
460
461         case CLSET_PRIVPORT:
462                 rc->rc_privport = *(int *) info;
463                 break;
464
465         case CLGET_PRIVPORT:
466                 *(int *) info = rc->rc_privport;
467                 break;
468
469         default:
470                 return (FALSE);
471         }
472
473         return (TRUE);
474 }
475
476 static void
477 clnt_reconnect_close(CLIENT *cl)
478 {
479         struct rc_data *rc = (struct rc_data *)cl->cl_private;
480         CLIENT *client;
481
482         mtx_lock(&rc->rc_lock);
483
484         if (rc->rc_closed) {
485                 mtx_unlock(&rc->rc_lock);
486                 return;
487         }
488
489         rc->rc_closed = TRUE;
490         client = rc->rc_client;
491         rc->rc_client = NULL;
492
493         mtx_unlock(&rc->rc_lock);
494
495         if (client) {
496                 CLNT_CLOSE(client);
497                 CLNT_RELEASE(client);
498         }
499 }
500
501 static void
502 clnt_reconnect_destroy(CLIENT *cl)
503 {
504         struct rc_data *rc = (struct rc_data *)cl->cl_private;
505
506         if (rc->rc_client)
507                 CLNT_DESTROY(rc->rc_client);
508         crfree(rc->rc_ucred);
509         mtx_destroy(&rc->rc_lock);
510         mem_free(rc, sizeof(*rc));
511         mem_free(cl, sizeof (CLIENT));
512 }