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