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 "%Z%%M% %I% %E% 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