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