dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / uts / common / rpc / xdr_mblk.c
blobf61b5d0c494217cd57f8e50a5f5286b1c7a118c7
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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>
54 #include <rpc/xdr.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 = {
73 xdrmblk_getbytes,
74 xdrmblk_putbytes,
75 xdrmblk_getpos,
76 xdrmblk_setpos,
77 xdrmblk_inline,
78 xdrmblk_destroy,
79 xdrmblk_control,
80 xdrmblk_getint32,
81 xdrmblk_putint32
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
97 * working with).
98 * x_handy The number of available bytes (either for read or for write) in
99 * the current mblk.
101 struct xdrmblk_params {
102 int sz;
103 uint_t apos; /* Absolute position of the current mblk */
104 uint_t rpos; /* Relative position in the current mblk */
108 * Initialize xdr stream.
110 void
111 xdrmblk_init(XDR *xdrs, mblk_t *m, enum xdr_op op, int sz)
113 struct xdrmblk_params *p;
115 xdrs->x_op = op;
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;
122 p->sz = sz;
123 p->apos = 0;
124 p->rpos = 0;
126 if (op == XDR_DECODE) {
127 xdrs->x_handy = (int)MBLKL(m);
128 } else {
129 xdrs->x_handy = (int)MBLKTAIL(m);
130 if (p->sz < sizeof (int32_t))
131 p->sz = sizeof (int32_t);
135 static void
136 xdrmblk_destroy(XDR *xdrs)
138 kmem_free(xdrs->x_private, sizeof (struct xdrmblk_params));
141 static bool_t
142 xdrmblk_getint32(XDR *xdrs, int32_t *int32p)
144 mblk_t *m;
145 struct xdrmblk_params *p;
147 xdrmblk_skip_fully_read_mblks(xdrs);
149 /* LINTED pointer alignment */
150 m = (mblk_t *)xdrs->x_base;
151 if (m == NULL)
152 return (FALSE);
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
159 * align the mblk.
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))
169 return (FALSE);
170 ddi_sleep(1);
172 p->apos += p->rpos;
173 p->rpos = 0;
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);
183 return (TRUE);
186 static bool_t
187 xdrmblk_putint32(XDR *xdrs, int32_t *int32p)
189 mblk_t *m;
190 struct xdrmblk_params *p;
192 /* LINTED pointer alignment */
193 m = (mblk_t *)xdrs->x_base;
194 if (m == NULL)
195 return (FALSE);
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);
205 m = m->b_cont;
206 xdrs->x_base = (caddr_t)m;
207 p->apos += p->rpos;
208 p->rpos = 0;
209 if (m == NULL) {
210 xdrs->x_handy = 0;
211 return (FALSE);
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);
224 return (TRUE);
228 * We pick 16 as a compromise threshold for most architectures.
230 #define XDRMBLK_BCOPY_LIMIT 16
232 static bool_t
233 xdrmblk_getbytes(XDR *xdrs, caddr_t addr, int len)
235 mblk_t *m;
236 struct xdrmblk_params *p;
237 int i;
239 /* LINTED pointer alignment */
240 m = (mblk_t *)xdrs->x_base;
241 if (m == NULL)
242 return (FALSE);
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
252 * small copies.
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++;
259 } else {
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;
267 m = m->b_cont;
268 xdrs->x_base = (caddr_t)m;
269 p->apos += p->rpos;
270 p->rpos = 0;
271 if (m == NULL) {
272 xdrs->x_handy = 0;
273 return (FALSE);
275 xdrs->x_handy = (int)MBLKL(m);
278 xdrs->x_handy -= len;
279 p->rpos += len;
281 if (len < XDRMBLK_BCOPY_LIMIT) {
282 for (i = 0; i < len; i++)
283 *addr++ = *m->b_rptr++;
284 } else {
285 bcopy(m->b_rptr, addr, len);
286 m->b_rptr += len;
289 return (TRUE);
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.
300 bool_t
301 xdrmblk_getmblk(XDR *xdrs, mblk_t **mm, uint_t *lenp)
303 mblk_t *m, *nextm;
304 struct xdrmblk_params *p;
305 int len;
306 uint32_t llen;
308 if (!xdrmblk_getint32(xdrs, (int32_t *)&llen))
309 return (FALSE);
311 *lenp = llen;
312 /* LINTED pointer alignment */
313 m = (mblk_t *)xdrs->x_base;
314 *mm = m;
317 * Walk the mblk chain until we get to the end or we've gathered
318 * enough data.
320 len = 0;
321 llen = roundup(llen, BYTES_PER_XDR_UNIT);
322 while (m != NULL && len + (int)MBLKL(m) <= llen) {
323 len += (int)MBLKL(m);
324 m = m->b_cont;
326 if (len < llen) {
327 if (m == NULL) {
328 return (FALSE);
329 } else {
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
336 * properly.
338 nextm = dupb(m);
339 if (nextm == NULL)
340 return (FALSE);
341 nextm->b_cont = m->b_cont;
342 m->b_cont = nextm;
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;
355 p->rpos = 0;
357 return (TRUE);
360 static bool_t
361 xdrmblk_putbytes(XDR *xdrs, caddr_t addr, int len)
363 mblk_t *m;
364 struct xdrmblk_params *p;
365 int i;
367 /* LINTED pointer alignment */
368 m = (mblk_t *)xdrs->x_base;
369 if (m == NULL)
370 return (FALSE);
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
380 * small copies.
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++;
387 } else {
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));
405 m = m->b_cont;
406 xdrs->x_base = (caddr_t)m;
407 p->apos += p->rpos;
408 p->rpos = 0;
409 if (m == NULL) {
410 xdrs->x_handy = 0;
411 return (FALSE);
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;
420 p->rpos += len;
422 if (len < XDRMBLK_BCOPY_LIMIT) {
423 for (i = 0; i < len; i++)
424 *m->b_wptr++ = *addr++;
425 } else {
426 bcopy(addr, m->b_wptr, len);
427 m->b_wptr += len;
429 ASSERT(m->b_wptr <= m->b_datap->db_lim);
430 return (TRUE);
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.
441 bool_t
442 xdrmblk_putmblk(XDR *xdrs, mblk_t *m, uint_t len)
444 int32_t llen = (int32_t)len;
446 if (!xdrmblk_putint32(xdrs, &llen))
447 return (FALSE);
449 return (xdrmblk_putmblk_raw(xdrs, m));
453 * The raw version of putmblk does not prepend the added data with the length.
455 bool_t
456 xdrmblk_putmblk_raw(XDR *xdrs, mblk_t *m)
458 struct xdrmblk_params *p;
460 if ((DLEN(m) % BYTES_PER_XDR_UNIT) != 0)
461 return (FALSE);
463 p = (struct xdrmblk_params *)xdrs->x_private;
465 /* LINTED pointer alignment */
466 ((mblk_t *)xdrs->x_base)->b_cont = m;
467 p->apos += p->rpos;
469 /* base points to the last mblk */
470 while (m->b_cont) {
471 p->apos += MBLKL(m);
472 m = m->b_cont;
474 xdrs->x_base = (caddr_t)m;
475 xdrs->x_handy = 0;
476 p->rpos = MBLKL(m);
477 return (TRUE);
480 static uint_t
481 xdrmblk_getpos(XDR *xdrs)
483 struct xdrmblk_params *p = (struct xdrmblk_params *)xdrs->x_private;
485 return (p->apos + p->rpos);
488 static bool_t
489 xdrmblk_setpos(XDR *xdrs, uint_t pos)
491 mblk_t *m;
492 struct xdrmblk_params *p;
494 p = (struct xdrmblk_params *)xdrs->x_private;
496 if (pos < p->apos)
497 return (FALSE);
499 if (pos > p->apos + p->rpos + xdrs->x_handy)
500 return (FALSE);
502 if (pos == p->apos + p->rpos)
503 return (TRUE);
505 /* LINTED pointer alignment */
506 m = (mblk_t *)xdrs->x_base;
507 ASSERT(m != NULL);
509 if (xdrs->x_op == XDR_DECODE)
510 m->b_rptr = m->b_rptr - p->rpos + (pos - p->apos);
511 else
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;
517 return (TRUE);
520 #ifdef DEBUG
521 static int xdrmblk_inline_hits = 0;
522 static int xdrmblk_inline_misses = 0;
523 static int do_xdrmblk_inline = 1;
524 #endif
526 static rpc_inline_t *
527 xdrmblk_inline(XDR *xdrs, int len)
529 rpc_inline_t *buf;
530 mblk_t *m;
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)
538 return (NULL);
540 #ifdef DEBUG
541 if (!do_xdrmblk_inline) {
542 xdrmblk_inline_misses++;
543 return (NULL);
545 #endif
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) {
554 #ifdef DEBUG
555 xdrmblk_inline_misses++;
556 #endif
557 return (NULL);
560 /* LINTED pointer alignment */
561 m = (mblk_t *)xdrs->x_base;
562 ASSERT(m != NULL);
564 if (xdrs->x_op == XDR_DECODE) {
565 /* LINTED pointer alignment */
566 mptr = &m->b_rptr;
567 } else {
568 /* LINTED pointer alignment */
569 mptr = &m->b_wptr;
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) {
581 #ifdef DEBUG
582 xdrmblk_inline_misses++;
583 #endif
584 return (NULL);
587 buf = (rpc_inline_t *)*mptr;
589 p = (struct xdrmblk_params *)xdrs->x_private;
591 *mptr += len;
592 xdrs->x_handy -= len;
593 p->rpos += len;
595 #ifdef DEBUG
596 xdrmblk_inline_hits++;
597 #endif
599 return (buf);
602 static bool_t
603 xdrmblk_control(XDR *xdrs, int request, void *info)
605 mblk_t *m;
606 struct xdrmblk_params *p;
607 int32_t *int32p;
608 int len;
610 switch (request) {
611 case XDR_PEEK:
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))
618 return (FALSE);
620 /* LINTED pointer alignment */
621 m = (mblk_t *)xdrs->x_base;
622 ASSERT(m != NULL);
625 * If the pointer is not aligned, fail the peek
627 if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)))
628 return (FALSE);
630 int32p = (int32_t *)info;
631 /* LINTED pointer alignment */
632 *int32p = ntohl(*((int32_t *)(m->b_rptr)));
633 return (TRUE);
635 case XDR_SKIPBYTES:
636 int32p = (int32_t *)info;
637 len = RNDUP((int)(*int32p));
638 if (len < 0)
639 return (FALSE);
640 if (len == 0)
641 return (TRUE);
643 /* LINTED pointer alignment */
644 m = (mblk_t *)xdrs->x_base;
645 if (m == NULL)
646 return (FALSE);
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;
656 m = m->b_cont;
657 xdrs->x_base = (caddr_t)m;
658 p->apos += p->rpos;
659 p->rpos = 0;
660 if (m == NULL) {
661 xdrs->x_handy = 0;
662 return (FALSE);
664 xdrs->x_handy = (int)MBLKL(m);
667 xdrs->x_handy -= len;
668 p->rpos += len;
669 m->b_rptr += len;
670 return (TRUE);
672 default:
673 return (FALSE);
677 #define HDR_SPACE 128
679 static mblk_t *
680 xdrmblk_alloc(int sz)
682 mblk_t *mp;
684 if (sz == 0)
685 return (NULL);
688 * Pad the front of the message to allow the lower networking
689 * layers space to add headers as needed.
691 sz += HDR_SPACE;
693 while ((mp = allocb(sz, BPRI_LO)) == NULL) {
694 if (strwaitbuf(sz, BPRI_LO))
695 return (NULL);
698 mp->b_wptr += HDR_SPACE;
699 mp->b_rptr = mp->b_wptr;
701 return (mp);
705 * Skip fully read or empty mblks
707 static void
708 xdrmblk_skip_fully_read_mblks(XDR *xdrs)
710 mblk_t *m;
711 struct xdrmblk_params *p;
713 if (xdrs->x_handy != 0)
714 return;
716 /* LINTED pointer alignment */
717 m = (mblk_t *)xdrs->x_base;
718 if (m == NULL)
719 return;
721 p = (struct xdrmblk_params *)xdrs->x_private;
722 p->apos += p->rpos;
723 p->rpos = 0;
725 do {
726 m = m->b_cont;
727 if (m == NULL)
728 break;
730 xdrs->x_handy = (int)MBLKL(m);
731 } while (xdrs->x_handy == 0);
733 xdrs->x_base = (caddr_t)m;