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 2010 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 /* 35 * xdr_mblk.c, XDR implementation on kernel streams mblks. 36 */ 37 38 #include <sys/param.h> 39 #include <sys/types.h> 40 #include <sys/systm.h> 41 #include <sys/stream.h> 42 #include <sys/cmn_err.h> 43 #include <sys/strsubr.h> 44 #include <sys/strsun.h> 45 #include <sys/debug.h> 46 #include <sys/sysmacros.h> 47 48 #include <rpc/types.h> 49 #include <rpc/xdr.h> 50 51 static bool_t xdrmblk_getint32(XDR *, int32_t *); 52 static bool_t xdrmblk_putint32(XDR *, int32_t *); 53 static bool_t xdrmblk_getbytes(XDR *, caddr_t, int); 54 static bool_t xdrmblk_putbytes(XDR *, caddr_t, int); 55 static uint_t xdrmblk_getpos(XDR *); 56 static bool_t xdrmblk_setpos(XDR *, uint_t); 57 static rpc_inline_t *xdrmblk_inline(XDR *, int); 58 static void xdrmblk_destroy(XDR *); 59 static bool_t xdrmblk_control(XDR *, int, void *); 60 61 static mblk_t *xdrmblk_alloc(int); 62 63 /* 64 * Xdr on mblks operations vector. 65 */ 66 struct xdr_ops xdrmblk_ops = { 67 xdrmblk_getbytes, 68 xdrmblk_putbytes, 69 xdrmblk_getpos, 70 xdrmblk_setpos, 71 xdrmblk_inline, 72 xdrmblk_destroy, 73 xdrmblk_control, 74 xdrmblk_getint32, 75 xdrmblk_putint32 76 }; 77 78 /* 79 * Initialize xdr stream. 80 */ 81 void 82 xdrmblk_init(XDR *xdrs, mblk_t *m, enum xdr_op op, int sz) 83 { 84 xdrs->x_op = op; 85 xdrs->x_ops = &xdrmblk_ops; 86 xdrs->x_base = (caddr_t)m; 87 xdrs->x_public = NULL; 88 xdrs->x_private = (caddr_t)(uintptr_t)sz; 89 90 if (op == XDR_DECODE) 91 xdrs->x_handy = (int)(m->b_wptr - m->b_rptr); 92 else 93 xdrs->x_handy = (int)(m->b_datap->db_lim - m->b_datap->db_base); 94 } 95 96 /* ARGSUSED */ 97 static void 98 xdrmblk_destroy(XDR *xdrs) 99 { 100 } 101 102 static bool_t 103 xdrmblk_getint32(XDR *xdrs, int32_t *int32p) 104 { 105 mblk_t *m; 106 107 /* LINTED pointer alignment */ 108 m = (mblk_t *)xdrs->x_base; 109 if (m == NULL) 110 return (FALSE); 111 /* 112 * If the pointer is not aligned or there is not 113 * enough bytes, pullupmsg to get enough bytes and 114 * align the mblk. 115 */ 116 if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)) || 117 xdrs->x_handy < sizeof (int32_t)) { 118 while (!pullupmsg(m, sizeof (int32_t))) { 119 /* 120 * Could have failed due to not 121 * enough data or an allocb failure. 122 */ 123 if (xmsgsize(m) < sizeof (int32_t)) 124 return (FALSE); 125 delay(hz); 126 } 127 xdrs->x_handy = (int)(m->b_wptr - m->b_rptr); 128 } 129 130 /* LINTED pointer alignment */ 131 *int32p = ntohl(*((int32_t *)(m->b_rptr))); 132 m->b_rptr += sizeof (int32_t); 133 134 /* 135 * Instead of leaving handy as 0 causing more pullupmsg's 136 * simply move to the next mblk. 137 */ 138 if ((xdrs->x_handy -= sizeof (int32_t)) == 0) { 139 m = m->b_cont; 140 xdrs->x_base = (caddr_t)m; 141 if (m != NULL) 142 xdrs->x_handy = (int)(m->b_wptr - m->b_rptr); 143 } 144 return (TRUE); 145 } 146 147 static bool_t 148 xdrmblk_putint32(XDR *xdrs, int32_t *int32p) 149 { 150 mblk_t *m; 151 152 /* LINTED pointer alignment */ 153 m = (mblk_t *)xdrs->x_base; 154 if (m == NULL) 155 return (FALSE); 156 if ((xdrs->x_handy -= (int)sizeof (int32_t)) < 0) { 157 if (m->b_cont == NULL) { 158 m->b_cont = xdrmblk_alloc((int)(uintptr_t) 159 xdrs->x_private); 160 } 161 m = m->b_cont; 162 xdrs->x_base = (caddr_t)m; 163 if (m == NULL) { 164 xdrs->x_handy = 0; 165 return (FALSE); 166 } 167 xdrs->x_handy = (int)(m->b_datap->db_lim - m->b_rptr - 168 sizeof (int32_t)); 169 ASSERT(m->b_rptr == m->b_wptr); 170 ASSERT(m->b_rptr >= m->b_datap->db_base); 171 ASSERT(m->b_rptr < m->b_datap->db_lim); 172 } 173 /* LINTED pointer alignment */ 174 *(int32_t *)m->b_wptr = htonl(*int32p); 175 m->b_wptr += sizeof (int32_t); 176 ASSERT(m->b_wptr <= m->b_datap->db_lim); 177 return (TRUE); 178 } 179 180 /* 181 * We pick 16 as a compromise threshold for most architectures. 182 */ 183 #define XDRMBLK_BCOPY_LIMIT 16 184 185 static bool_t 186 xdrmblk_getbytes(XDR *xdrs, caddr_t addr, int len) 187 { 188 mblk_t *m; 189 int i; 190 191 /* LINTED pointer alignment */ 192 m = (mblk_t *)xdrs->x_base; 193 if (m == NULL) 194 return (FALSE); 195 /* 196 * Performance tweak: converted explicit bcopy() 197 * call to simple in-line. This function is called 198 * to process things like readdir reply filenames 199 * which are small strings--typically 12 bytes or less. 200 * Overhead of calling bcopy() is obnoxious for such 201 * small copies. 202 */ 203 while ((xdrs->x_handy -= len) < 0) { 204 if ((xdrs->x_handy += len) > 0) { 205 if (len < XDRMBLK_BCOPY_LIMIT) { 206 for (i = 0; i < xdrs->x_handy; i++) 207 *addr++ = *m->b_rptr++; 208 } else { 209 bcopy(m->b_rptr, addr, xdrs->x_handy); 210 m->b_rptr += xdrs->x_handy; 211 addr += xdrs->x_handy; 212 } 213 len -= xdrs->x_handy; 214 } 215 m = m->b_cont; 216 xdrs->x_base = (caddr_t)m; 217 if (m == NULL) { 218 xdrs->x_handy = 0; 219 return (FALSE); 220 } 221 xdrs->x_handy = (int)(m->b_wptr - m->b_rptr); 222 } 223 if (len < XDRMBLK_BCOPY_LIMIT) { 224 for (i = 0; i < len; i++) 225 *addr++ = *m->b_rptr++; 226 } else { 227 bcopy(m->b_rptr, addr, len); 228 m->b_rptr += len; 229 } 230 return (TRUE); 231 } 232 233 /* 234 * Sort of like getbytes except that instead of getting bytes we return the 235 * mblk chain which contains the data. If the data ends in the middle of 236 * an mblk, the mblk is dup'd and split, so that the data will end on an 237 * mblk. Note that it is up to the caller to keep track of the data length 238 * and not walk too far down the mblk chain. 239 */ 240 241 bool_t 242 xdrmblk_getmblk(XDR *xdrs, mblk_t **mm, uint_t *lenp) 243 { 244 mblk_t *m, *nextm; 245 int len; 246 int32_t llen; 247 248 if (!xdrmblk_getint32(xdrs, &llen)) 249 return (FALSE); 250 251 *lenp = llen; 252 /* LINTED pointer alignment */ 253 m = (mblk_t *)xdrs->x_base; 254 *mm = m; 255 256 /* 257 * Walk the mblk chain until we get to the end or we've gathered 258 * enough data. 259 */ 260 len = 0; 261 llen = roundup(llen, BYTES_PER_XDR_UNIT); 262 while (m != NULL && len + (int)MBLKL(m) <= llen) { 263 len += (int)MBLKL(m); 264 m = m->b_cont; 265 } 266 if (len < llen) { 267 if (m == NULL) { 268 return (FALSE); 269 } else { 270 int tail_bytes = llen - len; 271 272 /* 273 * Split the mblk with the last chunk of data and 274 * insert it into the chain. The new mblk goes 275 * after the existing one so that it will get freed 276 * properly. 277 */ 278 nextm = dupb(m); 279 if (nextm == NULL) 280 return (FALSE); 281 nextm->b_cont = m->b_cont; 282 m->b_cont = nextm; 283 m->b_wptr = m->b_rptr + tail_bytes; 284 nextm->b_rptr += tail_bytes; 285 ASSERT(nextm->b_rptr != nextm->b_wptr); 286 287 m = nextm; /* for x_base */ 288 } 289 } 290 xdrs->x_base = (caddr_t)m; 291 xdrs->x_handy = m != NULL ? MBLKL(m) : 0; 292 return (TRUE); 293 } 294 295 static bool_t 296 xdrmblk_putbytes(XDR *xdrs, caddr_t addr, int len) 297 { 298 mblk_t *m; 299 uint_t i; 300 301 /* LINTED pointer alignment */ 302 m = (mblk_t *)xdrs->x_base; 303 if (m == NULL) 304 return (FALSE); 305 /* 306 * Performance tweak: converted explicit bcopy() 307 * call to simple in-line. This function is called 308 * to process things like readdir reply filenames 309 * which are small strings--typically 12 bytes or less. 310 * Overhead of calling bcopy() is obnoxious for such 311 * small copies. 312 */ 313 while ((xdrs->x_handy -= len) < 0) { 314 if ((xdrs->x_handy += len) > 0) { 315 if (xdrs->x_handy < XDRMBLK_BCOPY_LIMIT) { 316 for (i = 0; i < (uint_t)xdrs->x_handy; i++) 317 *m->b_wptr++ = *addr++; 318 } else { 319 bcopy(addr, m->b_wptr, xdrs->x_handy); 320 m->b_wptr += xdrs->x_handy; 321 addr += xdrs->x_handy; 322 } 323 len -= xdrs->x_handy; 324 } 325 326 /* 327 * We don't have enough space, so allocate the 328 * amount we need, or x_private, whichever is larger. 329 * It is better to let the underlying transport divide 330 * large chunks than to try and guess what is best. 331 */ 332 if (m->b_cont == NULL) 333 m->b_cont = xdrmblk_alloc(MAX(len, 334 (int)(uintptr_t)xdrs->x_private)); 335 336 m = m->b_cont; 337 xdrs->x_base = (caddr_t)m; 338 if (m == NULL) { 339 xdrs->x_handy = 0; 340 return (FALSE); 341 } 342 xdrs->x_handy = (int)(m->b_datap->db_lim - m->b_rptr); 343 ASSERT(m->b_rptr == m->b_wptr); 344 ASSERT(m->b_rptr >= m->b_datap->db_base); 345 ASSERT(m->b_rptr < m->b_datap->db_lim); 346 } 347 if (len < XDRMBLK_BCOPY_LIMIT) { 348 for (i = 0; i < len; i++) 349 *m->b_wptr++ = *addr++; 350 } else { 351 bcopy(addr, m->b_wptr, len); 352 m->b_wptr += len; 353 } 354 ASSERT(m->b_wptr <= m->b_datap->db_lim); 355 return (TRUE); 356 } 357 358 /* 359 * We avoid a copy by merely adding this mblk to the list. The caller is 360 * responsible for allocating and filling in the mblk. If len is 361 * not a multiple of BYTES_PER_XDR_UNIT, the caller has the option 362 * of making the data a BYTES_PER_XDR_UNIT multiple (b_wptr - b_rptr is 363 * a BYTES_PER_XDR_UNIT multiple), but in this case the caller has to ensure 364 * that the filler bytes are initialized to zero. 365 */ 366 bool_t 367 xdrmblk_putmblk(XDR *xdrs, mblk_t *m, uint_t len) 368 { 369 int32_t llen = (int32_t)len; 370 371 if ((DLEN(m) % BYTES_PER_XDR_UNIT) != 0) 372 return (FALSE); 373 if (!xdrmblk_putint32(xdrs, &llen)) 374 return (FALSE); 375 376 /* LINTED pointer alignment */ 377 ((mblk_t *)xdrs->x_base)->b_cont = m; 378 379 /* base points to the last mblk */ 380 while (m->b_cont) 381 m = m->b_cont; 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