xref: /illumos-gate/usr/src/uts/common/rpc/xdr_mblk.c (revision aeac2d873b68a43f6650e0d0f021c02f5a653a21)
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 2015 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 	struct xdrmblk_params *p;
445 	int32_t llen = (int32_t)len;
446 
447 	if ((DLEN(m) % BYTES_PER_XDR_UNIT) != 0)
448 		return (FALSE);
449 	if (!xdrmblk_putint32(xdrs, &llen))
450 		return (FALSE);
451 
452 	p = (struct xdrmblk_params *)xdrs->x_private;
453 
454 	/* LINTED pointer alignment */
455 	((mblk_t *)xdrs->x_base)->b_cont = m;
456 	p->apos += p->rpos;
457 
458 	/* base points to the last mblk */
459 	while (m->b_cont) {
460 		p->apos += MBLKL(m);
461 		m = m->b_cont;
462 	}
463 	xdrs->x_base = (caddr_t)m;
464 	xdrs->x_handy = 0;
465 	p->rpos = MBLKL(m);
466 	return (TRUE);
467 }
468 
469 static uint_t
470 xdrmblk_getpos(XDR *xdrs)
471 {
472 	struct xdrmblk_params *p = (struct xdrmblk_params *)xdrs->x_private;
473 
474 	return (p->apos + p->rpos);
475 }
476 
477 static bool_t
478 xdrmblk_setpos(XDR *xdrs, uint_t pos)
479 {
480 	mblk_t *m;
481 	struct xdrmblk_params *p;
482 
483 	p = (struct xdrmblk_params *)xdrs->x_private;
484 
485 	if (pos < p->apos)
486 		return (FALSE);
487 
488 	if (pos > p->apos + p->rpos + xdrs->x_handy)
489 		return (FALSE);
490 
491 	if (pos == p->apos + p->rpos)
492 		return (TRUE);
493 
494 	/* LINTED pointer alignment */
495 	m = (mblk_t *)xdrs->x_base;
496 	ASSERT(m != NULL);
497 
498 	if (xdrs->x_op == XDR_DECODE)
499 		m->b_rptr = m->b_rptr - p->rpos + (pos - p->apos);
500 	else
501 		m->b_wptr = m->b_wptr - p->rpos + (pos - p->apos);
502 
503 	xdrs->x_handy = p->rpos + xdrs->x_handy - (pos - p->apos);
504 	p->rpos = pos - p->apos;
505 
506 	return (TRUE);
507 }
508 
509 #ifdef DEBUG
510 static int xdrmblk_inline_hits = 0;
511 static int xdrmblk_inline_misses = 0;
512 static int do_xdrmblk_inline = 1;
513 #endif
514 
515 static rpc_inline_t *
516 xdrmblk_inline(XDR *xdrs, int len)
517 {
518 	rpc_inline_t *buf;
519 	mblk_t *m;
520 	unsigned char **mptr;
521 	struct xdrmblk_params *p;
522 
523 	/*
524 	 * Can't inline XDR_FREE calls, doesn't make sense.
525 	 */
526 	if (xdrs->x_op == XDR_FREE)
527 		return (NULL);
528 
529 #ifdef DEBUG
530 	if (!do_xdrmblk_inline) {
531 		xdrmblk_inline_misses++;
532 		return (NULL);
533 	}
534 #endif
535 
536 	if (xdrs->x_op == XDR_DECODE)
537 		xdrmblk_skip_fully_read_mblks(xdrs);
538 
539 	/*
540 	 * Can't inline if there isn't enough room.
541 	 */
542 	if (len <= 0 || xdrs->x_handy < len) {
543 #ifdef DEBUG
544 		xdrmblk_inline_misses++;
545 #endif
546 		return (NULL);
547 	}
548 
549 	/* LINTED pointer alignment */
550 	m = (mblk_t *)xdrs->x_base;
551 	ASSERT(m != NULL);
552 
553 	if (xdrs->x_op == XDR_DECODE) {
554 		/* LINTED pointer alignment */
555 		mptr = &m->b_rptr;
556 	} else {
557 		/* LINTED pointer alignment */
558 		mptr = &m->b_wptr;
559 	}
560 
561 	/*
562 	 * Can't inline if the buffer is not 4 byte aligned, or if there is
563 	 * more than one reference to the data block associated with this mblk.
564 	 * This last check is used because the caller may want to modify the
565 	 * data in the inlined portion and someone else is holding a reference
566 	 * to the data who may not want it to be modified.
567 	 */
568 	if (!IS_P2ALIGNED(*mptr, sizeof (int32_t)) ||
569 	    m->b_datap->db_ref != 1) {
570 #ifdef DEBUG
571 		xdrmblk_inline_misses++;
572 #endif
573 		return (NULL);
574 	}
575 
576 	buf = (rpc_inline_t *)*mptr;
577 
578 	p = (struct xdrmblk_params *)xdrs->x_private;
579 
580 	*mptr += len;
581 	xdrs->x_handy -= len;
582 	p->rpos += len;
583 
584 #ifdef DEBUG
585 	xdrmblk_inline_hits++;
586 #endif
587 
588 	return (buf);
589 }
590 
591 static bool_t
592 xdrmblk_control(XDR *xdrs, int request, void *info)
593 {
594 	mblk_t *m;
595 	struct xdrmblk_params *p;
596 	int32_t *int32p;
597 	int len;
598 
599 	switch (request) {
600 	case XDR_PEEK:
601 		xdrmblk_skip_fully_read_mblks(xdrs);
602 
603 		/*
604 		 * Return the next 4 byte unit in the XDR stream.
605 		 */
606 		if (xdrs->x_handy < sizeof (int32_t))
607 			return (FALSE);
608 
609 		/* LINTED pointer alignment */
610 		m = (mblk_t *)xdrs->x_base;
611 		ASSERT(m != NULL);
612 
613 		/*
614 		 * If the pointer is not aligned, fail the peek
615 		 */
616 		if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)))
617 			return (FALSE);
618 
619 		int32p = (int32_t *)info;
620 		/* LINTED pointer alignment */
621 		*int32p = ntohl(*((int32_t *)(m->b_rptr)));
622 		return (TRUE);
623 
624 	case XDR_SKIPBYTES:
625 		int32p = (int32_t *)info;
626 		len = RNDUP((int)(*int32p));
627 		if (len < 0)
628 			return (FALSE);
629 		if (len == 0)
630 			return (TRUE);
631 
632 		/* LINTED pointer alignment */
633 		m = (mblk_t *)xdrs->x_base;
634 		if (m == NULL)
635 			return (FALSE);
636 
637 		p = (struct xdrmblk_params *)xdrs->x_private;
638 
639 		while (xdrs->x_handy < len) {
640 			if (xdrs->x_handy > 0) {
641 				m->b_rptr += xdrs->x_handy;
642 				len -= xdrs->x_handy;
643 				p->rpos += xdrs->x_handy;
644 			}
645 			m = m->b_cont;
646 			xdrs->x_base = (caddr_t)m;
647 			p->apos += p->rpos;
648 			p->rpos = 0;
649 			if (m == NULL) {
650 				xdrs->x_handy = 0;
651 				return (FALSE);
652 			}
653 			xdrs->x_handy = (int)MBLKL(m);
654 		}
655 
656 		xdrs->x_handy -= len;
657 		p->rpos += len;
658 		m->b_rptr += len;
659 		return (TRUE);
660 
661 	default:
662 		return (FALSE);
663 	}
664 }
665 
666 #define	HDR_SPACE	128
667 
668 static mblk_t *
669 xdrmblk_alloc(int sz)
670 {
671 	mblk_t *mp;
672 
673 	if (sz == 0)
674 		return (NULL);
675 
676 	/*
677 	 * Pad the front of the message to allow the lower networking
678 	 * layers space to add headers as needed.
679 	 */
680 	sz += HDR_SPACE;
681 
682 	while ((mp = allocb(sz, BPRI_LO)) == NULL) {
683 		if (strwaitbuf(sz, BPRI_LO))
684 			return (NULL);
685 	}
686 
687 	mp->b_wptr += HDR_SPACE;
688 	mp->b_rptr = mp->b_wptr;
689 
690 	return (mp);
691 }
692 
693 /*
694  * Skip fully read or empty mblks
695  */
696 static void
697 xdrmblk_skip_fully_read_mblks(XDR *xdrs)
698 {
699 	mblk_t *m;
700 	struct xdrmblk_params *p;
701 
702 	if (xdrs->x_handy != 0)
703 		return;
704 
705 	/* LINTED pointer alignment */
706 	m = (mblk_t *)xdrs->x_base;
707 	if (m == NULL)
708 		return;
709 
710 	p = (struct xdrmblk_params *)xdrs->x_private;
711 	p->apos += p->rpos;
712 	p->rpos = 0;
713 
714 	do {
715 		m = m->b_cont;
716 		if (m == NULL)
717 			break;
718 
719 		xdrs->x_handy = (int)MBLKL(m);
720 	} while (xdrs->x_handy == 0);
721 
722 	xdrs->x_base = (caddr_t)m;
723 }
724