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.
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.
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]
22 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
25 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
29 * Portions of this source code were derived from Berkeley 4.3 BSD
30 * under license from the Regents of the University of California.
35 * Server side for RPC in the kernel.
39 #include <sys/param.h>
40 #include <sys/types.h>
41 #include <sys/sysmacros.h>
43 #include <sys/stream.h>
44 #include <sys/strsun.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>
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>
58 #include <rpc/types.h>
62 #include <rpc/rpc_msg.h>
67 * Routines exported through ops vector.
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 static void svc_clts_kclone_xprt(SVCXPRT
*, SVCXPRT
*);
83 static void svc_clts_ktattrs(SVCXPRT
*, int, void **);
86 * Server transport operations vector.
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 svc_clts_kclone_xprt
, /* transport specific clone xprt function */
101 svc_clts_ktattrs
/* Transport specific attributes. */
105 * Transport private data.
106 * Kept in xprt->xp_p2buf.
109 mblk_t
*ud_resp
; /* buffer for response */
110 mblk_t
*ud_inmp
; /* mblk chain of request */
113 #define UD_MAXSIZE 8800
114 #define UD_INITSIZE 2048
117 * Connectionless server statistics
119 static const struct rpc_clts_server
{
120 kstat_named_t rscalls
;
121 kstat_named_t rsbadcalls
;
122 kstat_named_t rsnullrecv
;
123 kstat_named_t rsbadlen
;
124 kstat_named_t rsxdrcall
;
125 kstat_named_t rsdupchecks
;
126 kstat_named_t rsdupreqs
;
127 } clts_rsstat_tmpl
= {
128 { "calls", KSTAT_DATA_UINT64
},
129 { "badcalls", KSTAT_DATA_UINT64
},
130 { "nullrecv", KSTAT_DATA_UINT64
},
131 { "badlen", KSTAT_DATA_UINT64
},
132 { "xdrcall", KSTAT_DATA_UINT64
},
133 { "dupchecks", KSTAT_DATA_UINT64
},
134 { "dupreqs", KSTAT_DATA_UINT64
}
137 static uint_t clts_rsstat_ndata
=
138 sizeof (clts_rsstat_tmpl
) / sizeof (kstat_named_t
);
140 #define CLONE2STATS(clone_xprt) \
141 (struct rpc_clts_server *)(clone_xprt)->xp_master->xp_p2
143 #define RSSTAT_INCR(stats, x) \
144 atomic_add_64(&(stats)->x.value.ui64, 1)
147 * Create a transport record.
148 * The transport record, output buffer, and private data structure
149 * are allocated. The output buffer is serialized into using xdrmem.
150 * There is one transport record per user process which implements a
155 svc_clts_kcreate(file_t
*fp
, uint_t sendsz
, struct T_info_ack
*tinfo
,
156 SVCMASTERXPRT
**nxprt
)
159 struct rpcstat
*rpcstat
;
164 rpcstat
= zone_getspecific(rpcstat_zone_key
, curproc
->p_zone
);
165 ASSERT(rpcstat
!= NULL
);
167 xprt
= kmem_zalloc(sizeof (*xprt
), KM_SLEEP
);
168 xprt
->xp_lcladdr
.buf
= kmem_zalloc(sizeof (sin6_t
), KM_SLEEP
);
169 xprt
->xp_p2
= (caddr_t
)rpcstat
->rpc_clts_server
;
170 xprt
->xp_ops
= &svc_clts_op
;
171 xprt
->xp_msg_size
= tinfo
->TSDU_size
;
173 xprt
->xp_rtaddr
.buf
= NULL
;
174 xprt
->xp_rtaddr
.maxlen
= tinfo
->ADDR_size
;
175 xprt
->xp_rtaddr
.len
= 0;
183 * Destroy a transport record.
184 * Frees the space allocated for a transport record.
187 svc_clts_kdestroy(SVCMASTERXPRT
*xprt
)
190 kmem_free(xprt
->xp_netid
, strlen(xprt
->xp_netid
) + 1);
191 if (xprt
->xp_addrmask
.maxlen
)
192 kmem_free(xprt
->xp_addrmask
.buf
, xprt
->xp_addrmask
.maxlen
);
194 mutex_destroy(&xprt
->xp_req_lock
);
195 mutex_destroy(&xprt
->xp_thread_lock
);
197 kmem_free(xprt
->xp_lcladdr
.buf
, sizeof (sin6_t
));
198 kmem_free(xprt
, sizeof (SVCMASTERXPRT
));
202 * Transport-type specific part of svc_xprt_cleanup().
203 * Frees the message buffer space allocated for a clone of a transport record
206 svc_clts_kclone_destroy(SVCXPRT
*clone_xprt
)
208 /* LINTED pointer alignment */
209 struct udp_data
*ud
= (struct udp_data
*)clone_xprt
->xp_p2buf
;
213 * There should not be any left over results buffer.
215 ASSERT(ud
->ud_resp
->b_cont
== NULL
);
218 * Free the T_UNITDATA_{REQ/IND} that svc_clts_krecv
224 freemsg(ud
->ud_inmp
);
228 * svc_tli_kcreate() calls this function at the end to tell
229 * rpcmod that the transport is ready to receive requests.
233 svc_clts_kstart(SVCMASTERXPRT
*xprt
)
238 svc_clts_kclone_xprt(SVCXPRT
*src_xprt
, SVCXPRT
*dst_xprt
)
240 struct udp_data
*ud_src
=
241 (struct udp_data
*)src_xprt
->xp_p2buf
;
242 struct udp_data
*ud_dst
=
243 (struct udp_data
*)dst_xprt
->xp_p2buf
;
246 ud_dst
->ud_resp
= dupb(ud_src
->ud_resp
);
251 svc_clts_ktattrs(SVCXPRT
*clone_xprt
, int attrflag
, void **tattr
)
256 case SVC_TATTR_ADDRMASK
:
257 *tattr
= (void *)&clone_xprt
->xp_master
->xp_addrmask
;
262 * Receive rpc requests.
263 * Pulls a request in off the socket, checks if the packet is intact,
264 * and deserializes the call packet.
267 svc_clts_krecv(SVCXPRT
*clone_xprt
, mblk_t
*mp
, struct rpc_msg
*msg
)
269 /* LINTED pointer alignment */
270 struct udp_data
*ud
= (struct udp_data
*)clone_xprt
->xp_p2buf
;
271 XDR
*xdrs
= &clone_xprt
->xp_xdrin
;
272 struct rpc_clts_server
*stats
= CLONE2STATS(clone_xprt
);
273 union T_primitives
*pptr
;
277 TRACE_0(TR_FAC_KRPC
, TR_SVC_CLTS_KRECV_START
,
278 "svc_clts_krecv_start:");
280 RSSTAT_INCR(stats
, rscalls
);
283 * The incoming request should start with an M_PROTO message.
285 if (mp
->b_datap
->db_type
!= M_PROTO
) {
290 * The incoming request should be an T_UNITDTA_IND. There
291 * might be other messages coming up the stream, but we can
294 pptr
= (union T_primitives
*)mp
->b_rptr
;
295 if (pptr
->type
!= T_UNITDATA_IND
) {
299 * Do some checking to make sure that the header at least looks okay.
301 hdrsz
= (int)(mp
->b_wptr
- mp
->b_rptr
);
302 if (hdrsz
< TUNITDATAINDSZ
||
303 hdrsz
< (pptr
->unitdata_ind
.OPT_offset
+
304 pptr
->unitdata_ind
.OPT_length
) ||
305 hdrsz
< (pptr
->unitdata_ind
.SRC_offset
+
306 pptr
->unitdata_ind
.SRC_length
)) {
311 * Make sure that the transport provided a usable address.
313 if (pptr
->unitdata_ind
.SRC_length
<= 0) {
317 * Point the remote transport address in the service_transport
318 * handle at the address in the request.
320 clone_xprt
->xp_rtaddr
.buf
= (char *)mp
->b_rptr
+
321 pptr
->unitdata_ind
.SRC_offset
;
322 clone_xprt
->xp_rtaddr
.len
= pptr
->unitdata_ind
.SRC_length
;
325 * Copy the local transport address in the service_transport
326 * handle at the address in the request. We will have only
327 * the local IP address in options.
329 ((sin_t
*)(clone_xprt
->xp_lcladdr
.buf
))->sin_family
= AF_UNSPEC
;
330 if (pptr
->unitdata_ind
.OPT_length
&& pptr
->unitdata_ind
.OPT_offset
) {
331 char *dstopt
= (char *)mp
->b_rptr
+
332 pptr
->unitdata_ind
.OPT_offset
;
333 struct T_opthdr
*toh
= (struct T_opthdr
*)dstopt
;
335 if (toh
->level
== IPPROTO_IPV6
&& toh
->status
== 0 &&
336 toh
->name
== IPV6_PKTINFO
) {
337 struct in6_pktinfo
*pkti
;
339 dstopt
+= sizeof (struct T_opthdr
);
340 pkti
= (struct in6_pktinfo
*)dstopt
;
341 ((sin6_t
*)(clone_xprt
->xp_lcladdr
.buf
))->sin6_addr
343 ((sin6_t
*)(clone_xprt
->xp_lcladdr
.buf
))->sin6_family
345 } else if (toh
->level
== IPPROTO_IP
&& toh
->status
== 0 &&
346 toh
->name
== IP_RECVDSTADDR
) {
347 dstopt
+= sizeof (struct T_opthdr
);
348 ((sin_t
*)(clone_xprt
->xp_lcladdr
.buf
))->sin_addr
349 = *(struct in_addr
*)dstopt
;
350 ((sin_t
*)(clone_xprt
->xp_lcladdr
.buf
))->sin_family
356 * Save the first mblk which contains the T_unidata_ind in
357 * ud_resp. It will be used to generate the T_unitdata_req
359 * We reuse any options in the T_unitdata_ind for the T_unitdata_req
360 * since we must pass any SCM_UCRED across in order for TX to
361 * work. We also make sure any cred_t is carried across.
364 if (ud
->ud_resp
->b_cont
!= NULL
) {
365 cmn_err(CE_WARN
, "svc_clts_krecv: ud_resp %p, "
366 "b_cont %p", (void *)ud
->ud_resp
,
367 (void *)ud
->ud_resp
->b_cont
);
371 /* Move any cred_t to the first mblk in the message */
372 cr
= msg_getcred(mp
, NULL
);
374 mblk_setcred(mp
, cr
, NOPID
);
378 ud
->ud_resp
->b_cont
= NULL
;
380 xdrmblk_init(xdrs
, mp
, XDR_DECODE
, 0);
382 TRACE_0(TR_FAC_KRPC
, TR_XDR_CALLMSG_START
,
383 "xdr_callmsg_start:");
384 if (! xdr_callmsg(xdrs
, msg
)) {
385 TRACE_1(TR_FAC_KRPC
, TR_XDR_CALLMSG_END
,
386 "xdr_callmsg_end:(%S)", "bad");
387 RSSTAT_INCR(stats
, rsxdrcall
);
390 TRACE_1(TR_FAC_KRPC
, TR_XDR_CALLMSG_END
,
391 "xdr_callmsg_end:(%S)", "good");
393 clone_xprt
->xp_xid
= msg
->rm_xid
;
396 TRACE_1(TR_FAC_KRPC
, TR_SVC_CLTS_KRECV_END
,
397 "svc_clts_krecv_end:(%S)", "good");
404 * There should not be any left over results buffer.
406 ASSERT(ud
->ud_resp
->b_cont
== NULL
);
411 RSSTAT_INCR(stats
, rsbadcalls
);
412 TRACE_1(TR_FAC_KRPC
, TR_SVC_CLTS_KRECV_END
,
413 "svc_clts_krecv_end:(%S)", "bad");
419 * Serialize the reply packet into the output buffer then
420 * call t_ksndudata to send it.
423 svc_clts_ksend(SVCXPRT
*clone_xprt
, struct rpc_msg
*msg
)
425 /* LINTED pointer alignment */
426 struct udp_data
*ud
= (struct udp_data
*)clone_xprt
->xp_p2buf
;
427 XDR
*xdrs
= &clone_xprt
->xp_xdrout
;
431 struct T_unitdata_req
*udreq
;
432 xdrproc_t xdr_results
;
433 caddr_t xdr_location
;
436 TRACE_0(TR_FAC_KRPC
, TR_SVC_CLTS_KSEND_START
,
437 "svc_clts_ksend_start:");
439 ASSERT(ud
->ud_resp
!= NULL
);
442 * If there is a result procedure specified in the reply message,
443 * it will be processed in the xdr_replymsg and SVCAUTH_WRAP.
444 * We need to make sure it won't be processed twice, so we null
445 * it for xdr_replymsg here.
448 if (msg
->rm_reply
.rp_stat
== MSG_ACCEPTED
&&
449 msg
->rm_reply
.rp_acpt
.ar_stat
== SUCCESS
) {
450 if ((xdr_results
= msg
->acpted_rply
.ar_results
.proc
) != NULL
) {
452 xdr_location
= msg
->acpted_rply
.ar_results
.where
;
453 msg
->acpted_rply
.ar_results
.proc
= xdr_void
;
454 msg
->acpted_rply
.ar_results
.where
= NULL
;
458 if (ud
->ud_resp
->b_cont
== NULL
) {
460 * Allocate an initial mblk for the response data.
462 while ((mp
= allocb(UD_INITSIZE
, BPRI_LO
)) == NULL
) {
463 if (strwaitbuf(UD_INITSIZE
, BPRI_LO
)) {
464 TRACE_1(TR_FAC_KRPC
, TR_SVC_CLTS_KSEND_END
,
465 "svc_clts_ksend_end:(%S)", "strwaitbuf");
471 * Initialize the XDR decode stream. Additional mblks
472 * will be allocated if necessary. They will be UD_MAXSIZE
475 xdrmblk_init(xdrs
, mp
, XDR_ENCODE
, UD_MAXSIZE
);
478 * Leave some space for protocol headers.
480 (void) XDR_SETPOS(xdrs
, 512);
483 msg
->rm_xid
= clone_xprt
->xp_xid
;
485 ud
->ud_resp
->b_cont
= mp
;
487 TRACE_0(TR_FAC_KRPC
, TR_XDR_REPLYMSG_START
,
488 "xdr_replymsg_start:");
489 if (!(xdr_replymsg(xdrs
, msg
) &&
490 (!has_args
|| SVCAUTH_WRAP(&clone_xprt
->xp_auth
, xdrs
,
491 xdr_results
, xdr_location
)))) {
492 TRACE_1(TR_FAC_KRPC
, TR_XDR_REPLYMSG_END
,
493 "xdr_replymsg_end:(%S)", "bad");
494 RPCLOG0(1, "xdr_replymsg/SVCAUTH_WRAP failed\n");
497 TRACE_1(TR_FAC_KRPC
, TR_XDR_REPLYMSG_END
,
498 "xdr_replymsg_end:(%S)", "good");
500 } else if (!(xdr_replymsg_body(xdrs
, msg
) &&
501 (!has_args
|| SVCAUTH_WRAP(&clone_xprt
->xp_auth
, xdrs
,
502 xdr_results
, xdr_location
)))) {
503 RPCLOG0(1, "xdr_replymsg_body/SVCAUTH_WRAP failed\n");
507 msgsz
= (int)xmsgsize(ud
->ud_resp
->b_cont
);
509 if (msgsz
<= 0 || (clone_xprt
->xp_msg_size
!= -1 &&
510 msgsz
> clone_xprt
->xp_msg_size
)) {
513 "KRPC: server response message of %d bytes; transport limits are [0, %d]",
514 msgsz
, clone_xprt
->xp_msg_size
);
520 * Construct the T_unitdata_req. We take advantage of the fact that
521 * T_unitdata_ind looks just like T_unitdata_req, except for the
522 * primitive type. Reusing it means we preserve the SCM_UCRED, and
523 * we must preserve it for TX to work.
525 * This has the side effect that we can also pass certain receive-side
526 * options like IPV6_PKTINFO back down the send side. This implies
527 * that we can not ASSERT on a non-NULL db_credp when we have send-side
530 ASSERT(MBLKL(ud
->ud_resp
) >= TUNITDATAREQSZ
);
531 udreq
= (struct T_unitdata_req
*)ud
->ud_resp
->b_rptr
;
532 ASSERT(udreq
->PRIM_type
== T_UNITDATA_IND
);
533 udreq
->PRIM_type
= T_UNITDATA_REQ
;
536 * If the local IPv4 transport address is known use it as a source
537 * address for the outgoing UDP packet.
539 if (((sin_t
*)(clone_xprt
->xp_lcladdr
.buf
))->sin_family
== AF_INET
) {
540 struct T_opthdr
*opthdr
;
541 in_pktinfo_t
*pktinfo
;
544 if (udreq
->DEST_length
== 0)
545 udreq
->OPT_offset
= _TPI_ALIGN_TOPT(TUNITDATAREQSZ
);
547 udreq
->OPT_offset
= _TPI_ALIGN_TOPT(udreq
->DEST_offset
+
550 udreq
->OPT_length
= sizeof (struct T_opthdr
) +
551 sizeof (in_pktinfo_t
);
553 size
= udreq
->OPT_length
+ udreq
->OPT_offset
;
555 /* make sure we have enough space for the option data */
556 mp
= reallocb(ud
->ud_resp
, size
, 1);
560 udreq
= (struct T_unitdata_req
*)mp
->b_rptr
;
562 /* set desired option header */
563 opthdr
= (struct T_opthdr
*)(mp
->b_rptr
+ udreq
->OPT_offset
);
564 opthdr
->len
= udreq
->OPT_length
;
565 opthdr
->level
= IPPROTO_IP
;
566 opthdr
->name
= IP_PKTINFO
;
569 * 1. set source IP of outbound packet
570 * 2. value '0' for index means IP layer uses this as source
573 pktinfo
= (in_pktinfo_t
*)(opthdr
+ 1);
574 (void) memset(pktinfo
, 0, sizeof (in_pktinfo_t
));
575 pktinfo
->ipi_spec_dst
.s_addr
=
576 ((sin_t
*)(clone_xprt
->xp_lcladdr
.buf
))->sin_addr
.s_addr
;
577 pktinfo
->ipi_ifindex
= 0;
579 /* adjust the end of active data */
580 mp
->b_wptr
= mp
->b_rptr
+ size
;
583 put(clone_xprt
->xp_wq
, ud
->ud_resp
);
589 freemsg(ud
->ud_resp
);
594 * This is completely disgusting. If public is set it is
595 * a pointer to a structure whose first field is the address
596 * of the function to free that structure and any related
597 * stuff. (see rrokfree in nfs_xdr.c).
599 if (xdrs
->x_public
) {
600 /* LINTED pointer alignment */
601 (**((int (**)())xdrs
->x_public
))(xdrs
->x_public
);
604 TRACE_1(TR_FAC_KRPC
, TR_SVC_CLTS_KSEND_END
,
605 "svc_clts_ksend_end:(%S)", "done");
610 * Deserialize arguments.
613 svc_clts_kgetargs(SVCXPRT
*clone_xprt
, xdrproc_t xdr_args
,
617 /* LINTED pointer alignment */
618 return (SVCAUTH_UNWRAP(&clone_xprt
->xp_auth
, &clone_xprt
->xp_xdrin
,
619 xdr_args
, args_ptr
));
624 svc_clts_kfreeargs(SVCXPRT
*clone_xprt
, xdrproc_t xdr_args
,
627 /* LINTED pointer alignment */
628 struct udp_data
*ud
= (struct udp_data
*)clone_xprt
->xp_p2buf
;
629 XDR
*xdrs
= &clone_xprt
->xp_xdrin
;
633 xdrs
->x_op
= XDR_FREE
;
634 retval
= (*xdr_args
)(xdrs
, args_ptr
);
639 freemsg(ud
->ud_inmp
);
647 svc_clts_kgetres(SVCXPRT
*clone_xprt
, int size
)
649 /* LINTED pointer alignment */
650 struct udp_data
*ud
= (struct udp_data
*)clone_xprt
->xp_p2buf
;
651 XDR
*xdrs
= &clone_xprt
->xp_xdrout
;
657 * Allocate an initial mblk for the response data.
659 while ((mp
= allocb(UD_INITSIZE
, BPRI_LO
)) == NULL
) {
660 if (strwaitbuf(UD_INITSIZE
, BPRI_LO
)) {
668 * Initialize the XDR decode stream. Additional mblks
669 * will be allocated if necessary. They will be UD_MAXSIZE
672 xdrmblk_init(xdrs
, mp
, XDR_ENCODE
, UD_MAXSIZE
);
675 * Leave some space for protocol headers.
677 (void) XDR_SETPOS(xdrs
, 512);
681 * Assume a successful RPC since most of them are.
683 rply
.rm_xid
= clone_xprt
->xp_xid
;
684 rply
.rm_direction
= REPLY
;
685 rply
.rm_reply
.rp_stat
= MSG_ACCEPTED
;
686 rply
.acpted_rply
.ar_verf
= clone_xprt
->xp_verf
;
687 rply
.acpted_rply
.ar_stat
= SUCCESS
;
689 if (!xdr_replymsg_hdr(xdrs
, &rply
)) {
694 buf
= XDR_INLINE(xdrs
, size
);
699 ud
->ud_resp
->b_cont
= mp
;
705 svc_clts_kfreeres(SVCXPRT
*clone_xprt
)
707 /* LINTED pointer alignment */
708 struct udp_data
*ud
= (struct udp_data
*)clone_xprt
->xp_p2buf
;
710 if (ud
->ud_resp
== NULL
|| ud
->ud_resp
->b_cont
== NULL
)
714 * SVC_FREERES() is called whenever the server decides not to
715 * send normal reply. Thus, we expect only one mblk to be allocated,
716 * because we have not attempted any XDR encoding.
717 * If we do any XDR encoding and we get an error, then SVC_REPLY()
718 * will freemsg(ud->ud_resp);
720 ASSERT(ud
->ud_resp
->b_cont
->b_cont
== NULL
);
721 freeb(ud
->ud_resp
->b_cont
);
722 ud
->ud_resp
->b_cont
= NULL
;
726 * the dup cacheing routines below provide a cache of non-failure
727 * transaction id's. rpc service routines can use this to detect
728 * retransmissions and re-send a non-failure response.
732 * MAXDUPREQS is the number of cached items. It should be adjusted
733 * to the service load so that there is likely to be a response entry
734 * when the first retransmission comes in.
736 #define MAXDUPREQS 1024
739 * This should be appropriately scaled to MAXDUPREQS.
743 #if ((DRHASHSZ & (DRHASHSZ - 1)) == 0)
744 #define XIDHASH(xid) ((xid) & (DRHASHSZ - 1))
746 #define XIDHASH(xid) ((xid) % DRHASHSZ)
748 #define DRHASH(dr) XIDHASH((dr)->dr_xid)
749 #define REQTOXID(req) ((req)->rq_xprt->xp_xid)
751 static int ndupreqs
= 0;
752 int maxdupreqs
= MAXDUPREQS
;
753 static kmutex_t dupreq_lock
;
754 static struct dupreq
*drhashtbl
[DRHASHSZ
];
755 static int drhashstat
[DRHASHSZ
];
757 static void unhash(struct dupreq
*);
760 * drmru points to the head of a circular linked list in lru order.
761 * drmru->dr_next == drlru
763 struct dupreq
*drmru
;
766 * PSARC 2003/523 Contract Private Interface
768 * Changes must be reviewed by Solaris File Sharing
769 * Changes must be communicated to contract-2003-523@sun.com
771 * svc_clts_kdup searches the request cache and returns 0 if the
772 * request is not found in the cache. If it is found, then it
773 * returns the state of the request (in progress or done) and
774 * the status or attributes that were part of the original reply.
776 * If DUP_DONE (there is a duplicate) svc_clts_kdup copies over the
777 * value of the response. In that case, also return in *dupcachedp
778 * whether the response free routine is cached in the dupreq - in which case
779 * the caller should not be freeing it, because it will be done later
780 * in the svc_clts_kdup code when the dupreq is reused.
783 svc_clts_kdup(struct svc_req
*req
, caddr_t res
, int size
, struct dupreq
**drpp
,
786 struct rpc_clts_server
*stats
= CLONE2STATS(req
->rq_xprt
);
793 mutex_enter(&dupreq_lock
);
794 RSSTAT_INCR(stats
, rsdupchecks
);
796 * Check to see whether an entry already exists in the cache.
798 dr
= drhashtbl
[XIDHASH(xid
)];
800 if (dr
->dr_xid
== xid
&&
801 dr
->dr_proc
== req
->rq_proc
&&
802 dr
->dr_prog
== req
->rq_prog
&&
803 dr
->dr_vers
== req
->rq_vers
&&
804 dr
->dr_addr
.len
== req
->rq_xprt
->xp_rtaddr
.len
&&
805 bcmp(dr
->dr_addr
.buf
, req
->rq_xprt
->xp_rtaddr
.buf
,
806 dr
->dr_addr
.len
) == 0) {
807 status
= dr
->dr_status
;
808 if (status
== DUP_DONE
) {
809 bcopy(dr
->dr_resp
.buf
, res
, size
);
810 if (dupcachedp
!= NULL
)
811 *dupcachedp
= (dr
->dr_resfree
!= NULL
);
813 dr
->dr_status
= DUP_INPROGRESS
;
816 RSSTAT_INCR(stats
, rsdupreqs
);
817 mutex_exit(&dupreq_lock
);
824 * There wasn't an entry, either allocate a new one or recycle
827 if (ndupreqs
< maxdupreqs
) {
828 dr
= kmem_alloc(sizeof (*dr
), KM_NOSLEEP
);
830 mutex_exit(&dupreq_lock
);
833 dr
->dr_resp
.buf
= NULL
;
834 dr
->dr_resp
.maxlen
= 0;
835 dr
->dr_addr
.buf
= NULL
;
836 dr
->dr_addr
.maxlen
= 0;
838 dr
->dr_next
= drmru
->dr_next
;
846 while (dr
->dr_status
== DUP_INPROGRESS
) {
848 if (dr
== drmru
->dr_next
) {
849 cmn_err(CE_WARN
, "svc_clts_kdup no slots free");
850 mutex_exit(&dupreq_lock
);
855 if (dr
->dr_resfree
) {
856 (*dr
->dr_resfree
)(dr
->dr_resp
.buf
);
859 dr
->dr_resfree
= NULL
;
862 dr
->dr_xid
= REQTOXID(req
);
863 dr
->dr_prog
= req
->rq_prog
;
864 dr
->dr_vers
= req
->rq_vers
;
865 dr
->dr_proc
= req
->rq_proc
;
866 if (dr
->dr_addr
.maxlen
< req
->rq_xprt
->xp_rtaddr
.len
) {
867 if (dr
->dr_addr
.buf
!= NULL
)
868 kmem_free(dr
->dr_addr
.buf
, dr
->dr_addr
.maxlen
);
869 dr
->dr_addr
.maxlen
= req
->rq_xprt
->xp_rtaddr
.len
;
870 dr
->dr_addr
.buf
= kmem_alloc(dr
->dr_addr
.maxlen
,
872 if (dr
->dr_addr
.buf
== NULL
) {
873 dr
->dr_addr
.maxlen
= 0;
874 dr
->dr_status
= DUP_DROP
;
875 mutex_exit(&dupreq_lock
);
879 dr
->dr_addr
.len
= req
->rq_xprt
->xp_rtaddr
.len
;
880 bcopy(req
->rq_xprt
->xp_rtaddr
.buf
, dr
->dr_addr
.buf
, dr
->dr_addr
.len
);
881 if (dr
->dr_resp
.maxlen
< size
) {
882 if (dr
->dr_resp
.buf
!= NULL
)
883 kmem_free(dr
->dr_resp
.buf
, dr
->dr_resp
.maxlen
);
884 dr
->dr_resp
.maxlen
= (unsigned int)size
;
885 dr
->dr_resp
.buf
= kmem_alloc(size
, KM_NOSLEEP
);
886 if (dr
->dr_resp
.buf
== NULL
) {
887 dr
->dr_resp
.maxlen
= 0;
888 dr
->dr_status
= DUP_DROP
;
889 mutex_exit(&dupreq_lock
);
893 dr
->dr_status
= DUP_INPROGRESS
;
895 drhash
= (uint32_t)DRHASH(dr
);
896 dr
->dr_chain
= drhashtbl
[drhash
];
897 drhashtbl
[drhash
] = dr
;
898 drhashstat
[drhash
]++;
899 mutex_exit(&dupreq_lock
);
905 * PSARC 2003/523 Contract Private Interface
907 * Changes must be reviewed by Solaris File Sharing
908 * Changes must be communicated to contract-2003-523@sun.com
910 * svc_clts_kdupdone marks the request done (DUP_DONE or DUP_DROP)
911 * and stores the response.
914 svc_clts_kdupdone(struct dupreq
*dr
, caddr_t res
, void (*dis_resfree
)(),
915 int size
, int status
)
918 ASSERT(dr
->dr_resfree
== NULL
);
919 if (status
== DUP_DONE
) {
920 bcopy(res
, dr
->dr_resp
.buf
, size
);
921 dr
->dr_resfree
= dis_resfree
;
923 dr
->dr_status
= status
;
927 * This routine expects that the mutex, dupreq_lock, is already held.
930 unhash(struct dupreq
*dr
)
933 struct dupreq
*drtprev
= NULL
;
936 ASSERT(MUTEX_HELD(&dupreq_lock
));
938 drhash
= (uint32_t)DRHASH(dr
);
939 drt
= drhashtbl
[drhash
];
940 while (drt
!= NULL
) {
942 drhashstat
[drhash
]--;
943 if (drtprev
== NULL
) {
944 drhashtbl
[drhash
] = drt
->dr_chain
;
946 drtprev
->dr_chain
= drt
->dr_chain
;
956 svc_clts_stats_init(zoneid_t zoneid
, struct rpc_clts_server
**statsp
)
961 knp
= rpcstat_zone_init_common(zoneid
, "unix", "rpc_clts_server",
962 (const kstat_named_t
*)&clts_rsstat_tmpl
,
963 sizeof (clts_rsstat_tmpl
));
965 * Backwards compatibility for old kstat clients
967 ksp
= kstat_create_zone("unix", 0, "rpc_server", "rpc",
968 KSTAT_TYPE_NAMED
, clts_rsstat_ndata
,
969 KSTAT_FLAG_VIRTUAL
| KSTAT_FLAG_WRITABLE
, zoneid
);
974 *statsp
= (struct rpc_clts_server
*)knp
;
978 svc_clts_stats_fini(zoneid_t zoneid
, struct rpc_clts_server
**statsp
)
980 rpcstat_zone_fini_common(zoneid
, "unix", "rpc_clts_server");
981 kstat_delete_byname_zone("unix", 0, "rpc_server", zoneid
);
982 kmem_free(*statsp
, sizeof (clts_rsstat_tmpl
));
989 * Check to make sure that the clts private data will fit into
990 * the stack buffer allocated by svc_run. The compiler should
991 * remove this check, but it's a safety net if the udp_data
992 * structure ever changes.
994 /*CONSTANTCONDITION*/
995 ASSERT(sizeof (struct udp_data
) <= SVC_P2LEN
);
997 mutex_init(&dupreq_lock
, NULL
, MUTEX_DEFAULT
, NULL
);