Home | History | Annotate | Download | only in rpc
      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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
     27 /*	  All Rights Reserved  	*/
     28 
     29 /*
     30  * Portions of this source code were derived from Berkeley 4.3 BSD
     31  * under license from the Regents of the University of California.
     32  */
     33 
     34 #pragma ident	"@(#)xdr_mblk.c	1.34	06/08/24 SMI"
     35 
     36 /*
     37  * xdr_mblk.c, XDR implementation on kernel streams mblks.
     38  */
     39 
     40 #include <sys/param.h>
     41 #include <sys/types.h>
     42 #include <sys/systm.h>
     43 #include <sys/stream.h>
     44 #include <sys/cmn_err.h>
     45 #include <sys/strsubr.h>
     46 #include <sys/strsun.h>
     47 #include <sys/debug.h>
     48 #include <sys/sysmacros.h>
     49 
     50 #include <rpc/types.h>
     51 #include <rpc/xdr.h>
     52 
     53 static bool_t	xdrmblk_getint32(XDR *, int32_t *);
     54 static bool_t	xdrmblk_putint32(XDR *, int32_t *);
     55 static bool_t	xdrmblk_getbytes(XDR *, caddr_t, int);
     56 static bool_t	xdrmblk_putbytes(XDR *, caddr_t, int);
     57 static uint_t	xdrmblk_getpos(XDR *);
     58 static bool_t	xdrmblk_setpos(XDR *, uint_t);
     59 static rpc_inline_t *xdrmblk_inline(XDR *, int);
     60 static void	xdrmblk_destroy(XDR *);
     61 static bool_t	xdrmblk_control(XDR *, int, void *);
     62 
     63 static mblk_t *xdrmblk_alloc(int);
     64 
     65 /*
     66  * Xdr on mblks operations vector.
     67  */
     68 struct	xdr_ops xdrmblk_ops = {
     69 	xdrmblk_getbytes,
     70 	xdrmblk_putbytes,
     71 	xdrmblk_getpos,
     72 	xdrmblk_setpos,
     73 	xdrmblk_inline,
     74 	xdrmblk_destroy,
     75 	xdrmblk_control,
     76 	xdrmblk_getint32,
     77 	xdrmblk_putint32
     78 };
     79 
     80 /*
     81  * Initialize xdr stream.
     82  */
     83 void
     84 xdrmblk_init(XDR *xdrs, mblk_t *m, enum xdr_op op, int sz)
     85 {
     86 	xdrs->x_op = op;
     87 	xdrs->x_ops = &xdrmblk_ops;
     88 	xdrs->x_base = (caddr_t)m;
     89 	xdrs->x_public = NULL;
     90 	xdrs->x_private = (caddr_t)(uintptr_t)sz;
     91 
     92 	if (op == XDR_DECODE)
     93 		xdrs->x_handy = (int)(m->b_wptr - m->b_rptr);
     94 	else
     95 		xdrs->x_handy = (int)(m->b_datap->db_lim - m->b_datap->db_base);
     96 }
     97 
     98 /* ARGSUSED */
     99 static void
    100 xdrmblk_destroy(XDR *xdrs)
    101 {
    102 }
    103 
    104 static bool_t
    105 xdrmblk_getint32(XDR *xdrs, int32_t *int32p)
    106 {
    107 	mblk_t *m;
    108 
    109 	/* LINTED pointer alignment */
    110 	m = (mblk_t *)xdrs->x_base;
    111 	if (m == NULL)
    112 		return (FALSE);
    113 	/*
    114 	 * If the pointer is not aligned or there is not
    115 	 * enough bytes, pullupmsg to get enough bytes and
    116 	 * align the mblk.
    117 	 */
    118 	if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)) ||
    119 			xdrs->x_handy < sizeof (int32_t)) {
    120 		while (!pullupmsg(m, sizeof (int32_t))) {
    121 			/*
    122 			 * Could have failed due to not
    123 			 * enough data or an allocb failure.
    124 			 */
    125 			if (xmsgsize(m) < sizeof (int32_t))
    126 				return (FALSE);
    127 			delay(hz);
    128 		}
    129 		xdrs->x_handy = (int)(m->b_wptr - m->b_rptr);
    130 	}
    131 
    132 	/* LINTED pointer alignment */
    133 	*int32p = ntohl(*((int32_t *)(m->b_rptr)));
    134 	m->b_rptr += sizeof (int32_t);
    135 
    136 	/*
    137 	 * Instead of leaving handy as 0 causing more pullupmsg's
    138 	 * simply move to the next mblk.
    139 	 */
    140 	if ((xdrs->x_handy -= sizeof (int32_t)) == 0) {
    141 		m = m->b_cont;
    142 		xdrs->x_base = (caddr_t)m;
    143 		if (m != NULL)
    144 			xdrs->x_handy = (int)(m->b_wptr - m->b_rptr);
    145 	}
    146 	return (TRUE);
    147 }
    148 
    149 static bool_t
    150 xdrmblk_putint32(XDR *xdrs, int32_t *int32p)
    151 {
    152 	mblk_t *m;
    153 
    154 	/* LINTED pointer alignment */
    155 	m = (mblk_t *)xdrs->x_base;
    156 	if (m == NULL)
    157 		return (FALSE);
    158 	if ((xdrs->x_handy -= (int)sizeof (int32_t)) < 0) {
    159 		if (m->b_cont == NULL) {
    160 			m->b_cont = xdrmblk_alloc((int)(uintptr_t)
    161 			    xdrs->x_private);
    162 		}
    163 		m = m->b_cont;
    164 		xdrs->x_base = (caddr_t)m;
    165 		if (m == NULL) {
    166 			xdrs->x_handy = 0;
    167 			return (FALSE);
    168 		}
    169 		xdrs->x_handy = (int)(m->b_datap->db_lim - m->b_rptr -
    170 			    sizeof (int32_t));
    171 		ASSERT(m->b_rptr == m->b_wptr);
    172 		ASSERT(m->b_rptr >= m->b_datap->db_base);
    173 		ASSERT(m->b_rptr < m->b_datap->db_lim);
    174 	}
    175 	/* LINTED pointer alignment */
    176 	*(int32_t *)m->b_wptr = htonl(*int32p);
    177 	m->b_wptr += sizeof (int32_t);
    178 	ASSERT(m->b_wptr <= m->b_datap->db_lim);
    179 	return (TRUE);
    180 }
    181 
    182 /*
    183  * We pick 16 as a compromise threshold for most architectures.
    184  */
    185 #define	XDRMBLK_BCOPY_LIMIT	16
    186 
    187 static bool_t
    188 xdrmblk_getbytes(XDR *xdrs, caddr_t addr, int len)
    189 {
    190 	mblk_t *m;
    191 	int i;
    192 
    193 	/* LINTED pointer alignment */
    194 	m = (mblk_t *)xdrs->x_base;
    195 	if (m == NULL)
    196 		return (FALSE);
    197 	/*
    198 	 * Performance tweak: converted explicit bcopy()
    199 	 * call to simple in-line. This function is called
    200 	 * to process things like readdir reply filenames
    201 	 * which are small strings--typically 12 bytes or less.
    202 	 * Overhead of calling bcopy() is obnoxious for such
    203 	 * small copies.
    204 	 */
    205 	while ((xdrs->x_handy -= len) < 0) {
    206 		if ((xdrs->x_handy += len) > 0) {
    207 			if (len < XDRMBLK_BCOPY_LIMIT) {
    208 				for (i = 0; i < xdrs->x_handy; i++)
    209 					*addr++ = *m->b_rptr++;
    210 			} else {
    211 				bcopy(m->b_rptr, addr, xdrs->x_handy);
    212 				m->b_rptr += xdrs->x_handy;
    213 				addr += xdrs->x_handy;
    214 			}
    215 			len -= xdrs->x_handy;
    216 		}
    217 		m = m->b_cont;
    218 		xdrs->x_base = (caddr_t)m;
    219 		if (m == NULL) {
    220 			xdrs->x_handy = 0;
    221 			return (FALSE);
    222 		}
    223 		xdrs->x_handy = (int)(m->b_wptr - m->b_rptr);
    224 	}
    225 	if (len < XDRMBLK_BCOPY_LIMIT) {
    226 		for (i = 0; i < len; i++)
    227 			*addr++ = *m->b_rptr++;
    228 	} else {
    229 		bcopy(m->b_rptr, addr, len);
    230 		m->b_rptr += len;
    231 	}
    232 	return (TRUE);
    233 }
    234 
    235 /*
    236  * Sort of like getbytes except that instead of getting bytes we return the
    237  * mblk chain which contains the data.  If the data ends in the middle of
    238  * an mblk, the mblk is dup'd and split, so that the data will end on an
    239  * mblk.  Note that it is up to the caller to keep track of the data length
    240  * and not walk too far down the mblk chain.
    241  */
    242 
    243 bool_t
    244 xdrmblk_getmblk(XDR *xdrs, mblk_t **mm, uint_t *lenp)
    245 {
    246 	mblk_t *m, *nextm;
    247 	int len;
    248 	int32_t llen;
    249 
    250 	if (!xdrmblk_getint32(xdrs, &llen))
    251 		return (FALSE);
    252 
    253 	*lenp = llen;
    254 	/* LINTED pointer alignment */
    255 	m = (mblk_t *)xdrs->x_base;
    256 	*mm = m;
    257 
    258 	/*
    259 	 * Walk the mblk chain until we get to the end or we've gathered
    260 	 * enough data.
    261 	 */
    262 	len = 0;
    263 	llen = roundup(llen, BYTES_PER_XDR_UNIT);
    264 	while (m != NULL && len + (int)MBLKL(m) <= llen) {
    265 		len += (int)MBLKL(m);
    266 		m = m->b_cont;
    267 	}
    268 	if (len < llen) {
    269 		if (m == NULL) {
    270 			/* not enough data in XDR stream */
    271 			printf("xdrmblk_getmblk failed\n");
    272 			return (FALSE);
    273 		} else {
    274 			int tail_bytes = llen - len;
    275 
    276 			/*
    277 			 * Split the mblk with the last chunk of data and
    278 			 * insert it into the chain.  The new mblk goes
    279 			 * after the existing one so that it will get freed
    280 			 * properly.
    281 			 */
    282 			nextm = dupb(m);
    283 			if (nextm == NULL)
    284 				return (FALSE);
    285 			nextm->b_cont = m->b_cont;
    286 			m->b_cont = nextm;
    287 			m->b_wptr = m->b_rptr + tail_bytes;
    288 			nextm->b_rptr += tail_bytes;
    289 			ASSERT(nextm->b_rptr != nextm->b_wptr);
    290 
    291 			m = nextm;	/* for x_base */
    292 		}
    293 	}
    294 	xdrs->x_base = (caddr_t)m;
    295 	xdrs->x_handy = m != NULL ? MBLKL(m) : 0;
    296 	return (TRUE);
    297 }
    298 
    299 static bool_t
    300 xdrmblk_putbytes(XDR *xdrs, caddr_t addr, int len)
    301 {
    302 	mblk_t *m;
    303 	uint_t i;
    304 
    305 	/* LINTED pointer alignment */
    306 	m = (mblk_t *)xdrs->x_base;
    307 	if (m == NULL)
    308 		return (FALSE);
    309 	/*
    310 	 * Performance tweak: converted explicit bcopy()
    311 	 * call to simple in-line. This function is called
    312 	 * to process things like readdir reply filenames
    313 	 * which are small strings--typically 12 bytes or less.
    314 	 * Overhead of calling bcopy() is obnoxious for such
    315 	 * small copies.
    316 	 */
    317 	while ((xdrs->x_handy -= len) < 0) {
    318 		if ((xdrs->x_handy += len) > 0) {
    319 			if (xdrs->x_handy < XDRMBLK_BCOPY_LIMIT) {
    320 				for (i = 0; i < (uint_t)xdrs->x_handy; i++)
    321 					*m->b_wptr++ = *addr++;
    322 			} else {
    323 				bcopy(addr, m->b_wptr, xdrs->x_handy);
    324 				m->b_wptr += xdrs->x_handy;
    325 				addr += xdrs->x_handy;
    326 			}
    327 			len -= xdrs->x_handy;
    328 		}
    329 
    330 		/*
    331 		 * We don't have enough space, so allocate the
    332 		 * amount we need, or x_private, whichever is larger.
    333 		 * It is better to let the underlying transport divide
    334 		 * large chunks than to try and guess what is best.
    335 		 */
    336 		if (m->b_cont == NULL)
    337 			m->b_cont = xdrmblk_alloc(MAX(len,
    338 			    (int)(uintptr_t)xdrs->x_private));
    339 
    340 		m = m->b_cont;
    341 		xdrs->x_base = (caddr_t)m;
    342 		if (m == NULL) {
    343 			xdrs->x_handy = 0;
    344 			return (FALSE);
    345 		}
    346 		xdrs->x_handy = (int)(m->b_datap->db_lim - m->b_rptr);
    347 		ASSERT(m->b_rptr == m->b_wptr);
    348 		ASSERT(m->b_rptr >= m->b_datap->db_base);
    349 		ASSERT(m->b_rptr < m->b_datap->db_lim);
    350 	}
    351 	if (len < XDRMBLK_BCOPY_LIMIT) {
    352 		for (i = 0; i < len; i++)
    353 			*m->b_wptr++ = *addr++;
    354 	} else {
    355 		bcopy(addr, m->b_wptr, len);
    356 		m->b_wptr += len;
    357 	}
    358 	ASSERT(m->b_wptr <= m->b_datap->db_lim);
    359 	return (TRUE);
    360 }
    361 
    362 /*
    363  * We avoid a copy by merely adding this mblk to the list.  The caller is
    364  * responsible for allocating and filling in the mblk. If len is
    365  * not a multiple of BYTES_PER_XDR_UNIT, the caller has the option
    366  * of making the data a BYTES_PER_XDR_UNIT multiple (b_wptr - b_rptr is
    367  * a BYTES_PER_XDR_UNIT multiple), but in this case the caller has to ensure
    368  * that the filler bytes are initialized to zero. Note: Doesn't to work for
    369  * chained mblks.
    370  */
    371 bool_t
    372 xdrmblk_putmblk(XDR *xdrs, mblk_t *m, uint_t len)
    373 {
    374 	int32_t llen = (int32_t)len;
    375 
    376 	if (((m->b_wptr - m->b_rptr) % BYTES_PER_XDR_UNIT) != 0)
    377 		return (FALSE);
    378 	if (!xdrmblk_putint32(xdrs, &llen))
    379 		return (FALSE);
    380 	/* LINTED pointer alignment */
    381 	((mblk_t *)xdrs->x_base)->b_cont = m;
    382 	xdrs->x_base = (caddr_t)m;
    383 	xdrs->x_handy = 0;
    384 	return (TRUE);
    385 }
    386 
    387 static uint_t
    388 xdrmblk_getpos(XDR *xdrs)
    389 {
    390 	uint_t tmp;
    391 	mblk_t *m;
    392 
    393 	/* LINTED pointer alignment */
    394 	m = (mblk_t *)xdrs->x_base;
    395 
    396 	if (xdrs->x_op == XDR_DECODE)
    397 		tmp = (uint_t)(m->b_rptr - m->b_datap->db_base);
    398 	else
    399 		tmp = (uint_t)(m->b_wptr - m->b_datap->db_base);
    400 	return (tmp);
    401 
    402 }
    403 
    404 static bool_t
    405 xdrmblk_setpos(XDR *xdrs, uint_t pos)
    406 {
    407 	mblk_t *m;
    408 	unsigned char *newaddr;
    409 
    410 	/* LINTED pointer alignment */
    411 	m = (mblk_t *)xdrs->x_base;
    412 	if (m == NULL)
    413 		return (FALSE);
    414 
    415 	/* calculate the new address from the base */
    416 	newaddr = m->b_datap->db_base + pos;
    417 
    418 	if (xdrs->x_op == XDR_DECODE) {
    419 		if (newaddr > m->b_wptr)
    420 			return (FALSE);
    421 		m->b_rptr = newaddr;
    422 		xdrs->x_handy = (int)(m->b_wptr - newaddr);
    423 	} else {
    424 		if (newaddr > m->b_datap->db_lim)
    425 			return (FALSE);
    426 		m->b_wptr = newaddr;
    427 		xdrs->x_handy = (int)(m->b_datap->db_lim - newaddr);
    428 	}
    429 
    430 	return (TRUE);
    431 }
    432 
    433 #ifdef DEBUG
    434 static int xdrmblk_inline_hits = 0;
    435 static int xdrmblk_inline_misses = 0;
    436 static int do_xdrmblk_inline = 1;
    437 #endif
    438 
    439 static rpc_inline_t *
    440 xdrmblk_inline(XDR *xdrs, int len)
    441 {
    442 	rpc_inline_t *buf;
    443 	mblk_t *m;
    444 
    445 	/*
    446 	 * Can't inline XDR_FREE calls, doesn't make sense.
    447 	 */
    448 	if (xdrs->x_op == XDR_FREE)
    449 		return (NULL);
    450 
    451 	/*
    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
    458 	 * to be modified.
    459 	 */
    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) {
    465 #ifdef DEBUG
    466 		xdrmblk_inline_misses++;
    467 #endif
    468 		return (NULL);
    469 	}
    470 
    471 #ifdef DEBUG
    472 	if (!do_xdrmblk_inline) {
    473 		xdrmblk_inline_misses++;
    474 		return (NULL);
    475 	}
    476 #endif
    477 
    478 	xdrs->x_handy -= len;
    479 	if (xdrs->x_op == XDR_DECODE) {
    480 		/* LINTED pointer alignment */
    481 		buf = (rpc_inline_t *)m->b_rptr;
    482 		m->b_rptr += len;
    483 	} else {
    484 		/* LINTED pointer alignment */
    485 		buf = (rpc_inline_t *)m->b_wptr;
    486 		m->b_wptr += len;
    487 	}
    488 #ifdef DEBUG
    489 	xdrmblk_inline_hits++;
    490 #endif
    491 	return (buf);
    492 }
    493 
    494 static bool_t
    495 xdrmblk_control(XDR *xdrs, int request, void *info)
    496 {
    497 	mblk_t *m;
    498 	int32_t *int32p;
    499 	int len;
    500 
    501 	switch (request) {
    502 	case XDR_PEEK:
    503 		/*
    504 		 * Return the next 4 byte unit in the XDR stream.
    505 		 */
    506 		if (xdrs->x_handy < sizeof (int32_t))
    507 			return (FALSE);
    508 
    509 		/* LINTED pointer alignment */
    510 		m = (mblk_t *)xdrs->x_base;
    511 		if (m == NULL)
    512 			return (FALSE);
    513 
    514 		/*
    515 		 * If the pointer is not aligned, fail the peek
    516 		 */
    517 		if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)))
    518 			return (FALSE);
    519 
    520 		int32p = (int32_t *)info;
    521 		/* LINTED pointer alignment */
    522 		*int32p = ntohl(*((int32_t *)(m->b_rptr)));
    523 		return (TRUE);
    524 
    525 	case XDR_SKIPBYTES:
    526 		/* LINTED pointer alignment */
    527 		m = (mblk_t *)xdrs->x_base;
    528 		if (m == NULL)
    529 			return (FALSE);
    530 		int32p = (int32_t *)info;
    531 		len = RNDUP((int)(*int32p));
    532 		if (len < 0)
    533 		    return (FALSE);
    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;
    538 			}
    539 			m = m->b_cont;
    540 			xdrs->x_base = (caddr_t)m;
    541 			if (m == NULL) {
    542 				xdrs->x_handy = 0;
    543 				return (FALSE);
    544 			}
    545 			xdrs->x_handy = (int)(m->b_wptr - m->b_rptr);
    546 		}
    547 		m->b_rptr += len;
    548 		return (TRUE);
    549 
    550 	default:
    551 		return (FALSE);
    552 	}
    553 }
    554 
    555 #define	HDR_SPACE	128
    556 
    557 static mblk_t *
    558 xdrmblk_alloc(int sz)
    559 {
    560 	mblk_t *mp;
    561 
    562 	if (sz == 0)
    563 		return (NULL);
    564 
    565 	/*
    566 	 * Pad the front of the message to allow the lower networking
    567 	 * layers space to add headers as needed.
    568 	 */
    569 	sz += HDR_SPACE;
    570 
    571 	while ((mp = allocb(sz, BPRI_LO)) == NULL) {
    572 		if (strwaitbuf(sz, BPRI_LO))
    573 			return (NULL);
    574 	}
    575 
    576 	mp->b_wptr += HDR_SPACE;
    577 	mp->b_rptr = mp->b_wptr;
    578 
    579 	return (mp);
    580 }
    581