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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
35 * xdr_mblk.c, XDR implementation on kernel streams mblks.
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/systm.h>
41 #include <sys/stream.h>
42 #include <sys/cmn_err.h>
43 #include <sys/strsubr.h>
44 #include <sys/strsun.h>
45 #include <sys/debug.h>
46 #include <sys/sysmacros.h>
48 #include <rpc/types.h>
51 static bool_t
xdrmblk_getint32(XDR
*, int32_t *);
52 static bool_t
xdrmblk_putint32(XDR
*, int32_t *);
53 static bool_t
xdrmblk_getbytes(XDR
*, caddr_t
, int);
54 static bool_t
xdrmblk_putbytes(XDR
*, caddr_t
, int);
55 static uint_t
xdrmblk_getpos(XDR
*);
56 static bool_t
xdrmblk_setpos(XDR
*, uint_t
);
57 static rpc_inline_t
*xdrmblk_inline(XDR
*, int);
58 static void xdrmblk_destroy(XDR
*);
59 static bool_t
xdrmblk_control(XDR
*, int, void *);
61 static mblk_t
*xdrmblk_alloc(int);
64 * Xdr on mblks operations vector.
66 struct xdr_ops xdrmblk_ops
= {
79 * Initialize xdr stream.
82 xdrmblk_init(XDR
*xdrs
, mblk_t
*m
, enum xdr_op op
, int sz
)
85 xdrs
->x_ops
= &xdrmblk_ops
;
86 xdrs
->x_base
= (caddr_t
)m
;
87 xdrs
->x_public
= NULL
;
88 xdrs
->x_private
= (caddr_t
)(uintptr_t)sz
;
91 xdrs
->x_handy
= (int)(m
->b_wptr
- m
->b_rptr
);
93 xdrs
->x_handy
= (int)(m
->b_datap
->db_lim
- m
->b_datap
->db_base
);
98 xdrmblk_destroy(XDR
*xdrs
)
103 xdrmblk_getint32(XDR
*xdrs
, int32_t *int32p
)
107 /* LINTED pointer alignment */
108 m
= (mblk_t
*)xdrs
->x_base
;
112 * If the pointer is not aligned or there is not
113 * enough bytes, pullupmsg to get enough bytes and
116 if (!IS_P2ALIGNED(m
->b_rptr
, sizeof (int32_t)) ||
117 xdrs
->x_handy
< sizeof (int32_t)) {
118 while (!pullupmsg(m
, sizeof (int32_t))) {
120 * Could have failed due to not
121 * enough data or an allocb failure.
123 if (xmsgsize(m
) < sizeof (int32_t))
127 xdrs
->x_handy
= (int)(m
->b_wptr
- m
->b_rptr
);
130 /* LINTED pointer alignment */
131 *int32p
= ntohl(*((int32_t *)(m
->b_rptr
)));
132 m
->b_rptr
+= sizeof (int32_t);
135 * Instead of leaving handy as 0 causing more pullupmsg's
136 * simply move to the next mblk.
138 if ((xdrs
->x_handy
-= sizeof (int32_t)) == 0) {
140 xdrs
->x_base
= (caddr_t
)m
;
142 xdrs
->x_handy
= (int)(m
->b_wptr
- m
->b_rptr
);
148 xdrmblk_putint32(XDR
*xdrs
, int32_t *int32p
)
152 /* LINTED pointer alignment */
153 m
= (mblk_t
*)xdrs
->x_base
;
156 if ((xdrs
->x_handy
-= (int)sizeof (int32_t)) < 0) {
157 if (m
->b_cont
== NULL
) {
158 m
->b_cont
= xdrmblk_alloc((int)(uintptr_t)
162 xdrs
->x_base
= (caddr_t
)m
;
167 xdrs
->x_handy
= (int)(m
->b_datap
->db_lim
- m
->b_rptr
-
169 ASSERT(m
->b_rptr
== m
->b_wptr
);
170 ASSERT(m
->b_rptr
>= m
->b_datap
->db_base
);
171 ASSERT(m
->b_rptr
< m
->b_datap
->db_lim
);
173 /* LINTED pointer alignment */
174 *(int32_t *)m
->b_wptr
= htonl(*int32p
);
175 m
->b_wptr
+= sizeof (int32_t);
176 ASSERT(m
->b_wptr
<= m
->b_datap
->db_lim
);
181 * We pick 16 as a compromise threshold for most architectures.
183 #define XDRMBLK_BCOPY_LIMIT 16
186 xdrmblk_getbytes(XDR
*xdrs
, caddr_t addr
, int len
)
191 /* LINTED pointer alignment */
192 m
= (mblk_t
*)xdrs
->x_base
;
196 * Performance tweak: converted explicit bcopy()
197 * call to simple in-line. This function is called
198 * to process things like readdir reply filenames
199 * which are small strings--typically 12 bytes or less.
200 * Overhead of calling bcopy() is obnoxious for such
203 while ((xdrs
->x_handy
-= len
) < 0) {
204 if ((xdrs
->x_handy
+= len
) > 0) {
205 if (len
< XDRMBLK_BCOPY_LIMIT
) {
206 for (i
= 0; i
< xdrs
->x_handy
; i
++)
207 *addr
++ = *m
->b_rptr
++;
209 bcopy(m
->b_rptr
, addr
, xdrs
->x_handy
);
210 m
->b_rptr
+= xdrs
->x_handy
;
211 addr
+= xdrs
->x_handy
;
213 len
-= xdrs
->x_handy
;
216 xdrs
->x_base
= (caddr_t
)m
;
221 xdrs
->x_handy
= (int)(m
->b_wptr
- m
->b_rptr
);
223 if (len
< XDRMBLK_BCOPY_LIMIT
) {
224 for (i
= 0; i
< len
; i
++)
225 *addr
++ = *m
->b_rptr
++;
227 bcopy(m
->b_rptr
, addr
, len
);
234 * Sort of like getbytes except that instead of getting bytes we return the
235 * mblk chain which contains the data. If the data ends in the middle of
236 * an mblk, the mblk is dup'd and split, so that the data will end on an
237 * mblk. Note that it is up to the caller to keep track of the data length
238 * and not walk too far down the mblk chain.
242 xdrmblk_getmblk(XDR
*xdrs
, mblk_t
**mm
, uint_t
*lenp
)
248 if (!xdrmblk_getint32(xdrs
, &llen
))
252 /* LINTED pointer alignment */
253 m
= (mblk_t
*)xdrs
->x_base
;
257 * Walk the mblk chain until we get to the end or we've gathered
261 llen
= roundup(llen
, BYTES_PER_XDR_UNIT
);
262 while (m
!= NULL
&& len
+ (int)MBLKL(m
) <= llen
) {
263 len
+= (int)MBLKL(m
);
270 int tail_bytes
= llen
- len
;
273 * Split the mblk with the last chunk of data and
274 * insert it into the chain. The new mblk goes
275 * after the existing one so that it will get freed
281 nextm
->b_cont
= m
->b_cont
;
283 m
->b_wptr
= m
->b_rptr
+ tail_bytes
;
284 nextm
->b_rptr
+= tail_bytes
;
285 ASSERT(nextm
->b_rptr
!= nextm
->b_wptr
);
287 m
= nextm
; /* for x_base */
290 xdrs
->x_base
= (caddr_t
)m
;
291 xdrs
->x_handy
= m
!= NULL
? MBLKL(m
) : 0;
296 xdrmblk_putbytes(XDR
*xdrs
, caddr_t addr
, int len
)
301 /* LINTED pointer alignment */
302 m
= (mblk_t
*)xdrs
->x_base
;
306 * Performance tweak: converted explicit bcopy()
307 * call to simple in-line. This function is called
308 * to process things like readdir reply filenames
309 * which are small strings--typically 12 bytes or less.
310 * Overhead of calling bcopy() is obnoxious for such
313 while ((xdrs
->x_handy
-= len
) < 0) {
314 if ((xdrs
->x_handy
+= len
) > 0) {
315 if (xdrs
->x_handy
< XDRMBLK_BCOPY_LIMIT
) {
316 for (i
= 0; i
< (uint_t
)xdrs
->x_handy
; i
++)
317 *m
->b_wptr
++ = *addr
++;
319 bcopy(addr
, m
->b_wptr
, xdrs
->x_handy
);
320 m
->b_wptr
+= xdrs
->x_handy
;
321 addr
+= xdrs
->x_handy
;
323 len
-= xdrs
->x_handy
;
327 * We don't have enough space, so allocate the
328 * amount we need, or x_private, whichever is larger.
329 * It is better to let the underlying transport divide
330 * large chunks than to try and guess what is best.
332 if (m
->b_cont
== NULL
)
333 m
->b_cont
= xdrmblk_alloc(MAX(len
,
334 (int)(uintptr_t)xdrs
->x_private
));
337 xdrs
->x_base
= (caddr_t
)m
;
342 xdrs
->x_handy
= (int)(m
->b_datap
->db_lim
- m
->b_rptr
);
343 ASSERT(m
->b_rptr
== m
->b_wptr
);
344 ASSERT(m
->b_rptr
>= m
->b_datap
->db_base
);
345 ASSERT(m
->b_rptr
< m
->b_datap
->db_lim
);
347 if (len
< XDRMBLK_BCOPY_LIMIT
) {
348 for (i
= 0; i
< len
; i
++)
349 *m
->b_wptr
++ = *addr
++;
351 bcopy(addr
, m
->b_wptr
, len
);
354 ASSERT(m
->b_wptr
<= m
->b_datap
->db_lim
);
359 * We avoid a copy by merely adding this mblk to the list. The caller is
360 * responsible for allocating and filling in the mblk. If len is
361 * not a multiple of BYTES_PER_XDR_UNIT, the caller has the option
362 * of making the data a BYTES_PER_XDR_UNIT multiple (b_wptr - b_rptr is
363 * a BYTES_PER_XDR_UNIT multiple), but in this case the caller has to ensure
364 * that the filler bytes are initialized to zero.
367 xdrmblk_putmblk(XDR
*xdrs
, mblk_t
*m
, uint_t len
)
369 int32_t llen
= (int32_t)len
;
371 if ((DLEN(m
) % BYTES_PER_XDR_UNIT
) != 0)
373 if (!xdrmblk_putint32(xdrs
, &llen
))
376 /* LINTED pointer alignment */
377 ((mblk_t
*)xdrs
->x_base
)->b_cont
= m
;
379 /* base points to the last mblk */
382 xdrs
->x_base
= (caddr_t
)m
;
388 xdrmblk_getpos(XDR
*xdrs
)
393 /* LINTED pointer alignment */
394 m
= (mblk_t
*)xdrs
->x_base
;
396 if (xdrs
->x_op
== XDR_DECODE
)
397 tmp
= (uint_t
)(m
->b_rptr
- m
->b_datap
->db_base
);
399 tmp
= (uint_t
)(m
->b_wptr
- m
->b_datap
->db_base
);
405 xdrmblk_setpos(XDR
*xdrs
, uint_t pos
)
408 unsigned char *newaddr
;
410 /* LINTED pointer alignment */
411 m
= (mblk_t
*)xdrs
->x_base
;
415 /* calculate the new address from the base */
416 newaddr
= m
->b_datap
->db_base
+ pos
;
418 if (xdrs
->x_op
== XDR_DECODE
) {
419 if (newaddr
> m
->b_wptr
)
422 xdrs
->x_handy
= (int)(m
->b_wptr
- newaddr
);
424 if (newaddr
> m
->b_datap
->db_lim
)
427 xdrs
->x_handy
= (int)(m
->b_datap
->db_lim
- newaddr
);
434 static int xdrmblk_inline_hits
= 0;
435 static int xdrmblk_inline_misses
= 0;
436 static int do_xdrmblk_inline
= 1;
439 static rpc_inline_t
*
440 xdrmblk_inline(XDR
*xdrs
, int len
)
446 * Can't inline XDR_FREE calls, doesn't make sense.
448 if (xdrs
->x_op
== XDR_FREE
)
452 * Can't inline if there isn't enough room, don't have an
453 * mblk pointer, its not 4 byte aligned, or if there is more than
454 * one reference to the data block associated with this mblk. This last
455 * check is used because the caller may want to modified
456 * the data in the inlined portion and someone else is
457 * holding a reference to the data who may not want it
460 if (xdrs
->x_handy
< len
||
461 /* LINTED pointer alignment */
462 (m
= (mblk_t
*)xdrs
->x_base
) == NULL
||
463 !IS_P2ALIGNED(m
->b_rptr
, sizeof (int32_t)) ||
464 m
->b_datap
->db_ref
!= 1) {
466 xdrmblk_inline_misses
++;
472 if (!do_xdrmblk_inline
) {
473 xdrmblk_inline_misses
++;
478 xdrs
->x_handy
-= len
;
479 if (xdrs
->x_op
== XDR_DECODE
) {
480 /* LINTED pointer alignment */
481 buf
= (rpc_inline_t
*)m
->b_rptr
;
484 /* LINTED pointer alignment */
485 buf
= (rpc_inline_t
*)m
->b_wptr
;
489 xdrmblk_inline_hits
++;
495 xdrmblk_control(XDR
*xdrs
, int request
, void *info
)
504 * Return the next 4 byte unit in the XDR stream.
506 if (xdrs
->x_handy
< sizeof (int32_t))
509 /* LINTED pointer alignment */
510 m
= (mblk_t
*)xdrs
->x_base
;
515 * If the pointer is not aligned, fail the peek
517 if (!IS_P2ALIGNED(m
->b_rptr
, sizeof (int32_t)))
520 int32p
= (int32_t *)info
;
521 /* LINTED pointer alignment */
522 *int32p
= ntohl(*((int32_t *)(m
->b_rptr
)));
526 /* LINTED pointer alignment */
527 m
= (mblk_t
*)xdrs
->x_base
;
530 int32p
= (int32_t *)info
;
531 len
= RNDUP((int)(*int32p
));
534 while ((xdrs
->x_handy
-= len
) < 0) {
535 if ((xdrs
->x_handy
+= len
) > 0) {
536 m
->b_rptr
+= xdrs
->x_handy
;
537 len
-= xdrs
->x_handy
;
540 xdrs
->x_base
= (caddr_t
)m
;
545 xdrs
->x_handy
= (int)(m
->b_wptr
- m
->b_rptr
);
555 #define HDR_SPACE 128
558 xdrmblk_alloc(int sz
)
566 * Pad the front of the message to allow the lower networking
567 * layers space to add headers as needed.
571 while ((mp
= allocb(sz
, BPRI_LO
)) == NULL
) {
572 if (strwaitbuf(sz
, BPRI_LO
))
576 mp
->b_wptr
+= HDR_SPACE
;
577 mp
->b_rptr
= mp
->b_wptr
;