1/* $NetBSD: svc_dg.c,v 1.12 2008/04/25 17:44:44 christos Exp $ */ 2 3/* 4 * Copyright (c) 2010, Oracle America, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above 13 * copyright notice, this list of conditions and the following 14 * disclaimer in the documentation and/or other materials 15 * provided with the distribution. 16 * * Neither the name of the "Oracle America, Inc." nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34/* 35 * Copyright (c) 1986-1991 by Sun Microsystems Inc. 36 */ 37 38/* #ident "@(#)svc_dg.c 1.17 94/04/24 SMI" */ 39 40 41/* 42 * svc_dg.c, Server side for connectionless RPC. 43 * 44 * Does some caching in the hopes of achieving execute-at-most-once semantics. 45 */ 46 47#include <sys/cdefs.h> 48#if defined(LIBC_SCCS) && !defined(lint) 49__RCSID("$NetBSD: svc_dg.c,v 1.12 2008/04/25 17:44:44 christos Exp $"); 50#endif 51 52#include "namespace.h" 53#include "reentrant.h" 54#include <sys/types.h> 55#include <sys/socket.h> 56#include <rpc/rpc.h> 57#include <assert.h> 58#include <errno.h> 59#include <unistd.h> 60#include <stdio.h> 61#include <stdlib.h> 62#include <string.h> 63#ifdef RPC_CACHE_DEBUG 64#include <netconfig.h> 65#include <netdir.h> 66#endif 67#include <err.h> 68 69#include "rpc_internal.h" 70#include "svc_dg.h" 71 72#define su_data(xprt) ((struct svc_dg_data *)(xprt->xp_p2)) 73#define rpc_buffer(xprt) ((xprt)->xp_p1) 74 75#ifdef __weak_alias 76__weak_alias(svc_dg_create,_svc_dg_create) 77#endif 78 79#ifndef MAX 80#define MAX(a, b) (((a) > (b)) ? (a) : (b)) 81#endif 82 83static void svc_dg_ops __P((SVCXPRT *)); 84static enum xprt_stat svc_dg_stat __P((SVCXPRT *)); 85static bool_t svc_dg_recv __P((SVCXPRT *, struct rpc_msg *)); 86static bool_t svc_dg_reply __P((SVCXPRT *, struct rpc_msg *)); 87static bool_t svc_dg_getargs __P((SVCXPRT *, xdrproc_t, caddr_t)); 88static bool_t svc_dg_freeargs __P((SVCXPRT *, xdrproc_t, caddr_t)); 89static void svc_dg_destroy __P((SVCXPRT *)); 90static bool_t svc_dg_control __P((SVCXPRT *, const u_int, void *)); 91static int cache_get __P((SVCXPRT *, struct rpc_msg *, char **, size_t *)); 92static void cache_set __P((SVCXPRT *, size_t)); 93 94/* 95 * Usage: 96 * xprt = svc_dg_create(sock, sendsize, recvsize); 97 * Does other connectionless specific initializations. 98 * Once *xprt is initialized, it is registered. 99 * see (svc.h, xprt_register). If recvsize or sendsize are 0 suitable 100 * system defaults are chosen. 101 * The routines returns NULL if a problem occurred. 102 */ 103static const char svc_dg_str[] = "svc_dg_create: %s"; 104static const char svc_dg_err1[] = "could not get transport information"; 105static const char svc_dg_err2[] = " transport does not support data transfer"; 106static const char __no_mem_str[] = "out of memory"; 107 108SVCXPRT * 109svc_dg_create(fd, sendsize, recvsize) 110 int fd; 111 u_int sendsize; 112 u_int recvsize; 113{ 114 SVCXPRT *xprt; 115 struct svc_dg_data *su = NULL; 116 struct __rpc_sockinfo si; 117 struct sockaddr_storage ss; 118 socklen_t slen; 119 120 if (!__rpc_fd2sockinfo(fd, &si)) { 121 warnx(svc_dg_str, svc_dg_err1); 122 return (NULL); 123 } 124 /* 125 * Find the receive and the send size 126 */ 127 sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsize); 128 recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsize); 129 if ((sendsize == 0) || (recvsize == 0)) { 130 warnx(svc_dg_str, svc_dg_err2); 131 return (NULL); 132 } 133 134 xprt = mem_alloc(sizeof (SVCXPRT)); 135 if (xprt == NULL) 136 goto freedata; 137 memset(xprt, 0, sizeof (SVCXPRT)); 138 139 su = mem_alloc(sizeof (*su)); 140 if (su == NULL) 141 goto freedata; 142 su->su_iosz = ((MAX(sendsize, recvsize) + 3) / 4) * 4; 143 if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL) 144 goto freedata; 145 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, 146 XDR_DECODE); 147 su->su_cache = NULL; 148 xprt->xp_fd = fd; 149 xprt->xp_p2 = (caddr_t)(void *)su; 150 xprt->xp_verf.oa_base = su->su_verfbody; 151 svc_dg_ops(xprt); 152 xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage); 153 154 slen = sizeof ss; 155 if (getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) 156 goto freedata; 157 xprt->xp_ltaddr.buf = mem_alloc(sizeof (struct sockaddr_storage)); 158 xprt->xp_ltaddr.maxlen = sizeof (struct sockaddr_storage); 159 xprt->xp_ltaddr.len = slen; 160 memcpy(xprt->xp_ltaddr.buf, &ss, slen); 161 162 xprt_register(xprt); 163 return (xprt); 164freedata: 165 (void) warnx(svc_dg_str, __no_mem_str); 166 if (xprt) { 167 if (su) 168 (void) mem_free(su, sizeof (*su)); 169 (void) mem_free(xprt, sizeof (SVCXPRT)); 170 } 171 return (NULL); 172} 173 174/*ARGSUSED*/ 175static enum xprt_stat 176svc_dg_stat(xprt) 177 SVCXPRT *xprt; 178{ 179 return (XPRT_IDLE); 180} 181 182static bool_t 183svc_dg_recv(xprt, msg) 184 SVCXPRT *xprt; 185 struct rpc_msg *msg; 186{ 187 struct svc_dg_data *su; 188 XDR *xdrs; 189 char *reply; 190 struct sockaddr_storage ss; 191 socklen_t alen; 192 size_t replylen; 193 ssize_t rlen; 194 195 _DIAGASSERT(xprt != NULL); 196 _DIAGASSERT(msg != NULL); 197 198 su = su_data(xprt); 199 xdrs = &(su->su_xdrs); 200 201again: 202 alen = sizeof (struct sockaddr_storage); 203 rlen = recvfrom(xprt->xp_fd, rpc_buffer(xprt), su->su_iosz, 0, 204 (struct sockaddr *)(void *)&ss, &alen); 205 if (rlen == -1 && errno == EINTR) 206 goto again; 207 if (rlen == -1 || (rlen < (ssize_t)(4 * sizeof (u_int32_t)))) 208 return (FALSE); 209 if (xprt->xp_rtaddr.len < alen) { 210 if (xprt->xp_rtaddr.len != 0) 211 mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.len); 212 xprt->xp_rtaddr.buf = mem_alloc(alen); 213 xprt->xp_rtaddr.len = alen; 214 } 215 memcpy(xprt->xp_rtaddr.buf, &ss, alen); 216#ifdef PORTMAP 217 if (ss.ss_family == AF_INET) { 218 xprt->xp_raddr = *(struct sockaddr_in *)xprt->xp_rtaddr.buf; 219 xprt->xp_addrlen = sizeof (struct sockaddr_in); 220 } 221#endif 222 xdrs->x_op = XDR_DECODE; 223 XDR_SETPOS(xdrs, 0); 224 if (! xdr_callmsg(xdrs, msg)) { 225 return (FALSE); 226 } 227 su->su_xid = msg->rm_xid; 228 if (su->su_cache != NULL) { 229 if (cache_get(xprt, msg, &reply, &replylen)) { 230 (void)sendto(xprt->xp_fd, reply, replylen, 0, 231 (struct sockaddr *)(void *)&ss, alen); 232 return (FALSE); 233 } 234 } 235 return (TRUE); 236} 237 238static bool_t 239svc_dg_reply(xprt, msg) 240 SVCXPRT *xprt; 241 struct rpc_msg *msg; 242{ 243 struct svc_dg_data *su; 244 XDR *xdrs; 245 bool_t stat = FALSE; 246 size_t slen; 247 248 _DIAGASSERT(xprt != NULL); 249 _DIAGASSERT(msg != NULL); 250 251 su = su_data(xprt); 252 xdrs = &(su->su_xdrs); 253 254 xdrs->x_op = XDR_ENCODE; 255 XDR_SETPOS(xdrs, 0); 256 msg->rm_xid = su->su_xid; 257 if (xdr_replymsg(xdrs, msg)) { 258 slen = XDR_GETPOS(xdrs); 259 if (sendto(xprt->xp_fd, rpc_buffer(xprt), slen, 0, 260 (struct sockaddr *)xprt->xp_rtaddr.buf, 261 (socklen_t)xprt->xp_rtaddr.len) == (ssize_t) slen) { 262 stat = TRUE; 263 if (su->su_cache) 264 cache_set(xprt, slen); 265 } 266 } 267 return (stat); 268} 269 270static bool_t 271svc_dg_getargs(xprt, xdr_args, args_ptr) 272 SVCXPRT *xprt; 273 xdrproc_t xdr_args; 274 caddr_t args_ptr; 275{ 276 return (*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr); 277} 278 279static bool_t 280svc_dg_freeargs(xprt, xdr_args, args_ptr) 281 SVCXPRT *xprt; 282 xdrproc_t xdr_args; 283 caddr_t args_ptr; 284{ 285 XDR *xdrs; 286 287 _DIAGASSERT(xprt != NULL); 288 289 xdrs = &(su_data(xprt)->su_xdrs); 290 xdrs->x_op = XDR_FREE; 291 return (*xdr_args)(xdrs, args_ptr); 292} 293 294static void 295svc_dg_destroy(xprt) 296 SVCXPRT *xprt; 297{ 298 struct svc_dg_data *su; 299 300 _DIAGASSERT(xprt != NULL); 301 302 su = su_data(xprt); 303 304 xprt_unregister(xprt); 305 if (xprt->xp_fd != -1) 306 (void)close(xprt->xp_fd); 307 XDR_DESTROY(&(su->su_xdrs)); 308 (void) mem_free(rpc_buffer(xprt), su->su_iosz); 309 (void) mem_free(su, sizeof (*su)); 310 if (xprt->xp_rtaddr.buf) 311 (void) mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen); 312 if (xprt->xp_ltaddr.buf) 313 (void) mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen); 314 if (xprt->xp_tp) 315 (void) free(xprt->xp_tp); 316 (void) mem_free(xprt, sizeof (SVCXPRT)); 317} 318 319static bool_t 320/*ARGSUSED*/ 321svc_dg_control(xprt, rq, in) 322 SVCXPRT *xprt; 323 const u_int rq; 324 void *in; 325{ 326 return (FALSE); 327} 328 329static void 330svc_dg_ops(xprt) 331 SVCXPRT *xprt; 332{ 333 static struct xp_ops ops; 334 static struct xp_ops2 ops2; 335#ifdef _REENTRANT 336 extern mutex_t ops_lock; 337#endif 338 339 _DIAGASSERT(xprt != NULL); 340 341/* VARIABLES PROTECTED BY ops_lock: ops */ 342 343 mutex_lock(&ops_lock); 344 if (ops.xp_recv == NULL) { 345 ops.xp_recv = svc_dg_recv; 346 ops.xp_stat = svc_dg_stat; 347 ops.xp_getargs = svc_dg_getargs; 348 ops.xp_reply = svc_dg_reply; 349 ops.xp_freeargs = svc_dg_freeargs; 350 ops.xp_destroy = svc_dg_destroy; 351 ops2.xp_control = svc_dg_control; 352 } 353 xprt->xp_ops = &ops; 354 xprt->xp_ops2 = &ops2; 355 mutex_unlock(&ops_lock); 356} 357 358/* The CACHING COMPONENT */ 359 360/* 361 * Could have been a separate file, but some part of it depends upon the 362 * private structure of the client handle. 363 * 364 * Fifo cache for cl server 365 * Copies pointers to reply buffers into fifo cache 366 * Buffers are sent again if retransmissions are detected. 367 */ 368 369#define SPARSENESS 4 /* 75% sparse */ 370 371#define ALLOC(type, size) \ 372 mem_alloc((sizeof (type) * (size))) 373 374#define MEMZERO(addr, type, size) \ 375 (void) memset((void *) (addr), 0, sizeof (type) * (int) (size)) 376 377#define FREE(addr, type, size) \ 378 mem_free((addr), (sizeof (type) * (size))) 379 380/* 381 * An entry in the cache 382 */ 383typedef struct cache_node *cache_ptr; 384struct cache_node { 385 /* 386 * Index into cache is xid, proc, vers, prog and address 387 */ 388 u_int32_t cache_xid; 389 rpcproc_t cache_proc; 390 rpcvers_t cache_vers; 391 rpcprog_t cache_prog; 392 struct netbuf cache_addr; 393 /* 394 * The cached reply and length 395 */ 396 char *cache_reply; 397 size_t cache_replylen; 398 /* 399 * Next node on the list, if there is a collision 400 */ 401 cache_ptr cache_next; 402}; 403 404/* 405 * The entire cache 406 */ 407struct cl_cache { 408 u_int uc_size; /* size of cache */ 409 cache_ptr *uc_entries; /* hash table of entries in cache */ 410 cache_ptr *uc_fifo; /* fifo list of entries in cache */ 411 u_int uc_nextvictim; /* points to next victim in fifo list */ 412 rpcprog_t uc_prog; /* saved program number */ 413 rpcvers_t uc_vers; /* saved version number */ 414 rpcproc_t uc_proc; /* saved procedure number */ 415}; 416 417 418/* 419 * the hashing function 420 */ 421#define CACHE_LOC(transp, xid) \ 422 (xid % (SPARSENESS * ((struct cl_cache *) \ 423 su_data(transp)->su_cache)->uc_size)) 424 425#ifdef _REENTRANT 426extern mutex_t dupreq_lock; 427#endif 428 429/* 430 * Enable use of the cache. Returns 1 on success, 0 on failure. 431 * Note: there is no disable. 432 */ 433static const char cache_enable_str[] = "svc_enablecache: %s %s"; 434static const char alloc_err[] = "could not allocate cache "; 435static const char enable_err[] = "cache already enabled"; 436 437int 438svc_dg_enablecache(transp, size) 439 SVCXPRT *transp; 440 u_int size; 441{ 442 struct svc_dg_data *su; 443 struct cl_cache *uc; 444 445 _DIAGASSERT(transp != NULL); 446 447 su = su_data(transp); 448 449 mutex_lock(&dupreq_lock); 450 if (su->su_cache != NULL) { 451 (void) warnx(cache_enable_str, enable_err, " "); 452 mutex_unlock(&dupreq_lock); 453 return (0); 454 } 455 uc = ALLOC(struct cl_cache, 1); 456 if (uc == NULL) { 457 warnx(cache_enable_str, alloc_err, " "); 458 mutex_unlock(&dupreq_lock); 459 return (0); 460 } 461 uc->uc_size = size; 462 uc->uc_nextvictim = 0; 463 uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS); 464 if (uc->uc_entries == NULL) { 465 warnx(cache_enable_str, alloc_err, "data"); 466 FREE(uc, struct cl_cache, 1); 467 mutex_unlock(&dupreq_lock); 468 return (0); 469 } 470 MEMZERO(uc->uc_entries, cache_ptr, size * SPARSENESS); 471 uc->uc_fifo = ALLOC(cache_ptr, size); 472 if (uc->uc_fifo == NULL) { 473 warnx(cache_enable_str, alloc_err, "fifo"); 474 FREE(uc->uc_entries, cache_ptr, size * SPARSENESS); 475 FREE(uc, struct cl_cache, 1); 476 mutex_unlock(&dupreq_lock); 477 return (0); 478 } 479 MEMZERO(uc->uc_fifo, cache_ptr, size); 480 su->su_cache = (char *)(void *)uc; 481 mutex_unlock(&dupreq_lock); 482 return (1); 483} 484 485/* 486 * Set an entry in the cache. It assumes that the uc entry is set from 487 * the earlier call to cache_get() for the same procedure. This will always 488 * happen because cache_get() is calle by svc_dg_recv and cache_set() is called 489 * by svc_dg_reply(). All this hoopla because the right RPC parameters are 490 * not available at svc_dg_reply time. 491 */ 492 493static const char cache_set_str[] = "cache_set: %s"; 494static const char cache_set_err1[] = "victim not found"; 495static const char cache_set_err2[] = "victim alloc failed"; 496static const char cache_set_err3[] = "could not allocate new rpc buffer"; 497 498static void 499cache_set(xprt, replylen) 500 SVCXPRT *xprt; 501 size_t replylen; 502{ 503 cache_ptr victim; 504 cache_ptr *vicp; 505 struct svc_dg_data *su; 506 struct cl_cache *uc; 507 u_int loc; 508 char *newbuf; 509#ifdef RPC_CACHE_DEBUG 510 struct netconfig *nconf; 511 char *uaddr; 512#endif 513 514 _DIAGASSERT(xprt != NULL); 515 516 su = su_data(xprt); 517 uc = (struct cl_cache *) su->su_cache; 518 519 mutex_lock(&dupreq_lock); 520 /* 521 * Find space for the new entry, either by 522 * reusing an old entry, or by mallocing a new one 523 */ 524 victim = uc->uc_fifo[uc->uc_nextvictim]; 525 if (victim != NULL) { 526 loc = CACHE_LOC(xprt, victim->cache_xid); 527 for (vicp = &uc->uc_entries[loc]; 528 *vicp != NULL && *vicp != victim; 529 vicp = &(*vicp)->cache_next) 530 ; 531 if (*vicp == NULL) { 532 warnx(cache_set_str, cache_set_err1); 533 mutex_unlock(&dupreq_lock); 534 return; 535 } 536 *vicp = victim->cache_next; /* remove from cache */ 537 newbuf = victim->cache_reply; 538 } else { 539 victim = ALLOC(struct cache_node, 1); 540 if (victim == NULL) { 541 warnx(cache_set_str, cache_set_err2); 542 mutex_unlock(&dupreq_lock); 543 return; 544 } 545 newbuf = mem_alloc(su->su_iosz); 546 if (newbuf == NULL) { 547 warnx(cache_set_str, cache_set_err3); 548 FREE(victim, struct cache_node, 1); 549 mutex_unlock(&dupreq_lock); 550 return; 551 } 552 } 553 554 /* 555 * Store it away 556 */ 557#ifdef RPC_CACHE_DEBUG 558 if (nconf = getnetconfigent(xprt->xp_netid)) { 559 uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr); 560 freenetconfigent(nconf); 561 printf( 562 "cache set for xid= %x prog=%d vers=%d proc=%d for rmtaddr=%s\n", 563 su->su_xid, uc->uc_prog, uc->uc_vers, 564 uc->uc_proc, uaddr); 565 free(uaddr); 566 } 567#endif 568 victim->cache_replylen = replylen; 569 victim->cache_reply = rpc_buffer(xprt); 570 rpc_buffer(xprt) = newbuf; 571 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), 572 su->su_iosz, XDR_ENCODE); 573 victim->cache_xid = su->su_xid; 574 victim->cache_proc = uc->uc_proc; 575 victim->cache_vers = uc->uc_vers; 576 victim->cache_prog = uc->uc_prog; 577 victim->cache_addr = xprt->xp_rtaddr; 578 victim->cache_addr.buf = ALLOC(char, xprt->xp_rtaddr.len); 579 (void) memcpy(victim->cache_addr.buf, xprt->xp_rtaddr.buf, 580 (size_t)xprt->xp_rtaddr.len); 581 loc = CACHE_LOC(xprt, victim->cache_xid); 582 victim->cache_next = uc->uc_entries[loc]; 583 uc->uc_entries[loc] = victim; 584 uc->uc_fifo[uc->uc_nextvictim++] = victim; 585 uc->uc_nextvictim %= uc->uc_size; 586 mutex_unlock(&dupreq_lock); 587} 588 589/* 590 * Try to get an entry from the cache 591 * return 1 if found, 0 if not found and set the stage for cache_set() 592 */ 593static int 594cache_get(xprt, msg, replyp, replylenp) 595 SVCXPRT *xprt; 596 struct rpc_msg *msg; 597 char **replyp; 598 size_t *replylenp; 599{ 600 u_int loc; 601 cache_ptr ent; 602 struct svc_dg_data *su; 603 struct cl_cache *uc; 604#ifdef RPC_CACHE_DEBUG 605 struct netconfig *nconf; 606 char *uaddr; 607#endif 608 609 _DIAGASSERT(xprt != NULL); 610 _DIAGASSERT(msg != NULL); 611 _DIAGASSERT(replyp != NULL); 612 _DIAGASSERT(replylenp != NULL); 613 614 su = su_data(xprt); 615 uc = (struct cl_cache *) su->su_cache; 616 617 mutex_lock(&dupreq_lock); 618 loc = CACHE_LOC(xprt, su->su_xid); 619 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { 620 if (ent->cache_xid == su->su_xid && 621 ent->cache_proc == msg->rm_call.cb_proc && 622 ent->cache_vers == msg->rm_call.cb_vers && 623 ent->cache_prog == msg->rm_call.cb_prog && 624 ent->cache_addr.len == xprt->xp_rtaddr.len && 625 (memcmp(ent->cache_addr.buf, xprt->xp_rtaddr.buf, 626 xprt->xp_rtaddr.len) == 0)) { 627#ifdef RPC_CACHE_DEBUG 628 if (nconf = getnetconfigent(xprt->xp_netid)) { 629 uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr); 630 freenetconfigent(nconf); 631 printf( 632 "cache entry found for xid=%x prog=%d vers=%d proc=%d for rmtaddr=%s\n", 633 su->su_xid, msg->rm_call.cb_prog, 634 msg->rm_call.cb_vers, 635 msg->rm_call.cb_proc, uaddr); 636 free(uaddr); 637 } 638#endif 639 *replyp = ent->cache_reply; 640 *replylenp = ent->cache_replylen; 641 mutex_unlock(&dupreq_lock); 642 return (1); 643 } 644 } 645 /* 646 * Failed to find entry 647 * Remember a few things so we can do a set later 648 */ 649 uc->uc_proc = msg->rm_call.cb_proc; 650 uc->uc_vers = msg->rm_call.cb_vers; 651 uc->uc_prog = msg->rm_call.cb_prog; 652 mutex_unlock(&dupreq_lock); 653 return (0); 654} 655