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