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]
23 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
27 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
31 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
35 * Portions of this source code were derived from Berkeley 4.3 BSD
36 * under license from the Regents of the University of California.
40 * xdr_mblk.c, XDR implementation on kernel streams mblks.
43 #include <sys/param.h>
44 #include <sys/types.h>
45 #include <sys/systm.h>
46 #include <sys/stream.h>
47 #include <sys/cmn_err.h>
48 #include <sys/strsubr.h>
49 #include <sys/strsun.h>
50 #include <sys/debug.h>
51 #include <sys/sysmacros.h>
53 #include <rpc/types.h>
56 static bool_t
xdrmblk_getint32(XDR
*, int32_t *);
57 static bool_t
xdrmblk_putint32(XDR
*, int32_t *);
58 static bool_t
xdrmblk_getbytes(XDR
*, caddr_t
, int);
59 static bool_t
xdrmblk_putbytes(XDR
*, caddr_t
, int);
60 static uint_t
xdrmblk_getpos(XDR
*);
61 static bool_t
xdrmblk_setpos(XDR
*, uint_t
);
62 static rpc_inline_t
*xdrmblk_inline(XDR
*, int);
63 static void xdrmblk_destroy(XDR
*);
64 static bool_t
xdrmblk_control(XDR
*, int, void *);
66 static mblk_t
*xdrmblk_alloc(int);
67 static void xdrmblk_skip_fully_read_mblks(XDR
*);
70 * Xdr on mblks operations vector.
72 struct xdr_ops xdrmblk_ops
= {
85 * The xdrmblk_params structure holds the internal data for the XDR stream.
86 * The x_private member of the XDR points to this structure. The
87 * xdrmblk_params structure is dynamically allocated in xdrmblk_init() and
88 * freed in xdrmblk_destroy().
90 * The apos and rpos members of the xdrmblk_params structure are used to
91 * implement xdrmblk_getpos() and xdrmblk_setpos().
93 * In addition to the xdrmblk_params structure we store some additional
94 * internal data directly in the XDR stream structure:
96 * x_base A pointer to the current mblk (that one we are currently
98 * x_handy The number of available bytes (either for read or for write) in
101 struct xdrmblk_params
{
103 uint_t apos
; /* Absolute position of the current mblk */
104 uint_t rpos
; /* Relative position in the current mblk */
108 * Initialize xdr stream.
111 xdrmblk_init(XDR
*xdrs
, mblk_t
*m
, enum xdr_op op
, int sz
)
113 struct xdrmblk_params
*p
;
116 xdrs
->x_ops
= &xdrmblk_ops
;
117 xdrs
->x_base
= (caddr_t
)m
;
118 xdrs
->x_public
= NULL
;
119 p
= kmem_alloc(sizeof (struct xdrmblk_params
), KM_SLEEP
);
120 xdrs
->x_private
= (caddr_t
)p
;
126 if (op
== XDR_DECODE
) {
127 xdrs
->x_handy
= (int)MBLKL(m
);
129 xdrs
->x_handy
= (int)MBLKTAIL(m
);
130 if (p
->sz
< sizeof (int32_t))
131 p
->sz
= sizeof (int32_t);
136 xdrmblk_destroy(XDR
*xdrs
)
138 kmem_free(xdrs
->x_private
, sizeof (struct xdrmblk_params
));
142 xdrmblk_getint32(XDR
*xdrs
, int32_t *int32p
)
145 struct xdrmblk_params
*p
;
147 xdrmblk_skip_fully_read_mblks(xdrs
);
149 /* LINTED pointer alignment */
150 m
= (mblk_t
*)xdrs
->x_base
;
154 p
= (struct xdrmblk_params
*)xdrs
->x_private
;
157 * If the pointer is not aligned or there is not
158 * enough bytes, pullupmsg to get enough bytes and
161 if (!IS_P2ALIGNED(m
->b_rptr
, sizeof (int32_t)) ||
162 xdrs
->x_handy
< sizeof (int32_t)) {
163 while (!pullupmsg(m
, sizeof (int32_t))) {
165 * Could have failed due to not
166 * enough data or an allocb failure.
168 if (xmsgsize(m
) < sizeof (int32_t))
174 xdrs
->x_handy
= (int)MBLKL(m
);
177 /* LINTED pointer alignment */
178 *int32p
= ntohl(*((int32_t *)(m
->b_rptr
)));
179 m
->b_rptr
+= sizeof (int32_t);
180 xdrs
->x_handy
-= sizeof (int32_t);
181 p
->rpos
+= sizeof (int32_t);
187 xdrmblk_putint32(XDR
*xdrs
, int32_t *int32p
)
190 struct xdrmblk_params
*p
;
192 /* LINTED pointer alignment */
193 m
= (mblk_t
*)xdrs
->x_base
;
197 p
= (struct xdrmblk_params
*)xdrs
->x_private
;
199 while (!IS_P2ALIGNED(m
->b_wptr
, sizeof (int32_t)) ||
200 xdrs
->x_handy
< sizeof (int32_t)) {
201 if (m
->b_cont
== NULL
) {
202 ASSERT(p
->sz
>= sizeof (int32_t));
203 m
->b_cont
= xdrmblk_alloc(p
->sz
);
206 xdrs
->x_base
= (caddr_t
)m
;
213 xdrs
->x_handy
= (int)MBLKTAIL(m
);
214 ASSERT(m
->b_rptr
== m
->b_wptr
);
215 ASSERT(m
->b_rptr
>= m
->b_datap
->db_base
);
216 ASSERT(m
->b_rptr
< m
->b_datap
->db_lim
);
218 /* LINTED pointer alignment */
219 *(int32_t *)m
->b_wptr
= htonl(*int32p
);
220 m
->b_wptr
+= sizeof (int32_t);
221 xdrs
->x_handy
-= sizeof (int32_t);
222 p
->rpos
+= sizeof (int32_t);
223 ASSERT(m
->b_wptr
<= m
->b_datap
->db_lim
);
228 * We pick 16 as a compromise threshold for most architectures.
230 #define XDRMBLK_BCOPY_LIMIT 16
233 xdrmblk_getbytes(XDR
*xdrs
, caddr_t addr
, int len
)
236 struct xdrmblk_params
*p
;
239 /* LINTED pointer alignment */
240 m
= (mblk_t
*)xdrs
->x_base
;
244 p
= (struct xdrmblk_params
*)xdrs
->x_private
;
247 * Performance tweak: converted explicit bcopy()
248 * call to simple in-line. This function is called
249 * to process things like readdir reply filenames
250 * which are small strings--typically 12 bytes or less.
251 * Overhead of calling bcopy() is obnoxious for such
254 while (xdrs
->x_handy
< len
) {
255 if (xdrs
->x_handy
> 0) {
256 if (xdrs
->x_handy
< XDRMBLK_BCOPY_LIMIT
) {
257 for (i
= 0; i
< xdrs
->x_handy
; i
++)
258 *addr
++ = *m
->b_rptr
++;
260 bcopy(m
->b_rptr
, addr
, xdrs
->x_handy
);
261 m
->b_rptr
+= xdrs
->x_handy
;
262 addr
+= xdrs
->x_handy
;
264 len
-= xdrs
->x_handy
;
265 p
->rpos
+= xdrs
->x_handy
;
268 xdrs
->x_base
= (caddr_t
)m
;
275 xdrs
->x_handy
= (int)MBLKL(m
);
278 xdrs
->x_handy
-= len
;
281 if (len
< XDRMBLK_BCOPY_LIMIT
) {
282 for (i
= 0; i
< len
; i
++)
283 *addr
++ = *m
->b_rptr
++;
285 bcopy(m
->b_rptr
, addr
, len
);
293 * Sort of like getbytes except that instead of getting bytes we return the
294 * mblk chain which contains the data. If the data ends in the middle of
295 * an mblk, the mblk is dup'd and split, so that the data will end on an
296 * mblk. Note that it is up to the caller to keep track of the data length
297 * and not walk too far down the mblk chain.
301 xdrmblk_getmblk(XDR
*xdrs
, mblk_t
**mm
, uint_t
*lenp
)
304 struct xdrmblk_params
*p
;
308 if (!xdrmblk_getint32(xdrs
, (int32_t *)&llen
))
312 /* LINTED pointer alignment */
313 m
= (mblk_t
*)xdrs
->x_base
;
317 * Walk the mblk chain until we get to the end or we've gathered
321 llen
= roundup(llen
, BYTES_PER_XDR_UNIT
);
322 while (m
!= NULL
&& len
+ (int)MBLKL(m
) <= llen
) {
323 len
+= (int)MBLKL(m
);
330 int tail_bytes
= llen
- len
;
333 * Split the mblk with the last chunk of data and
334 * insert it into the chain. The new mblk goes
335 * after the existing one so that it will get freed
341 nextm
->b_cont
= m
->b_cont
;
343 m
->b_wptr
= m
->b_rptr
+ tail_bytes
;
344 nextm
->b_rptr
+= tail_bytes
;
345 ASSERT(nextm
->b_rptr
!= nextm
->b_wptr
);
347 m
= nextm
; /* for x_base */
350 xdrs
->x_base
= (caddr_t
)m
;
351 xdrs
->x_handy
= m
!= NULL
? MBLKL(m
) : 0;
353 p
= (struct xdrmblk_params
*)xdrs
->x_private
;
354 p
->apos
+= p
->rpos
+ llen
;
361 xdrmblk_putbytes(XDR
*xdrs
, caddr_t addr
, int len
)
364 struct xdrmblk_params
*p
;
367 /* LINTED pointer alignment */
368 m
= (mblk_t
*)xdrs
->x_base
;
372 p
= (struct xdrmblk_params
*)xdrs
->x_private
;
375 * Performance tweak: converted explicit bcopy()
376 * call to simple in-line. This function is called
377 * to process things like readdir reply filenames
378 * which are small strings--typically 12 bytes or less.
379 * Overhead of calling bcopy() is obnoxious for such
382 while (xdrs
->x_handy
< len
) {
383 if (xdrs
->x_handy
> 0) {
384 if (xdrs
->x_handy
< XDRMBLK_BCOPY_LIMIT
) {
385 for (i
= 0; i
< xdrs
->x_handy
; i
++)
386 *m
->b_wptr
++ = *addr
++;
388 bcopy(addr
, m
->b_wptr
, xdrs
->x_handy
);
389 m
->b_wptr
+= xdrs
->x_handy
;
390 addr
+= xdrs
->x_handy
;
392 len
-= xdrs
->x_handy
;
393 p
->rpos
+= xdrs
->x_handy
;
397 * We don't have enough space, so allocate the
398 * amount we need, or sz, whichever is larger.
399 * It is better to let the underlying transport divide
400 * large chunks than to try and guess what is best.
402 if (m
->b_cont
== NULL
)
403 m
->b_cont
= xdrmblk_alloc(MAX(len
, p
->sz
));
406 xdrs
->x_base
= (caddr_t
)m
;
413 xdrs
->x_handy
= (int)MBLKTAIL(m
);
414 ASSERT(m
->b_rptr
== m
->b_wptr
);
415 ASSERT(m
->b_rptr
>= m
->b_datap
->db_base
);
416 ASSERT(m
->b_rptr
< m
->b_datap
->db_lim
);
419 xdrs
->x_handy
-= len
;
422 if (len
< XDRMBLK_BCOPY_LIMIT
) {
423 for (i
= 0; i
< len
; i
++)
424 *m
->b_wptr
++ = *addr
++;
426 bcopy(addr
, m
->b_wptr
, len
);
429 ASSERT(m
->b_wptr
<= m
->b_datap
->db_lim
);
434 * We avoid a copy by merely adding this mblk to the list. The caller is
435 * responsible for allocating and filling in the mblk. If len is
436 * not a multiple of BYTES_PER_XDR_UNIT, the caller has the option
437 * of making the data a BYTES_PER_XDR_UNIT multiple (b_wptr - b_rptr is
438 * a BYTES_PER_XDR_UNIT multiple), but in this case the caller has to ensure
439 * that the filler bytes are initialized to zero.
442 xdrmblk_putmblk(XDR
*xdrs
, mblk_t
*m
, uint_t len
)
444 int32_t llen
= (int32_t)len
;
446 if (!xdrmblk_putint32(xdrs
, &llen
))
449 return (xdrmblk_putmblk_raw(xdrs
, m
));
453 * The raw version of putmblk does not prepend the added data with the length.
456 xdrmblk_putmblk_raw(XDR
*xdrs
, mblk_t
*m
)
458 struct xdrmblk_params
*p
;
460 if ((DLEN(m
) % BYTES_PER_XDR_UNIT
) != 0)
463 p
= (struct xdrmblk_params
*)xdrs
->x_private
;
465 /* LINTED pointer alignment */
466 ((mblk_t
*)xdrs
->x_base
)->b_cont
= m
;
469 /* base points to the last mblk */
474 xdrs
->x_base
= (caddr_t
)m
;
481 xdrmblk_getpos(XDR
*xdrs
)
483 struct xdrmblk_params
*p
= (struct xdrmblk_params
*)xdrs
->x_private
;
485 return (p
->apos
+ p
->rpos
);
489 xdrmblk_setpos(XDR
*xdrs
, uint_t pos
)
492 struct xdrmblk_params
*p
;
494 p
= (struct xdrmblk_params
*)xdrs
->x_private
;
499 if (pos
> p
->apos
+ p
->rpos
+ xdrs
->x_handy
)
502 if (pos
== p
->apos
+ p
->rpos
)
505 /* LINTED pointer alignment */
506 m
= (mblk_t
*)xdrs
->x_base
;
509 if (xdrs
->x_op
== XDR_DECODE
)
510 m
->b_rptr
= m
->b_rptr
- p
->rpos
+ (pos
- p
->apos
);
512 m
->b_wptr
= m
->b_wptr
- p
->rpos
+ (pos
- p
->apos
);
514 xdrs
->x_handy
= p
->rpos
+ xdrs
->x_handy
- (pos
- p
->apos
);
515 p
->rpos
= pos
- p
->apos
;
521 static int xdrmblk_inline_hits
= 0;
522 static int xdrmblk_inline_misses
= 0;
523 static int do_xdrmblk_inline
= 1;
526 static rpc_inline_t
*
527 xdrmblk_inline(XDR
*xdrs
, int len
)
531 unsigned char **mptr
;
532 struct xdrmblk_params
*p
;
535 * Can't inline XDR_FREE calls, doesn't make sense.
537 if (xdrs
->x_op
== XDR_FREE
)
541 if (!do_xdrmblk_inline
) {
542 xdrmblk_inline_misses
++;
547 if (xdrs
->x_op
== XDR_DECODE
)
548 xdrmblk_skip_fully_read_mblks(xdrs
);
551 * Can't inline if there isn't enough room.
553 if (len
<= 0 || xdrs
->x_handy
< len
) {
555 xdrmblk_inline_misses
++;
560 /* LINTED pointer alignment */
561 m
= (mblk_t
*)xdrs
->x_base
;
564 if (xdrs
->x_op
== XDR_DECODE
) {
565 /* LINTED pointer alignment */
568 /* LINTED pointer alignment */
573 * Can't inline if the buffer is not 4 byte aligned, or if there is
574 * more than one reference to the data block associated with this mblk.
575 * This last check is used because the caller may want to modify the
576 * data in the inlined portion and someone else is holding a reference
577 * to the data who may not want it to be modified.
579 if (!IS_P2ALIGNED(*mptr
, sizeof (int32_t)) ||
580 m
->b_datap
->db_ref
!= 1) {
582 xdrmblk_inline_misses
++;
587 buf
= (rpc_inline_t
*)*mptr
;
589 p
= (struct xdrmblk_params
*)xdrs
->x_private
;
592 xdrs
->x_handy
-= len
;
596 xdrmblk_inline_hits
++;
603 xdrmblk_control(XDR
*xdrs
, int request
, void *info
)
606 struct xdrmblk_params
*p
;
612 xdrmblk_skip_fully_read_mblks(xdrs
);
615 * Return the next 4 byte unit in the XDR stream.
617 if (xdrs
->x_handy
< sizeof (int32_t))
620 /* LINTED pointer alignment */
621 m
= (mblk_t
*)xdrs
->x_base
;
625 * If the pointer is not aligned, fail the peek
627 if (!IS_P2ALIGNED(m
->b_rptr
, sizeof (int32_t)))
630 int32p
= (int32_t *)info
;
631 /* LINTED pointer alignment */
632 *int32p
= ntohl(*((int32_t *)(m
->b_rptr
)));
636 int32p
= (int32_t *)info
;
637 len
= RNDUP((int)(*int32p
));
643 /* LINTED pointer alignment */
644 m
= (mblk_t
*)xdrs
->x_base
;
648 p
= (struct xdrmblk_params
*)xdrs
->x_private
;
650 while (xdrs
->x_handy
< len
) {
651 if (xdrs
->x_handy
> 0) {
652 m
->b_rptr
+= xdrs
->x_handy
;
653 len
-= xdrs
->x_handy
;
654 p
->rpos
+= xdrs
->x_handy
;
657 xdrs
->x_base
= (caddr_t
)m
;
664 xdrs
->x_handy
= (int)MBLKL(m
);
667 xdrs
->x_handy
-= len
;
677 #define HDR_SPACE 128
680 xdrmblk_alloc(int sz
)
688 * Pad the front of the message to allow the lower networking
689 * layers space to add headers as needed.
693 while ((mp
= allocb(sz
, BPRI_LO
)) == NULL
) {
694 if (strwaitbuf(sz
, BPRI_LO
))
698 mp
->b_wptr
+= HDR_SPACE
;
699 mp
->b_rptr
= mp
->b_wptr
;
705 * Skip fully read or empty mblks
708 xdrmblk_skip_fully_read_mblks(XDR
*xdrs
)
711 struct xdrmblk_params
*p
;
713 if (xdrs
->x_handy
!= 0)
716 /* LINTED pointer alignment */
717 m
= (mblk_t
*)xdrs
->x_base
;
721 p
= (struct xdrmblk_params
*)xdrs
->x_private
;
730 xdrs
->x_handy
= (int)MBLKL(m
);
731 } while (xdrs
->x_handy
== 0);
733 xdrs
->x_base
= (caddr_t
)m
;