xref: /illumos-gate/usr/src/uts/common/rpc/xdr_mblk.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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 /*
23  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 /*
27  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
32 /*	  All Rights Reserved  	*/
33 
34 /*
35  * Portions of this source code were derived from Berkeley 4.3 BSD
36  * under license from the Regents of the University of California.
37  */
38 
39 /*
40  * xdr_mblk.c, XDR implementation on kernel streams mblks.
41  */
42 
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>
52 
53 #include <rpc/types.h>
54 #include <rpc/xdr.h>
55 
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 *);
65 
66 static mblk_t *xdrmblk_alloc(int);
67 static void xdrmblk_skip_fully_read_mblks(XDR *);
68 
69 /*
70  * Xdr on mblks operations vector.
71  */
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
82 };
83 
84 /*
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().
89  *
90  * The apos and rpos members of the xdrmblk_params structure are used to
91  * implement xdrmblk_getpos() and xdrmblk_setpos().
92  *
93  * In addition to the xdrmblk_params structure we store some additional
94  * internal data directly in the XDR stream structure:
95  *
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.
100  */
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 */
105 };
106 
107 /*
108  * Initialize xdr stream.
109  */
110 void
111 xdrmblk_init(XDR *xdrs, mblk_t *m, enum xdr_op op, int sz)
112 {
113 	struct xdrmblk_params *p;
114 
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;
121 
122 	p->sz = sz;
123 	p->apos = 0;
124 	p->rpos = 0;
125 
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);
132 	}
133 }
134 
135 static void
136 xdrmblk_destroy(XDR *xdrs)
137 {
138 	kmem_free(xdrs->x_private, sizeof (struct xdrmblk_params));
139 }
140 
141 static bool_t
142 xdrmblk_getint32(XDR *xdrs, int32_t *int32p)
143 {
144 	mblk_t *m;
145 	struct xdrmblk_params *p;
146 
147 	xdrmblk_skip_fully_read_mblks(xdrs);
148 
149 	/* LINTED pointer alignment */
150 	m = (mblk_t *)xdrs->x_base;
151 	if (m == NULL)
152 		return (FALSE);
153 
154 	p = (struct xdrmblk_params *)xdrs->x_private;
155 
156 	/*
157 	 * If the pointer is not aligned or there is not
158 	 * enough bytes, pullupmsg to get enough bytes and
159 	 * align the mblk.
160 	 */
161 	if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)) ||
162 	    xdrs->x_handy < sizeof (int32_t)) {
163 		while (!pullupmsg(m, sizeof (int32_t))) {
164 			/*
165 			 * Could have failed due to not
166 			 * enough data or an allocb failure.
167 			 */
168 			if (xmsgsize(m) < sizeof (int32_t))
169 				return (FALSE);
170 			delay(hz);
171 		}
172 		p->apos += p->rpos;
173 		p->rpos = 0;
174 		xdrs->x_handy = (int)MBLKL(m);
175 	}
176 
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);
182 
183 	return (TRUE);
184 }
185 
186 static bool_t
187 xdrmblk_putint32(XDR *xdrs, int32_t *int32p)
188 {
189 	mblk_t *m;
190 	struct xdrmblk_params *p;
191 
192 	/* LINTED pointer alignment */
193 	m = (mblk_t *)xdrs->x_base;
194 	if (m == NULL)
195 		return (FALSE);
196 
197 	p = (struct xdrmblk_params *)xdrs->x_private;
198 
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);
204 		}
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);
212 		}
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);
217 	}
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);
225 }
226 
227 /*
228  * We pick 16 as a compromise threshold for most architectures.
229  */
230 #define	XDRMBLK_BCOPY_LIMIT	16
231 
232 static bool_t
233 xdrmblk_getbytes(XDR *xdrs, caddr_t addr, int len)
234 {
235 	mblk_t *m;
236 	struct xdrmblk_params *p;
237 	int i;
238 
239 	/* LINTED pointer alignment */
240 	m = (mblk_t *)xdrs->x_base;
241 	if (m == NULL)
242 		return (FALSE);
243 
244 	p = (struct xdrmblk_params *)xdrs->x_private;
245 
246 	/*
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.
253 	 */
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;
263 			}
264 			len -= xdrs->x_handy;
265 			p->rpos += xdrs->x_handy;
266 		}
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);
274 		}
275 		xdrs->x_handy = (int)MBLKL(m);
276 	}
277 
278 	xdrs->x_handy -= len;
279 	p->rpos += len;
280 
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;
287 	}
288 
289 	return (TRUE);
290 }
291 
292 /*
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.
298  */
299 
300 bool_t
301 xdrmblk_getmblk(XDR *xdrs, mblk_t **mm, uint_t *lenp)
302 {
303 	mblk_t *m, *nextm;
304 	struct xdrmblk_params *p;
305 	int len;
306 	uint32_t llen;
307 
308 	if (!xdrmblk_getint32(xdrs, (int32_t *)&llen))
309 		return (FALSE);
310 
311 	*lenp = llen;
312 	/* LINTED pointer alignment */
313 	m = (mblk_t *)xdrs->x_base;
314 	*mm = m;
315 
316 	/*
317 	 * Walk the mblk chain until we get to the end or we've gathered
318 	 * enough data.
319 	 */
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;
325 	}
326 	if (len < llen) {
327 		if (m == NULL) {
328 			return (FALSE);
329 		} else {
330 			int tail_bytes = llen - len;
331 
332 			/*
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.
337 			 */
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);
346 
347 			m = nextm;	/* for x_base */
348 		}
349 	}
350 	xdrs->x_base = (caddr_t)m;
351 	xdrs->x_handy = m != NULL ? MBLKL(m) : 0;
352 
353 	p = (struct xdrmblk_params *)xdrs->x_private;
354 	p->apos += p->rpos + llen;
355 	p->rpos = 0;
356 
357 	return (TRUE);
358 }
359 
360 static bool_t
361 xdrmblk_putbytes(XDR *xdrs, caddr_t addr, int len)
362 {
363 	mblk_t *m;
364 	struct xdrmblk_params *p;
365 	int i;
366 
367 	/* LINTED pointer alignment */
368 	m = (mblk_t *)xdrs->x_base;
369 	if (m == NULL)
370 		return (FALSE);
371 
372 	p = (struct xdrmblk_params *)xdrs->x_private;
373 
374 	/*
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.
381 	 */
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;
391 			}
392 			len -= xdrs->x_handy;
393 			p->rpos += xdrs->x_handy;
394 		}
395 
396 		/*
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.
401 		 */
402 		if (m->b_cont == NULL)
403 			m->b_cont = xdrmblk_alloc(MAX(len, p->sz));
404 
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);
412 		}
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);
417 	}
418 
419 	xdrs->x_handy -= len;
420 	p->rpos += len;
421 
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;
428 	}
429 	ASSERT(m->b_wptr <= m->b_datap->db_lim);
430 	return (TRUE);
431 }
432 
433 /*
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.
440  */
441 bool_t
442 xdrmblk_putmblk(XDR *xdrs, mblk_t *m, uint_t len)
443 {
444 	int32_t llen = (int32_t)len;
445 
446 	if (!xdrmblk_putint32(xdrs, &llen))
447 		return (FALSE);
448 
449 	return (xdrmblk_putmblk_raw(xdrs, m));
450 }
451 
452 /*
453  * The raw version of putmblk does not prepend the added data with the length.
454  */
455 bool_t
456 xdrmblk_putmblk_raw(XDR *xdrs, mblk_t *m)
457 {
458 	struct xdrmblk_params *p;
459 
460 	if ((DLEN(m) % BYTES_PER_XDR_UNIT) != 0)
461 		return (FALSE);
462 
463 	p = (struct xdrmblk_params *)xdrs->x_private;
464 
465 	/* LINTED pointer alignment */
466 	((mblk_t *)xdrs->x_base)->b_cont = m;
467 	p->apos += p->rpos;
468 
469 	/* base points to the last mblk */
470 	while (m->b_cont) {
471 		p->apos += MBLKL(m);
472 		m = m->b_cont;
473 	}
474 	xdrs->x_base = (caddr_t)m;
475 	xdrs->x_handy = 0;
476 	p->rpos = MBLKL(m);
477 	return (TRUE);
478 }
479 
480 static uint_t
481 xdrmblk_getpos(XDR *xdrs)
482 {
483 	struct xdrmblk_params *p = (struct xdrmblk_params *)xdrs->x_private;
484 
485 	return (p->apos + p->rpos);
486 }
487 
488 static bool_t
489 xdrmblk_setpos(XDR *xdrs, uint_t pos)
490 {
491 	mblk_t *m;
492 	struct xdrmblk_params *p;
493 
494 	p = (struct xdrmblk_params *)xdrs->x_private;
495 
496 	if (pos < p->apos)
497 		return (FALSE);
498 
499 	if (pos > p->apos + p->rpos + xdrs->x_handy)
500 		return (FALSE);
501 
502 	if (pos == p->apos + p->rpos)
503 		return (TRUE);
504 
505 	/* LINTED pointer alignment */
506 	m = (mblk_t *)xdrs->x_base;
507 	ASSERT(m != NULL);
508 
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);
513 
514 	xdrs->x_handy = p->rpos + xdrs->x_handy - (pos - p->apos);
515 	p->rpos = pos - p->apos;
516 
517 	return (TRUE);
518 }
519 
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
525 
526 static rpc_inline_t *
527 xdrmblk_inline(XDR *xdrs, int len)
528 {
529 	rpc_inline_t *buf;
530 	mblk_t *m;
531 	unsigned char **mptr;
532 	struct xdrmblk_params *p;
533 
534 	/*
535 	 * Can't inline XDR_FREE calls, doesn't make sense.
536 	 */
537 	if (xdrs->x_op == XDR_FREE)
538 		return (NULL);
539 
540 #ifdef DEBUG
541 	if (!do_xdrmblk_inline) {
542 		xdrmblk_inline_misses++;
543 		return (NULL);
544 	}
545 #endif
546 
547 	if (xdrs->x_op == XDR_DECODE)
548 		xdrmblk_skip_fully_read_mblks(xdrs);
549 
550 	/*
551 	 * Can't inline if there isn't enough room.
552 	 */
553 	if (len <= 0 || xdrs->x_handy < len) {
554 #ifdef DEBUG
555 		xdrmblk_inline_misses++;
556 #endif
557 		return (NULL);
558 	}
559 
560 	/* LINTED pointer alignment */
561 	m = (mblk_t *)xdrs->x_base;
562 	ASSERT(m != NULL);
563 
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;
570 	}
571 
572 	/*
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.
578 	 */
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);
585 	}
586 
587 	buf = (rpc_inline_t *)*mptr;
588 
589 	p = (struct xdrmblk_params *)xdrs->x_private;
590 
591 	*mptr += len;
592 	xdrs->x_handy -= len;
593 	p->rpos += len;
594 
595 #ifdef DEBUG
596 	xdrmblk_inline_hits++;
597 #endif
598 
599 	return (buf);
600 }
601 
602 static bool_t
603 xdrmblk_control(XDR *xdrs, int request, void *info)
604 {
605 	mblk_t *m;
606 	struct xdrmblk_params *p;
607 	int32_t *int32p;
608 	int len;
609 
610 	switch (request) {
611 	case XDR_PEEK:
612 		xdrmblk_skip_fully_read_mblks(xdrs);
613 
614 		/*
615 		 * Return the next 4 byte unit in the XDR stream.
616 		 */
617 		if (xdrs->x_handy < sizeof (int32_t))
618 			return (FALSE);
619 
620 		/* LINTED pointer alignment */
621 		m = (mblk_t *)xdrs->x_base;
622 		ASSERT(m != NULL);
623 
624 		/*
625 		 * If the pointer is not aligned, fail the peek
626 		 */
627 		if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)))
628 			return (FALSE);
629 
630 		int32p = (int32_t *)info;
631 		/* LINTED pointer alignment */
632 		*int32p = ntohl(*((int32_t *)(m->b_rptr)));
633 		return (TRUE);
634 
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);
642 
643 		/* LINTED pointer alignment */
644 		m = (mblk_t *)xdrs->x_base;
645 		if (m == NULL)
646 			return (FALSE);
647 
648 		p = (struct xdrmblk_params *)xdrs->x_private;
649 
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;
655 			}
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);
663 			}
664 			xdrs->x_handy = (int)MBLKL(m);
665 		}
666 
667 		xdrs->x_handy -= len;
668 		p->rpos += len;
669 		m->b_rptr += len;
670 		return (TRUE);
671 
672 	default:
673 		return (FALSE);
674 	}
675 }
676 
677 #define	HDR_SPACE	128
678 
679 static mblk_t *
680 xdrmblk_alloc(int sz)
681 {
682 	mblk_t *mp;
683 
684 	if (sz == 0)
685 		return (NULL);
686 
687 	/*
688 	 * Pad the front of the message to allow the lower networking
689 	 * layers space to add headers as needed.
690 	 */
691 	sz += HDR_SPACE;
692 
693 	while ((mp = allocb(sz, BPRI_LO)) == NULL) {
694 		if (strwaitbuf(sz, BPRI_LO))
695 			return (NULL);
696 	}
697 
698 	mp->b_wptr += HDR_SPACE;
699 	mp->b_rptr = mp->b_wptr;
700 
701 	return (mp);
702 }
703 
704 /*
705  * Skip fully read or empty mblks
706  */
707 static void
708 xdrmblk_skip_fully_read_mblks(XDR *xdrs)
709 {
710 	mblk_t *m;
711 	struct xdrmblk_params *p;
712 
713 	if (xdrs->x_handy != 0)
714 		return;
715 
716 	/* LINTED pointer alignment */
717 	m = (mblk_t *)xdrs->x_base;
718 	if (m == NULL)
719 		return;
720 
721 	p = (struct xdrmblk_params *)xdrs->x_private;
722 	p->apos += p->rpos;
723 	p->rpos = 0;
724 
725 	do {
726 		m = m->b_cont;
727 		if (m == NULL)
728 			break;
729 
730 		xdrs->x_handy = (int)MBLKL(m);
731 	} while (xdrs->x_handy == 0);
732 
733 	xdrs->x_base = (caddr_t)m;
734 }
735