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 } 513