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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 /* 38 * xdr_mblk.c, XDR implementation on kernel streams mblks. 39 */ 40 41 #include <sys/param.h> 42 #include <sys/types.h> 43 #include <sys/systm.h> 44 #include <sys/stream.h> 45 #include <sys/cmn_err.h> 46 #include <sys/strsubr.h> 47 #include <sys/strsun.h> 48 #include <sys/debug.h> 49 #include <sys/sysmacros.h> 50 51 #include <rpc/types.h> 52 #include <rpc/xdr.h> 53 54 static bool_t xdrmblk_getint32(XDR *, int32_t *); 55 static bool_t xdrmblk_putint32(XDR *, int32_t *); 56 static bool_t xdrmblk_getbytes(XDR *, caddr_t, int); 57 static bool_t xdrmblk_putbytes(XDR *, caddr_t, int); 58 static uint_t xdrmblk_getpos(XDR *); 59 static bool_t xdrmblk_setpos(XDR *, uint_t); 60 static rpc_inline_t *xdrmblk_inline(XDR *, int); 61 static void xdrmblk_destroy(XDR *); 62 static bool_t xdrmblk_control(XDR *, int, void *); 63 64 static mblk_t *xdrmblk_alloc(int); 65 66 /* 67 * Xdr on mblks operations vector. 68 */ 69 struct xdr_ops xdrmblk_ops = { 70 xdrmblk_getbytes, 71 xdrmblk_putbytes, 72 xdrmblk_getpos, 73 xdrmblk_setpos, 74 xdrmblk_inline, 75 xdrmblk_destroy, 76 xdrmblk_control, 77 xdrmblk_getint32, 78 xdrmblk_putint32 79 }; 80 81 /* 82 * Initialize xdr stream. 83 */ 84 void 85 xdrmblk_init(XDR *xdrs, mblk_t *m, enum xdr_op op, int sz) 86 { 87 xdrs->x_op = op; 88 xdrs->x_ops = &xdrmblk_ops; 89 xdrs->x_base = (caddr_t)m; 90 xdrs->x_public = NULL; 91 xdrs->x_private = (caddr_t)(uintptr_t)sz; 92 93 if (op == XDR_DECODE) 94 xdrs->x_handy = (int)(m->b_wptr - m->b_rptr); 95 else 96 xdrs->x_handy = (int)(m->b_datap->db_lim - m->b_datap->db_base); 97 } 98 99 /* ARGSUSED */ 100 static void 101 xdrmblk_destroy(XDR *xdrs) 102 { 103 } 104 105 static bool_t 106 xdrmblk_getint32(XDR *xdrs, int32_t *int32p) 107 { 108 mblk_t *m; 109 110 /* LINTED pointer alignment */ 111 m = (mblk_t *)xdrs->x_base; 112 if (m == NULL) 113 return (FALSE); 114 /* 115 * If the pointer is not aligned or there is not 116 * enough bytes, pullupmsg to get enough bytes and 117 * align the mblk. 118 */ 119 if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)) || 120 xdrs->x_handy < sizeof (int32_t)) { 121 while (!pullupmsg(m, sizeof (int32_t))) { 122 /* 123 * Could have failed due to not 124 * enough data or an allocb failure. 125 */ 126 if (xmsgsize(m) < sizeof (int32_t)) 127 return (FALSE); 128 delay(hz); 129 } 130 xdrs->x_handy = (int)(m->b_wptr - m->b_rptr); 131 } 132 133 /* LINTED pointer alignment */ 134 *int32p = ntohl(*((int32_t *)(m->b_rptr))); 135 m->b_rptr += sizeof (int32_t); 136 137 /* 138 * Instead of leaving handy as 0 causing more pullupmsg's 139 * simply move to the next mblk. 140 */ 141 if ((xdrs->x_handy -= sizeof (int32_t)) == 0) { 142 m = m->b_cont; 143 xdrs->x_base = (caddr_t)m; 144 if (m != NULL) 145 xdrs->x_handy = (int)(m->b_wptr - m->b_rptr); 146 } 147 return (TRUE); 148 } 149 150 static bool_t 151 xdrmblk_putint32(XDR *xdrs, int32_t *int32p) 152 { 153 mblk_t *m; 154 155 /* LINTED pointer alignment */ 156 m = (mblk_t *)xdrs->x_base; 157 if (m == NULL) 158 return (FALSE); 159 if ((xdrs->x_handy -= (int)sizeof (int32_t)) < 0) { 160 if (m->b_cont == NULL) { 161 m->b_cont = xdrmblk_alloc((int)(uintptr_t) 162 xdrs->x_private); 163 } 164 m = m->b_cont; 165 xdrs->x_base = (caddr_t)m; 166 if (m == NULL) { 167 xdrs->x_handy = 0; 168 return (FALSE); 169 } 170 xdrs->x_handy = (int)(m->b_datap->db_lim - m->b_rptr - 171 sizeof (int32_t)); 172 ASSERT(m->b_rptr == m->b_wptr); 173 ASSERT(m->b_rptr >= m->b_datap->db_base); 174 ASSERT(m->b_rptr < m->b_datap->db_lim); 175 } 176 /* LINTED pointer alignment */ 177 *(int32_t *)m->b_wptr = htonl(*int32p); 178 m->b_wptr += sizeof (int32_t); 179 ASSERT(m->b_wptr <= m->b_datap->db_lim); 180 return (TRUE); 181 } 182 183 /* 184 * We pick 16 as a compromise threshold for most architectures. 185 */ 186 #define XDRMBLK_BCOPY_LIMIT 16 187 188 static bool_t 189 xdrmblk_getbytes(XDR *xdrs, caddr_t addr, int len) 190 { 191 mblk_t *m; 192 int i; 193 194 /* LINTED pointer alignment */ 195 m = (mblk_t *)xdrs->x_base; 196 if (m == NULL) 197 return (FALSE); 198 /* 199 * Performance tweak: converted explicit bcopy() 200 * call to simple in-line. This function is called 201 * to process things like readdir reply filenames 202 * which are small strings--typically 12 bytes or less. 203 * Overhead of calling bcopy() is obnoxious for such 204 * small copies. 205 */ 206 while ((xdrs->x_handy -= len) < 0) { 207 if ((xdrs->x_handy += len) > 0) { 208 if (len < XDRMBLK_BCOPY_LIMIT) { 209 for (i = 0; i < xdrs->x_handy; i++) 210 *addr++ = *m->b_rptr++; 211 } else { 212 bcopy(m->b_rptr, addr, xdrs->x_handy); 213 m->b_rptr += xdrs->x_handy; 214 addr += xdrs->x_handy; 215 } 216 len -= xdrs->x_handy; 217 } 218 m = m->b_cont; 219 xdrs->x_base = (caddr_t)m; 220 if (m == NULL) { 221 xdrs->x_handy = 0; 222 return (FALSE); 223 } 224 xdrs->x_handy = (int)(m->b_wptr - m->b_rptr); 225 } 226 if (len < XDRMBLK_BCOPY_LIMIT) { 227 for (i = 0; i < len; i++) 228 *addr++ = *m->b_rptr++; 229 } else { 230 bcopy(m->b_rptr, addr, len); 231 m->b_rptr += len; 232 } 233 return (TRUE); 234 } 235 236 /* 237 * Sort of like getbytes except that instead of getting bytes we return the 238 * mblk chain which contains the data. If the data ends in the middle of 239 * an mblk, the mblk is dup'd and split, so that the data will end on an 240 * mblk. Note that it is up to the caller to keep track of the data length 241 * and not walk too far down the mblk chain. 242 */ 243 244 bool_t 245 xdrmblk_getmblk(XDR *xdrs, mblk_t **mm, uint_t *lenp) 246 { 247 mblk_t *m, *nextm; 248 int len; 249 int32_t llen; 250 251 if (!xdrmblk_getint32(xdrs, &llen)) 252 return (FALSE); 253 254 *lenp = llen; 255 /* LINTED pointer alignment */ 256 m = (mblk_t *)xdrs->x_base; 257 *mm = m; 258 259 /* 260 * Walk the mblk chain until we get to the end or we've gathered 261 * enough data. 262 */ 263 len = 0; 264 llen = roundup(llen, BYTES_PER_XDR_UNIT); 265 while (m != NULL && len + (int)MBLKL(m) <= llen) { 266 len += (int)MBLKL(m); 267 m = m->b_cont; 268 } 269 if (len < llen) { 270 if (m == NULL) { 271 /* not enough data in XDR stream */ 272 printf("xdrmblk_getmblk failed\n"); 273 return (FALSE); 274 } else { 275 int tail_bytes = llen - len; 276 277 /* 278 * Split the mblk with the last chunk of data and 279 * insert it into the chain. The new mblk goes 280 * after the existing one so that it will get freed 281 * properly. 282 */ 283 nextm = dupb(m); 284 if (nextm == NULL) 285 return (FALSE); 286 nextm->b_cont = m->b_cont; 287 m->b_cont = nextm; 288 m->b_wptr = m->b_rptr + tail_bytes; 289 nextm->b_rptr += tail_bytes; 290 ASSERT(nextm->b_rptr != nextm->b_wptr); 291 292 m = nextm; /* for x_base */ 293 } 294 } 295 xdrs->x_base = (caddr_t)m; 296 xdrs->x_handy = m != NULL ? MBLKL(m) : 0; 297 return (TRUE); 298 } 299 300 static bool_t 301 xdrmblk_putbytes(XDR *xdrs, caddr_t addr, int len) 302 { 303 mblk_t *m; 304 uint_t i; 305 306 /* LINTED pointer alignment */ 307 m = (mblk_t *)xdrs->x_base; 308 if (m == NULL) 309 return (FALSE); 310 /* 311 * Performance tweak: converted explicit bcopy() 312 * call to simple in-line. This function is called 313 * to process things like readdir reply filenames 314 * which are small strings--typically 12 bytes or less. 315 * Overhead of calling bcopy() is obnoxious for such 316 * small copies. 317 */ 318 while ((xdrs->x_handy -= len) < 0) { 319 if ((xdrs->x_handy += len) > 0) { 320 if (xdrs->x_handy < XDRMBLK_BCOPY_LIMIT) { 321 for (i = 0; i < (uint_t)xdrs->x_handy; i++) 322 *m->b_wptr++ = *addr++; 323 } else { 324 bcopy(addr, m->b_wptr, xdrs->x_handy); 325 m->b_wptr += xdrs->x_handy; 326 addr += xdrs->x_handy; 327 } 328 len -= xdrs->x_handy; 329 } 330 331 /* 332 * We don't have enough space, so allocate the 333 * amount we need, or x_private, whichever is larger. 334 * It is better to let the underlying transport divide 335 * large chunks than to try and guess what is best. 336 */ 337 if (m->b_cont == NULL) 338 m->b_cont = xdrmblk_alloc(MAX(len, 339 (int)(uintptr_t)xdrs->x_private)); 340 341 m = m->b_cont; 342 xdrs->x_base = (caddr_t)m; 343 if (m == NULL) { 344 xdrs->x_handy = 0; 345 return (FALSE); 346 } 347 xdrs->x_handy = (int)(m->b_datap->db_lim - m->b_rptr); 348 ASSERT(m->b_rptr == m->b_wptr); 349 ASSERT(m->b_rptr >= m->b_datap->db_base); 350 ASSERT(m->b_rptr < m->b_datap->db_lim); 351 } 352 if (len < XDRMBLK_BCOPY_LIMIT) { 353 for (i = 0; i < len; i++) 354 *m->b_wptr++ = *addr++; 355 } else { 356 bcopy(addr, m->b_wptr, len); 357 m->b_wptr += len; 358 } 359 ASSERT(m->b_wptr <= m->b_datap->db_lim); 360 return (TRUE); 361 } 362 363 /* 364 * We avoid a copy by merely adding this mblk to the list. The caller is 365 * responsible for allocating and filling in the mblk. If len is 366 * not a multiple of BYTES_PER_XDR_UNIT, the caller has the option 367 * of making the data a BYTES_PER_XDR_UNIT multiple (b_wptr - b_rptr is 368 * a BYTES_PER_XDR_UNIT multiple), but in this case the caller has to ensure 369 * that the filler bytes are initialized to zero. Note: Doesn't to work for 370 * chained mblks. 371 */ 372 bool_t 373 xdrmblk_putmblk(XDR *xdrs, mblk_t *m, uint_t len) 374 { 375 int32_t llen = (int32_t)len; 376 377 if (((m->b_wptr - m->b_rptr) % BYTES_PER_XDR_UNIT) != 0) 378 return (FALSE); 379 if (!xdrmblk_putint32(xdrs, &llen)) 380 return (FALSE); 381 /* LINTED pointer alignment */ 382 ((mblk_t *)xdrs->x_base)->b_cont = m; 383 xdrs->x_base = (caddr_t)m; 384 xdrs->x_handy = 0; 385 return (TRUE); 386 } 387 388 static uint_t 389 xdrmblk_getpos(XDR *xdrs) 390 { 391 uint_t tmp; 392 mblk_t *m; 393 394 /* LINTED pointer alignment */ 395 m = (mblk_t *)xdrs->x_base; 396 397 if (xdrs->x_op == XDR_DECODE) 398 tmp = (uint_t)(m->b_rptr - m->b_datap->db_base); 399 else 400 tmp = (uint_t)(m->b_wptr - m->b_datap->db_base); 401 return (tmp); 402 403 } 404 405 static bool_t 406 xdrmblk_setpos(XDR *xdrs, uint_t pos) 407 { 408 mblk_t *m; 409 unsigned char *newaddr; 410 411 /* LINTED pointer alignment */ 412 m = (mblk_t *)xdrs->x_base; 413 if (m == NULL) 414 return (FALSE); 415 416 /* calculate the new address from the base */ 417 newaddr = m->b_datap->db_base + pos; 418 419 if (xdrs->x_op == XDR_DECODE) { 420 if (newaddr > m->b_wptr) 421 return (FALSE); 422 m->b_rptr = newaddr; 423 xdrs->x_handy = (int)(m->b_wptr - newaddr); 424 } else { 425 if (newaddr > m->b_datap->db_lim) 426 return (FALSE); 427 m->b_wptr = newaddr; 428 xdrs->x_handy = (int)(m->b_datap->db_lim - newaddr); 429 } 430 431 return (TRUE); 432 } 433 434 #ifdef DEBUG 435 static int xdrmblk_inline_hits = 0; 436 static int xdrmblk_inline_misses = 0; 437 static int do_xdrmblk_inline = 1; 438 #endif 439 440 static rpc_inline_t * 441 xdrmblk_inline(XDR *xdrs, int len) 442 { 443 rpc_inline_t *buf; 444 mblk_t *m; 445 446 /* 447 * Can't inline XDR_FREE calls, doesn't make sense. 448 */ 449 if (xdrs->x_op == XDR_FREE) 450 return (NULL); 451 452 /* 453 * Can't inline if there isn't enough room, don't have an 454 * mblk pointer, its not 4 byte aligned, or if there is more than 455 * one reference to the data block associated with this mblk. This last 456 * check is used because the caller may want to modified 457 * the data in the inlined portion and someone else is 458 * holding a reference to the data who may not want it 459 * to be modified. 460 */ 461 if (xdrs->x_handy < len || 462 /* LINTED pointer alignment */ 463 (m = (mblk_t *)xdrs->x_base) == NULL || 464 !IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)) || 465 m->b_datap->db_ref != 1) { 466 #ifdef DEBUG 467 xdrmblk_inline_misses++; 468 #endif 469 return (NULL); 470 } 471 472 #ifdef DEBUG 473 if (!do_xdrmblk_inline) { 474 xdrmblk_inline_misses++; 475 return (NULL); 476 } 477 #endif 478 479 xdrs->x_handy -= len; 480 if (xdrs->x_op == XDR_DECODE) { 481 /* LINTED pointer alignment */ 482 buf = (rpc_inline_t *)m->b_rptr; 483 m->b_rptr += len; 484 } else { 485 /* LINTED pointer alignment */ 486 buf = (rpc_inline_t *)m->b_wptr; 487 m->b_wptr += len; 488 } 489 #ifdef DEBUG 490 xdrmblk_inline_hits++; 491 #endif 492 return (buf); 493 } 494 495 static bool_t 496 xdrmblk_control(XDR *xdrs, int request, void *info) 497 { 498 mblk_t *m; 499 int32_t *int32p; 500 int len; 501 502 switch (request) { 503 case XDR_PEEK: 504 /* 505 * Return the next 4 byte unit in the XDR stream. 506 */ 507 if (xdrs->x_handy < sizeof (int32_t)) 508 return (FALSE); 509 510 /* LINTED pointer alignment */ 511 m = (mblk_t *)xdrs->x_base; 512 if (m == NULL) 513 return (FALSE); 514 515 /* 516 * If the pointer is not aligned, fail the peek 517 */ 518 if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t))) 519 return (FALSE); 520 521 int32p = (int32_t *)info; 522 /* LINTED pointer alignment */ 523 *int32p = ntohl(*((int32_t *)(m->b_rptr))); 524 return (TRUE); 525 526 case XDR_SKIPBYTES: 527 /* LINTED pointer alignment */ 528 m = (mblk_t *)xdrs->x_base; 529 if (m == NULL) 530 return (FALSE); 531 int32p = (int32_t *)info; 532 len = RNDUP((int)(*int32p)); 533 while ((xdrs->x_handy -= len) < 0) { 534 if ((xdrs->x_handy += len) > 0) { 535 m->b_rptr += xdrs->x_handy; 536 len -= xdrs->x_handy; 537 } 538 m = m->b_cont; 539 xdrs->x_base = (caddr_t)m; 540 if (m == NULL) { 541 xdrs->x_handy = 0; 542 return (FALSE); 543 } 544 xdrs->x_handy = (int)(m->b_wptr - m->b_rptr); 545 } 546 m->b_rptr += len; 547 return (TRUE); 548 549 default: 550 return (FALSE); 551 } 552 } 553 554 #define HDR_SPACE 128 555 556 static mblk_t * 557 xdrmblk_alloc(int sz) 558 { 559 mblk_t *mp; 560 561 if (sz == 0) 562 return (NULL); 563 564 /* 565 * Pad the front of the message to allow the lower networking 566 * layers space to add headers as needed. 567 */ 568 sz += HDR_SPACE; 569 570 while ((mp = allocb(sz, BPRI_LO)) == NULL) { 571 if (strwaitbuf(sz, BPRI_LO)) 572 return (NULL); 573 } 574 575 mp->b_wptr += HDR_SPACE; 576 mp->b_rptr = mp->b_wptr; 577 578 return (mp); 579 } 580