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 2008 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. Note: Doesn't to work for 365 * chained mblks. 366 */ 367 bool_t 368 xdrmblk_putmblk(XDR *xdrs, mblk_t *m, uint_t len) 369 { 370 int32_t llen = (int32_t)len; 371 372 if (((m->b_wptr - m->b_rptr) % BYTES_PER_XDR_UNIT) != 0) 373 return (FALSE); 374 if (!xdrmblk_putint32(xdrs, &llen)) 375 return (FALSE); 376 /* LINTED pointer alignment */ 377 ((mblk_t *)xdrs->x_base)->b_cont = m; 378 xdrs->x_base = (caddr_t)m; 379 xdrs->x_handy = 0; 380 return (TRUE); 381 } 382 383 static uint_t 384 xdrmblk_getpos(XDR *xdrs) 385 { 386 uint_t tmp; 387 mblk_t *m; 388 389 /* LINTED pointer alignment */ 390 m = (mblk_t *)xdrs->x_base; 391 392 if (xdrs->x_op == XDR_DECODE) 393 tmp = (uint_t)(m->b_rptr - m->b_datap->db_base); 394 else 395 tmp = (uint_t)(m->b_wptr - m->b_datap->db_base); 396 return (tmp); 397 398 } 399 400 static bool_t 401 xdrmblk_setpos(XDR *xdrs, uint_t pos) 402 { 403 mblk_t *m; 404 unsigned char *newaddr; 405 406 /* LINTED pointer alignment */ 407 m = (mblk_t *)xdrs->x_base; 408 if (m == NULL) 409 return (FALSE); 410 411 /* calculate the new address from the base */ 412 newaddr = m->b_datap->db_base + pos; 413 414 if (xdrs->x_op == XDR_DECODE) { 415 if (newaddr > m->b_wptr) 416 return (FALSE); 417 m->b_rptr = newaddr; 418 xdrs->x_handy = (int)(m->b_wptr - newaddr); 419 } else { 420 if (newaddr > m->b_datap->db_lim) 421 return (FALSE); 422 m->b_wptr = newaddr; 423 xdrs->x_handy = (int)(m->b_datap->db_lim - newaddr); 424 } 425 426 return (TRUE); 427 } 428 429 #ifdef DEBUG 430 static int xdrmblk_inline_hits = 0; 431 static int xdrmblk_inline_misses = 0; 432 static int do_xdrmblk_inline = 1; 433 #endif 434 435 static rpc_inline_t * 436 xdrmblk_inline(XDR *xdrs, int len) 437 { 438 rpc_inline_t *buf; 439 mblk_t *m; 440 441 /* 442 * Can't inline XDR_FREE calls, doesn't make sense. 443 */ 444 if (xdrs->x_op == XDR_FREE) 445 return (NULL); 446 447 /* 448 * Can't inline if there isn't enough room, don't have an 449 * mblk pointer, its not 4 byte aligned, or if there is more than 450 * one reference to the data block associated with this mblk. This last 451 * check is used because the caller may want to modified 452 * the data in the inlined portion and someone else is 453 * holding a reference to the data who may not want it 454 * to be modified. 455 */ 456 if (xdrs->x_handy < len || 457 /* LINTED pointer alignment */ 458 (m = (mblk_t *)xdrs->x_base) == NULL || 459 !IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)) || 460 m->b_datap->db_ref != 1) { 461 #ifdef DEBUG 462 xdrmblk_inline_misses++; 463 #endif 464 return (NULL); 465 } 466 467 #ifdef DEBUG 468 if (!do_xdrmblk_inline) { 469 xdrmblk_inline_misses++; 470 return (NULL); 471 } 472 #endif 473 474 xdrs->x_handy -= len; 475 if (xdrs->x_op == XDR_DECODE) { 476 /* LINTED pointer alignment */ 477 buf = (rpc_inline_t *)m->b_rptr; 478 m->b_rptr += len; 479 } else { 480 /* LINTED pointer alignment */ 481 buf = (rpc_inline_t *)m->b_wptr; 482 m->b_wptr += len; 483 } 484 #ifdef DEBUG 485 xdrmblk_inline_hits++; 486 #endif 487 return (buf); 488 } 489 490 static bool_t 491 xdrmblk_control(XDR *xdrs, int request, void *info) 492 { 493 mblk_t *m; 494 int32_t *int32p; 495 int len; 496 497 switch (request) { 498 case XDR_PEEK: 499 /* 500 * Return the next 4 byte unit in the XDR stream. 501 */ 502 if (xdrs->x_handy < sizeof (int32_t)) 503 return (FALSE); 504 505 /* LINTED pointer alignment */ 506 m = (mblk_t *)xdrs->x_base; 507 if (m == NULL) 508 return (FALSE); 509 510 /* 511 * If the pointer is not aligned, fail the peek 512 */ 513 if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t))) 514 return (FALSE); 515 516 int32p = (int32_t *)info; 517 /* LINTED pointer alignment */ 518 *int32p = ntohl(*((int32_t *)(m->b_rptr))); 519 return (TRUE); 520 521 case XDR_SKIPBYTES: 522 /* LINTED pointer alignment */ 523 m = (mblk_t *)xdrs->x_base; 524 if (m == NULL) 525 return (FALSE); 526 int32p = (int32_t *)info; 527 len = RNDUP((int)(*int32p)); 528 if (len < 0) 529 return (FALSE); 530 while ((xdrs->x_handy -= len) < 0) { 531 if ((xdrs->x_handy += len) > 0) { 532 m->b_rptr += xdrs->x_handy; 533 len -= xdrs->x_handy; 534 } 535 m = m->b_cont; 536 xdrs->x_base = (caddr_t)m; 537 if (m == NULL) { 538 xdrs->x_handy = 0; 539 return (FALSE); 540 } 541 xdrs->x_handy = (int)(m->b_wptr - m->b_rptr); 542 } 543 m->b_rptr += len; 544 return (TRUE); 545 546 default: 547 return (FALSE); 548 } 549 } 550 551 #define HDR_SPACE 128 552 553 static mblk_t * 554 xdrmblk_alloc(int sz) 555 { 556 mblk_t *mp; 557 558 if (sz == 0) 559 return (NULL); 560 561 /* 562 * Pad the front of the message to allow the lower networking 563 * layers space to add headers as needed. 564 */ 565 sz += HDR_SPACE; 566 567 while ((mp = allocb(sz, BPRI_LO)) == NULL) { 568 if (strwaitbuf(sz, BPRI_LO)) 569 return (NULL); 570 } 571 572 mp->b_wptr += HDR_SPACE; 573 mp->b_rptr = mp->b_wptr; 574 575 return (mp); 576 } 577