1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * Portions of this source code were derived from Berkeley 4.3 BSD 31 * under license from the Regents of the University of California. 32 */ 33 34 #pragma ident "%Z%%M% %I% %E% SMI" 35 36 /* 37 * svc_clts.c 38 * Server side for RPC in the kernel. 39 * 40 */ 41 42 #include <sys/param.h> 43 #include <sys/types.h> 44 #include <sys/sysmacros.h> 45 #include <sys/file.h> 46 #include <sys/stream.h> 47 #include <sys/strsubr.h> 48 #include <sys/tihdr.h> 49 #include <sys/tiuser.h> 50 #include <sys/t_kuser.h> 51 #include <sys/fcntl.h> 52 #include <sys/errno.h> 53 #include <sys/kmem.h> 54 #include <sys/systm.h> 55 #include <sys/cmn_err.h> 56 #include <sys/kstat.h> 57 #include <sys/vtrace.h> 58 #include <sys/debug.h> 59 60 #include <rpc/types.h> 61 #include <rpc/xdr.h> 62 #include <rpc/auth.h> 63 #include <rpc/clnt.h> 64 #include <rpc/rpc_msg.h> 65 #include <rpc/svc.h> 66 #include <inet/ip.h> 67 68 /* 69 * Routines exported through ops vector. 70 */ 71 static bool_t svc_clts_krecv(SVCXPRT *, mblk_t *, struct rpc_msg *); 72 static bool_t svc_clts_ksend(SVCXPRT *, struct rpc_msg *); 73 static bool_t svc_clts_kgetargs(SVCXPRT *, xdrproc_t, caddr_t); 74 static bool_t svc_clts_kfreeargs(SVCXPRT *, xdrproc_t, caddr_t); 75 static void svc_clts_kdestroy(SVCMASTERXPRT *); 76 static int svc_clts_kdup(struct svc_req *, caddr_t, int, 77 struct dupreq **, bool_t *); 78 static void svc_clts_kdupdone(struct dupreq *, caddr_t, 79 void (*)(), int, int); 80 static int32_t *svc_clts_kgetres(SVCXPRT *, int); 81 static void svc_clts_kclone_destroy(SVCXPRT *); 82 static void svc_clts_kfreeres(SVCXPRT *); 83 static void svc_clts_kstart(SVCMASTERXPRT *); 84 85 /* 86 * Server transport operations vector. 87 */ 88 struct svc_ops svc_clts_op = { 89 svc_clts_krecv, /* Get requests */ 90 svc_clts_kgetargs, /* Deserialize arguments */ 91 svc_clts_ksend, /* Send reply */ 92 svc_clts_kfreeargs, /* Free argument data space */ 93 svc_clts_kdestroy, /* Destroy transport handle */ 94 svc_clts_kdup, /* Check entry in dup req cache */ 95 svc_clts_kdupdone, /* Mark entry in dup req cache as done */ 96 svc_clts_kgetres, /* Get pointer to response buffer */ 97 svc_clts_kfreeres, /* Destroy pre-serialized response header */ 98 svc_clts_kclone_destroy, /* Destroy a clone xprt */ 99 svc_clts_kstart /* Tell `ready-to-receive' to rpcmod */ 100 }; 101 102 /* 103 * Transport private data. 104 * Kept in xprt->xp_p2buf. 105 */ 106 struct udp_data { 107 mblk_t *ud_resp; /* buffer for response */ 108 mblk_t *ud_inmp; /* mblk chain of request */ 109 }; 110 111 #define UD_MAXSIZE 8800 112 #define UD_INITSIZE 2048 113 114 /* 115 * Connectionless server statistics 116 */ 117 static const struct rpc_clts_server { 118 kstat_named_t rscalls; 119 kstat_named_t rsbadcalls; 120 kstat_named_t rsnullrecv; 121 kstat_named_t rsbadlen; 122 kstat_named_t rsxdrcall; 123 kstat_named_t rsdupchecks; 124 kstat_named_t rsdupreqs; 125 } clts_rsstat_tmpl = { 126 { "calls", KSTAT_DATA_UINT64 }, 127 { "badcalls", KSTAT_DATA_UINT64 }, 128 { "nullrecv", KSTAT_DATA_UINT64 }, 129 { "badlen", KSTAT_DATA_UINT64 }, 130 { "xdrcall", KSTAT_DATA_UINT64 }, 131 { "dupchecks", KSTAT_DATA_UINT64 }, 132 { "dupreqs", KSTAT_DATA_UINT64 } 133 }; 134 135 static uint_t clts_rsstat_ndata = 136 sizeof (clts_rsstat_tmpl) / sizeof (kstat_named_t); 137 138 #define CLONE2STATS(clone_xprt) \ 139 (struct rpc_clts_server *)(clone_xprt)->xp_master->xp_p2 140 141 #define RSSTAT_INCR(stats, x) \ 142 atomic_add_64(&(stats)->x.value.ui64, 1) 143 144 /* 145 * Create a transport record. 146 * The transport record, output buffer, and private data structure 147 * are allocated. The output buffer is serialized into using xdrmem. 148 * There is one transport record per user process which implements a 149 * set of services. 150 */ 151 /* ARGSUSED */ 152 int 153 svc_clts_kcreate(file_t *fp, uint_t sendsz, struct T_info_ack *tinfo, 154 SVCMASTERXPRT **nxprt) 155 { 156 SVCMASTERXPRT *xprt; 157 struct rpcstat *rpcstat; 158 159 if (nxprt == NULL) 160 return (EINVAL); 161 162 rpcstat = zone_getspecific(rpcstat_zone_key, curproc->p_zone); 163 ASSERT(rpcstat != NULL); 164 165 xprt = kmem_zalloc(sizeof (*xprt), KM_SLEEP); 166 xprt->xp_lcladdr.buf = kmem_zalloc(sizeof (sin6_t), KM_SLEEP); 167 xprt->xp_p2 = (caddr_t)rpcstat->rpc_clts_server; 168 xprt->xp_ops = &svc_clts_op; 169 xprt->xp_msg_size = tinfo->TSDU_size; 170 171 xprt->xp_rtaddr.buf = NULL; 172 xprt->xp_rtaddr.maxlen = tinfo->ADDR_size; 173 xprt->xp_rtaddr.len = 0; 174 175 *nxprt = xprt; 176 177 return (0); 178 } 179 180 /* 181 * Destroy a transport record. 182 * Frees the space allocated for a transport record. 183 */ 184 static void 185 svc_clts_kdestroy(SVCMASTERXPRT *xprt) 186 { 187 if (xprt->xp_netid) 188 kmem_free(xprt->xp_netid, strlen(xprt->xp_netid) + 1); 189 if (xprt->xp_addrmask.maxlen) 190 kmem_free(xprt->xp_addrmask.buf, xprt->xp_addrmask.maxlen); 191 192 mutex_destroy(&xprt->xp_req_lock); 193 mutex_destroy(&xprt->xp_thread_lock); 194 195 kmem_free(xprt->xp_lcladdr.buf, sizeof (sin6_t)); 196 kmem_free(xprt, sizeof (SVCMASTERXPRT)); 197 } 198 199 /* 200 * Transport-type specific part of svc_xprt_cleanup(). 201 * Frees the message buffer space allocated for a clone of a transport record 202 */ 203 static void 204 svc_clts_kclone_destroy(SVCXPRT *clone_xprt) 205 { 206 /* LINTED pointer alignment */ 207 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 208 209 if (ud->ud_resp) { 210 /* 211 * There should not be any left over results buffer. 212 */ 213 ASSERT(ud->ud_resp->b_cont == NULL); 214 215 /* 216 * Free the T_UNITDATA_{REQ/IND} that svc_clts_krecv 217 * saved. 218 */ 219 freeb(ud->ud_resp); 220 } 221 if (ud->ud_inmp) 222 freemsg(ud->ud_inmp); 223 } 224 225 /* 226 * svc_tli_kcreate() calls this function at the end to tell 227 * rpcmod that the transport is ready to receive requests. 228 */ 229 /* ARGSUSED */ 230 static void 231 svc_clts_kstart(SVCMASTERXPRT *xprt) 232 { 233 } 234 235 /* 236 * Receive rpc requests. 237 * Pulls a request in off the socket, checks if the packet is intact, 238 * and deserializes the call packet. 239 */ 240 static bool_t 241 svc_clts_krecv(SVCXPRT *clone_xprt, mblk_t *mp, struct rpc_msg *msg) 242 { 243 /* LINTED pointer alignment */ 244 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 245 XDR *xdrs = &clone_xprt->xp_xdrin; 246 struct rpc_clts_server *stats = CLONE2STATS(clone_xprt); 247 union T_primitives *pptr; 248 int hdrsz; 249 250 TRACE_0(TR_FAC_KRPC, TR_SVC_CLTS_KRECV_START, 251 "svc_clts_krecv_start:"); 252 253 RSSTAT_INCR(stats, rscalls); 254 255 /* 256 * The incoming request should start with an M_PROTO message. 257 */ 258 if (mp->b_datap->db_type != M_PROTO) { 259 goto bad; 260 } 261 262 /* 263 * The incoming request should be an T_UNITDTA_IND. There 264 * might be other messages coming up the stream, but we can 265 * ignore them. 266 */ 267 pptr = (union T_primitives *)mp->b_rptr; 268 if (pptr->type != T_UNITDATA_IND) { 269 goto bad; 270 } 271 /* 272 * Do some checking to make sure that the header at least looks okay. 273 */ 274 hdrsz = (int)(mp->b_wptr - mp->b_rptr); 275 if (hdrsz < TUNITDATAINDSZ || 276 hdrsz < (pptr->unitdata_ind.OPT_offset + 277 pptr->unitdata_ind.OPT_length) || 278 hdrsz < (pptr->unitdata_ind.SRC_offset + 279 pptr->unitdata_ind.SRC_length)) { 280 goto bad; 281 } 282 283 /* 284 * Make sure that the transport provided a usable address. 285 */ 286 if (pptr->unitdata_ind.SRC_length <= 0) { 287 goto bad; 288 } 289 /* 290 * Point the remote transport address in the service_transport 291 * handle at the address in the request. 292 */ 293 clone_xprt->xp_rtaddr.buf = (char *)mp->b_rptr + 294 pptr->unitdata_ind.SRC_offset; 295 clone_xprt->xp_rtaddr.len = pptr->unitdata_ind.SRC_length; 296 297 /* 298 * Copy the local transport address in the service_transport 299 * handle at the address in the request. We will have only 300 * the local IP address in options. 301 */ 302 if (pptr->unitdata_ind.OPT_length && pptr->unitdata_ind.OPT_offset) { 303 char *dstopt = (char *)mp->b_rptr + 304 pptr->unitdata_ind.OPT_offset; 305 struct T_opthdr *toh = (struct T_opthdr *)dstopt; 306 307 if (toh->level == IPPROTO_IPV6 && toh->status == 0 && 308 toh->name == IPV6_PKTINFO) { 309 struct in6_pktinfo *pkti; 310 311 dstopt += sizeof (struct T_opthdr); 312 pkti = (struct in6_pktinfo *)dstopt; 313 ((sin6_t *)(clone_xprt->xp_lcladdr.buf))->sin6_addr 314 = pkti->ipi6_addr; 315 } else if (toh->level == IPPROTO_IP && toh->status == 0 && 316 toh->name == IP_RECVDSTADDR) { 317 dstopt += sizeof (struct T_opthdr); 318 ((sin_t *)(clone_xprt->xp_lcladdr.buf))->sin_addr 319 = *(struct in_addr *)dstopt; 320 } 321 } 322 323 /* 324 * Save the first mblk which contains the T_unidata_ind in 325 * ud_resp. It will be used to generate the T_unitdata_req 326 * during the reply. 327 */ 328 if (ud->ud_resp) { 329 if (ud->ud_resp->b_cont != NULL) { 330 cmn_err(CE_WARN, "svc_clts_krecv: ud_resp %p, " 331 "b_cont %p", (void *)ud->ud_resp, 332 (void *)ud->ud_resp->b_cont); 333 } 334 freeb(ud->ud_resp); 335 } 336 ud->ud_resp = mp; 337 mp = mp->b_cont; 338 ud->ud_resp->b_cont = NULL; 339 340 xdrmblk_init(xdrs, mp, XDR_DECODE, 0); 341 342 TRACE_0(TR_FAC_KRPC, TR_XDR_CALLMSG_START, 343 "xdr_callmsg_start:"); 344 if (! xdr_callmsg(xdrs, msg)) { 345 TRACE_1(TR_FAC_KRPC, TR_XDR_CALLMSG_END, 346 "xdr_callmsg_end:(%S)", "bad"); 347 RSSTAT_INCR(stats, rsxdrcall); 348 goto bad; 349 } 350 TRACE_1(TR_FAC_KRPC, TR_XDR_CALLMSG_END, 351 "xdr_callmsg_end:(%S)", "good"); 352 353 clone_xprt->xp_xid = msg->rm_xid; 354 ud->ud_inmp = mp; 355 356 TRACE_1(TR_FAC_KRPC, TR_SVC_CLTS_KRECV_END, 357 "svc_clts_krecv_end:(%S)", "good"); 358 return (TRUE); 359 360 bad: 361 if (mp) 362 freemsg(mp); 363 if (ud->ud_resp) { 364 /* 365 * There should not be any left over results buffer. 366 */ 367 ASSERT(ud->ud_resp->b_cont == NULL); 368 freeb(ud->ud_resp); 369 ud->ud_resp = NULL; 370 } 371 372 RSSTAT_INCR(stats, rsbadcalls); 373 TRACE_1(TR_FAC_KRPC, TR_SVC_CLTS_KRECV_END, 374 "svc_clts_krecv_end:(%S)", "bad"); 375 return (FALSE); 376 } 377 378 /* 379 * Send rpc reply. 380 * Serialize the reply packet into the output buffer then 381 * call t_ksndudata to send it. 382 */ 383 static bool_t 384 svc_clts_ksend(SVCXPRT *clone_xprt, struct rpc_msg *msg) 385 { 386 /* LINTED pointer alignment */ 387 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 388 XDR *xdrs = &clone_xprt->xp_xdrout; 389 int stat = FALSE; 390 mblk_t *mp; 391 int msgsz; 392 struct T_unitdata_req *udreq; 393 xdrproc_t xdr_results; 394 caddr_t xdr_location; 395 bool_t has_args; 396 397 TRACE_0(TR_FAC_KRPC, TR_SVC_CLTS_KSEND_START, 398 "svc_clts_ksend_start:"); 399 400 ASSERT(ud->ud_resp != NULL); 401 402 /* 403 * If there is a result procedure specified in the reply message, 404 * it will be processed in the xdr_replymsg and SVCAUTH_WRAP. 405 * We need to make sure it won't be processed twice, so we null 406 * it for xdr_replymsg here. 407 */ 408 has_args = FALSE; 409 if (msg->rm_reply.rp_stat == MSG_ACCEPTED && 410 msg->rm_reply.rp_acpt.ar_stat == SUCCESS) { 411 if ((xdr_results = msg->acpted_rply.ar_results.proc) != NULL) { 412 has_args = TRUE; 413 xdr_location = msg->acpted_rply.ar_results.where; 414 msg->acpted_rply.ar_results.proc = xdr_void; 415 msg->acpted_rply.ar_results.where = NULL; 416 } 417 } 418 419 if (ud->ud_resp->b_cont == NULL) { 420 /* 421 * Allocate an initial mblk for the response data. 422 */ 423 while ((mp = allocb(UD_INITSIZE, BPRI_LO)) == NULL) { 424 if (strwaitbuf(UD_INITSIZE, BPRI_LO)) { 425 TRACE_1(TR_FAC_KRPC, TR_SVC_CLTS_KSEND_END, 426 "svc_clts_ksend_end:(%S)", "strwaitbuf"); 427 return (FALSE); 428 } 429 } 430 431 /* 432 * Initialize the XDR decode stream. Additional mblks 433 * will be allocated if necessary. They will be UD_MAXSIZE 434 * sized. 435 */ 436 xdrmblk_init(xdrs, mp, XDR_ENCODE, UD_MAXSIZE); 437 438 /* 439 * Leave some space for protocol headers. 440 */ 441 (void) XDR_SETPOS(xdrs, 512); 442 mp->b_rptr += 512; 443 444 msg->rm_xid = clone_xprt->xp_xid; 445 446 ud->ud_resp->b_cont = mp; 447 448 TRACE_0(TR_FAC_KRPC, TR_XDR_REPLYMSG_START, 449 "xdr_replymsg_start:"); 450 if (!(xdr_replymsg(xdrs, msg) && 451 (!has_args || SVCAUTH_WRAP(&clone_xprt->xp_auth, xdrs, 452 xdr_results, xdr_location)))) { 453 TRACE_1(TR_FAC_KRPC, TR_XDR_REPLYMSG_END, 454 "xdr_replymsg_end:(%S)", "bad"); 455 RPCLOG0(1, "xdr_replymsg/SVCAUTH_WRAP failed\n"); 456 goto out; 457 } 458 TRACE_1(TR_FAC_KRPC, TR_XDR_REPLYMSG_END, 459 "xdr_replymsg_end:(%S)", "good"); 460 461 } else if (!(xdr_replymsg_body(xdrs, msg) && 462 (!has_args || SVCAUTH_WRAP(&clone_xprt->xp_auth, xdrs, 463 xdr_results, xdr_location)))) { 464 RPCLOG0(1, "xdr_replymsg_body/SVCAUTH_WRAP failed\n"); 465 goto out; 466 } 467 468 msgsz = (int)xmsgsize(ud->ud_resp->b_cont); 469 470 if (msgsz <= 0 || (clone_xprt->xp_msg_size != -1 && 471 msgsz > clone_xprt->xp_msg_size)) { 472 #ifdef DEBUG 473 cmn_err(CE_NOTE, 474 "KRPC: server response message of %d bytes; transport limits are [0, %d]", 475 msgsz, clone_xprt->xp_msg_size); 476 #endif 477 goto out; 478 } 479 480 /* 481 * Construct the T_unitdata_req. We take advantage 482 * of the fact that T_unitdata_ind looks just like 483 * T_unitdata_req, except for the primitive type. 484 */ 485 udreq = (struct T_unitdata_req *)ud->ud_resp->b_rptr; 486 udreq->PRIM_type = T_UNITDATA_REQ; 487 488 put(clone_xprt->xp_wq, ud->ud_resp); 489 stat = TRUE; 490 ud->ud_resp = NULL; 491 492 out: 493 if (stat == FALSE) { 494 freemsg(ud->ud_resp); 495 ud->ud_resp = NULL; 496 } 497 498 /* 499 * This is completely disgusting. If public is set it is 500 * a pointer to a structure whose first field is the address 501 * of the function to free that structure and any related 502 * stuff. (see rrokfree in nfs_xdr.c). 503 */ 504 if (xdrs->x_public) { 505 /* LINTED pointer alignment */ 506 (**((int (**)())xdrs->x_public))(xdrs->x_public); 507 } 508 509 TRACE_1(TR_FAC_KRPC, TR_SVC_CLTS_KSEND_END, 510 "svc_clts_ksend_end:(%S)", "done"); 511 return (stat); 512 } 513 514 /* 515 * Deserialize arguments. 516 */ 517 static bool_t 518 svc_clts_kgetargs(SVCXPRT *clone_xprt, xdrproc_t xdr_args, 519 caddr_t args_ptr) 520 { 521 522 /* LINTED pointer alignment */ 523 return (SVCAUTH_UNWRAP(&clone_xprt->xp_auth, &clone_xprt->xp_xdrin, 524 xdr_args, args_ptr)); 525 526 } 527 528 static bool_t 529 svc_clts_kfreeargs(SVCXPRT *clone_xprt, xdrproc_t xdr_args, 530 caddr_t args_ptr) 531 { 532 /* LINTED pointer alignment */ 533 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 534 XDR *xdrs = &clone_xprt->xp_xdrin; 535 bool_t retval; 536 537 if (args_ptr) { 538 xdrs->x_op = XDR_FREE; 539 retval = (*xdr_args)(xdrs, args_ptr); 540 } else 541 retval = TRUE; 542 543 if (ud->ud_inmp) { 544 freemsg(ud->ud_inmp); 545 ud->ud_inmp = NULL; 546 } 547 548 return (retval); 549 } 550 551 static int32_t * 552 svc_clts_kgetres(SVCXPRT *clone_xprt, int size) 553 { 554 /* LINTED pointer alignment */ 555 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 556 XDR *xdrs = &clone_xprt->xp_xdrout; 557 mblk_t *mp; 558 int32_t *buf; 559 struct rpc_msg rply; 560 561 /* 562 * Allocate an initial mblk for the response data. 563 */ 564 while ((mp = allocb(UD_INITSIZE, BPRI_LO)) == NULL) { 565 if (strwaitbuf(UD_INITSIZE, BPRI_LO)) { 566 return (FALSE); 567 } 568 } 569 570 mp->b_cont = NULL; 571 572 /* 573 * Initialize the XDR decode stream. Additional mblks 574 * will be allocated if necessary. They will be UD_MAXSIZE 575 * sized. 576 */ 577 xdrmblk_init(xdrs, mp, XDR_ENCODE, UD_MAXSIZE); 578 579 /* 580 * Leave some space for protocol headers. 581 */ 582 (void) XDR_SETPOS(xdrs, 512); 583 mp->b_rptr += 512; 584 585 /* 586 * Assume a successful RPC since most of them are. 587 */ 588 rply.rm_xid = clone_xprt->xp_xid; 589 rply.rm_direction = REPLY; 590 rply.rm_reply.rp_stat = MSG_ACCEPTED; 591 rply.acpted_rply.ar_verf = clone_xprt->xp_verf; 592 rply.acpted_rply.ar_stat = SUCCESS; 593 594 if (!xdr_replymsg_hdr(xdrs, &rply)) { 595 freeb(mp); 596 return (NULL); 597 } 598 599 buf = XDR_INLINE(xdrs, size); 600 601 if (buf == NULL) 602 freeb(mp); 603 else 604 ud->ud_resp->b_cont = mp; 605 606 return (buf); 607 } 608 609 static void 610 svc_clts_kfreeres(SVCXPRT *clone_xprt) 611 { 612 /* LINTED pointer alignment */ 613 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 614 615 if (ud->ud_resp == NULL || ud->ud_resp->b_cont == NULL) 616 return; 617 618 /* 619 * SVC_FREERES() is called whenever the server decides not to 620 * send normal reply. Thus, we expect only one mblk to be allocated, 621 * because we have not attempted any XDR encoding. 622 * If we do any XDR encoding and we get an error, then SVC_REPLY() 623 * will freemsg(ud->ud_resp); 624 */ 625 ASSERT(ud->ud_resp->b_cont->b_cont == NULL); 626 freeb(ud->ud_resp->b_cont); 627 ud->ud_resp->b_cont = NULL; 628 } 629 630 /* 631 * the dup cacheing routines below provide a cache of non-failure 632 * transaction id's. rpc service routines can use this to detect 633 * retransmissions and re-send a non-failure response. 634 */ 635 636 /* 637 * MAXDUPREQS is the number of cached items. It should be adjusted 638 * to the service load so that there is likely to be a response entry 639 * when the first retransmission comes in. 640 */ 641 #define MAXDUPREQS 1024 642 643 /* 644 * This should be appropriately scaled to MAXDUPREQS. 645 */ 646 #define DRHASHSZ 257 647 648 #if ((DRHASHSZ & (DRHASHSZ - 1)) == 0) 649 #define XIDHASH(xid) ((xid) & (DRHASHSZ - 1)) 650 #else 651 #define XIDHASH(xid) ((xid) % DRHASHSZ) 652 #endif 653 #define DRHASH(dr) XIDHASH((dr)->dr_xid) 654 #define REQTOXID(req) ((req)->rq_xprt->xp_xid) 655 656 static int ndupreqs = 0; 657 static int maxdupreqs = MAXDUPREQS; 658 static kmutex_t dupreq_lock; 659 static struct dupreq *drhashtbl[DRHASHSZ]; 660 static int drhashstat[DRHASHSZ]; 661 662 static void unhash(struct dupreq *); 663 664 /* 665 * drmru points to the head of a circular linked list in lru order. 666 * drmru->dr_next == drlru 667 */ 668 struct dupreq *drmru; 669 670 /* 671 * PSARC 2003/523 Contract Private Interface 672 * svc_clts_kdup 673 * Changes must be reviewed by Solaris File Sharing 674 * Changes must be communicated to contract-2003-523@sun.com 675 * 676 * svc_clts_kdup searches the request cache and returns 0 if the 677 * request is not found in the cache. If it is found, then it 678 * returns the state of the request (in progress or done) and 679 * the status or attributes that were part of the original reply. 680 * 681 * If DUP_DONE (there is a duplicate) svc_clts_kdup copies over the 682 * value of the response. In that case, also return in *dupcachedp 683 * whether the response free routine is cached in the dupreq - in which case 684 * the caller should not be freeing it, because it will be done later 685 * in the svc_clts_kdup code when the dupreq is reused. 686 */ 687 static int 688 svc_clts_kdup(struct svc_req *req, caddr_t res, int size, struct dupreq **drpp, 689 bool_t *dupcachedp) 690 { 691 struct rpc_clts_server *stats = CLONE2STATS(req->rq_xprt); 692 struct dupreq *dr; 693 uint32_t xid; 694 uint32_t drhash; 695 int status; 696 697 xid = REQTOXID(req); 698 mutex_enter(&dupreq_lock); 699 RSSTAT_INCR(stats, rsdupchecks); 700 /* 701 * Check to see whether an entry already exists in the cache. 702 */ 703 dr = drhashtbl[XIDHASH(xid)]; 704 while (dr != NULL) { 705 if (dr->dr_xid == xid && 706 dr->dr_proc == req->rq_proc && 707 dr->dr_prog == req->rq_prog && 708 dr->dr_vers == req->rq_vers && 709 dr->dr_addr.len == req->rq_xprt->xp_rtaddr.len && 710 bcmp(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf, 711 dr->dr_addr.len) == 0) { 712 status = dr->dr_status; 713 if (status == DUP_DONE) { 714 bcopy(dr->dr_resp.buf, res, size); 715 if (dupcachedp != NULL) 716 *dupcachedp = (dr->dr_resfree != NULL); 717 } else { 718 dr->dr_status = DUP_INPROGRESS; 719 *drpp = dr; 720 } 721 RSSTAT_INCR(stats, rsdupreqs); 722 mutex_exit(&dupreq_lock); 723 return (status); 724 } 725 dr = dr->dr_chain; 726 } 727 728 /* 729 * There wasn't an entry, either allocate a new one or recycle 730 * an old one. 731 */ 732 if (ndupreqs < maxdupreqs) { 733 dr = kmem_alloc(sizeof (*dr), KM_NOSLEEP); 734 if (dr == NULL) { 735 mutex_exit(&dupreq_lock); 736 return (DUP_ERROR); 737 } 738 dr->dr_resp.buf = NULL; 739 dr->dr_resp.maxlen = 0; 740 dr->dr_addr.buf = NULL; 741 dr->dr_addr.maxlen = 0; 742 if (drmru) { 743 dr->dr_next = drmru->dr_next; 744 drmru->dr_next = dr; 745 } else { 746 dr->dr_next = dr; 747 } 748 ndupreqs++; 749 } else { 750 dr = drmru->dr_next; 751 while (dr->dr_status == DUP_INPROGRESS) { 752 dr = dr->dr_next; 753 if (dr == drmru->dr_next) { 754 cmn_err(CE_WARN, "svc_clts_kdup no slots free"); 755 mutex_exit(&dupreq_lock); 756 return (DUP_ERROR); 757 } 758 } 759 unhash(dr); 760 if (dr->dr_resfree) { 761 (*dr->dr_resfree)(dr->dr_resp.buf); 762 } 763 } 764 dr->dr_resfree = NULL; 765 drmru = dr; 766 767 dr->dr_xid = REQTOXID(req); 768 dr->dr_prog = req->rq_prog; 769 dr->dr_vers = req->rq_vers; 770 dr->dr_proc = req->rq_proc; 771 if (dr->dr_addr.maxlen < req->rq_xprt->xp_rtaddr.len) { 772 if (dr->dr_addr.buf != NULL) 773 kmem_free(dr->dr_addr.buf, dr->dr_addr.maxlen); 774 dr->dr_addr.maxlen = req->rq_xprt->xp_rtaddr.len; 775 dr->dr_addr.buf = kmem_alloc(dr->dr_addr.maxlen, 776 KM_NOSLEEP); 777 if (dr->dr_addr.buf == NULL) { 778 dr->dr_addr.maxlen = 0; 779 dr->dr_status = DUP_DROP; 780 mutex_exit(&dupreq_lock); 781 return (DUP_ERROR); 782 } 783 } 784 dr->dr_addr.len = req->rq_xprt->xp_rtaddr.len; 785 bcopy(req->rq_xprt->xp_rtaddr.buf, dr->dr_addr.buf, dr->dr_addr.len); 786 if (dr->dr_resp.maxlen < size) { 787 if (dr->dr_resp.buf != NULL) 788 kmem_free(dr->dr_resp.buf, dr->dr_resp.maxlen); 789 dr->dr_resp.maxlen = (unsigned int)size; 790 dr->dr_resp.buf = kmem_alloc(size, KM_NOSLEEP); 791 if (dr->dr_resp.buf == NULL) { 792 dr->dr_resp.maxlen = 0; 793 dr->dr_status = DUP_DROP; 794 mutex_exit(&dupreq_lock); 795 return (DUP_ERROR); 796 } 797 } 798 dr->dr_status = DUP_INPROGRESS; 799 800 drhash = (uint32_t)DRHASH(dr); 801 dr->dr_chain = drhashtbl[drhash]; 802 drhashtbl[drhash] = dr; 803 drhashstat[drhash]++; 804 mutex_exit(&dupreq_lock); 805 *drpp = dr; 806 return (DUP_NEW); 807 } 808 809 /* 810 * PSARC 2003/523 Contract Private Interface 811 * svc_clts_kdupdone 812 * Changes must be reviewed by Solaris File Sharing 813 * Changes must be communicated to contract-2003-523@sun.com 814 * 815 * svc_clts_kdupdone marks the request done (DUP_DONE or DUP_DROP) 816 * and stores the response. 817 */ 818 static void 819 svc_clts_kdupdone(struct dupreq *dr, caddr_t res, void (*dis_resfree)(), 820 int size, int status) 821 { 822 823 ASSERT(dr->dr_resfree == NULL); 824 if (status == DUP_DONE) { 825 bcopy(res, dr->dr_resp.buf, size); 826 dr->dr_resfree = dis_resfree; 827 } 828 dr->dr_status = status; 829 } 830 831 /* 832 * This routine expects that the mutex, dupreq_lock, is already held. 833 */ 834 static void 835 unhash(struct dupreq *dr) 836 { 837 struct dupreq *drt; 838 struct dupreq *drtprev = NULL; 839 uint32_t drhash; 840 841 ASSERT(MUTEX_HELD(&dupreq_lock)); 842 843 drhash = (uint32_t)DRHASH(dr); 844 drt = drhashtbl[drhash]; 845 while (drt != NULL) { 846 if (drt == dr) { 847 drhashstat[drhash]--; 848 if (drtprev == NULL) { 849 drhashtbl[drhash] = drt->dr_chain; 850 } else { 851 drtprev->dr_chain = drt->dr_chain; 852 } 853 return; 854 } 855 drtprev = drt; 856 drt = drt->dr_chain; 857 } 858 } 859 860 void 861 svc_clts_stats_init(zoneid_t zoneid, struct rpc_clts_server **statsp) 862 { 863 kstat_t *ksp; 864 kstat_named_t *knp; 865 866 knp = rpcstat_zone_init_common(zoneid, "unix", "rpc_clts_server", 867 (const kstat_named_t *)&clts_rsstat_tmpl, 868 sizeof (clts_rsstat_tmpl)); 869 /* 870 * Backwards compatibility for old kstat clients 871 */ 872 ksp = kstat_create_zone("unix", 0, "rpc_server", "rpc", 873 KSTAT_TYPE_NAMED, clts_rsstat_ndata, 874 KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE, zoneid); 875 if (ksp) { 876 ksp->ks_data = knp; 877 kstat_install(ksp); 878 } 879 *statsp = (struct rpc_clts_server *)knp; 880 } 881 882 void 883 svc_clts_stats_fini(zoneid_t zoneid, struct rpc_clts_server **statsp) 884 { 885 rpcstat_zone_fini_common(zoneid, "unix", "rpc_clts_server"); 886 kstat_delete_byname_zone("unix", 0, "rpc_server", zoneid); 887 kmem_free(*statsp, sizeof (clts_rsstat_tmpl)); 888 } 889 890 void 891 svc_clts_init() 892 { 893 /* 894 * Check to make sure that the clts private data will fit into 895 * the stack buffer allocated by svc_run. The compiler should 896 * remove this check, but it's a safety net if the udp_data 897 * structure ever changes. 898 */ 899 /*CONSTANTCONDITION*/ 900 ASSERT(sizeof (struct udp_data) <= SVC_P2LEN); 901 902 mutex_init(&dupreq_lock, NULL, MUTEX_DEFAULT, NULL); 903 } 904