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 2010 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
xdrmblk_init(XDR * xdrs,mblk_t * m,enum xdr_op op,int sz)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
xdrmblk_destroy(XDR * xdrs)98 xdrmblk_destroy(XDR *xdrs)
99 {
100 }
101
102 static bool_t
xdrmblk_getint32(XDR * xdrs,int32_t * int32p)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
xdrmblk_putint32(XDR * xdrs,int32_t * int32p)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
xdrmblk_getbytes(XDR * xdrs,caddr_t addr,int len)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
xdrmblk_getmblk(XDR * xdrs,mblk_t ** mm,uint_t * lenp)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
xdrmblk_putbytes(XDR * xdrs,caddr_t addr,int len)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.
365 */
366 bool_t
xdrmblk_putmblk(XDR * xdrs,mblk_t * m,uint_t len)367 xdrmblk_putmblk(XDR *xdrs, mblk_t *m, uint_t len)
368 {
369 int32_t llen = (int32_t)len;
370
371 if ((DLEN(m) % BYTES_PER_XDR_UNIT) != 0)
372 return (FALSE);
373 if (!xdrmblk_putint32(xdrs, &llen))
374 return (FALSE);
375
376 /* LINTED pointer alignment */
377 ((mblk_t *)xdrs->x_base)->b_cont = m;
378
379 /* base points to the last mblk */
380 while (m->b_cont)
381 m = m->b_cont;
382 xdrs->x_base = (caddr_t)m;
383 xdrs->x_handy = 0;
384 return (TRUE);
385 }
386
387 static uint_t
xdrmblk_getpos(XDR * xdrs)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
xdrmblk_setpos(XDR * xdrs,uint_t pos)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 *
xdrmblk_inline(XDR * xdrs,int len)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
xdrmblk_control(XDR * xdrs,int request,void * info)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 *
xdrmblk_alloc(int sz)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