2 * Copyright (c) 2000, 2001 Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * $FreeBSD: src/sys/kern/subr_mchain.c,v 1.1 2001/02/24 15:44:29 bp Exp $
36 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
37 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
38 * Use is subject to license terms.
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/errno.h>
45 #include <sys/types.h>
46 #include <sys/stream.h>
47 #include <sys/strsun.h>
48 #include <sys/strsubr.h>
49 #include <sys/sunddi.h>
50 #include <sys/cmn_err.h>
52 #include <netsmb/smb_osdep.h>
53 #include <netsmb/mchain.h>
55 #include <netsmb/smb.h>
56 #include <netsmb/smb_conn.h>
57 #include <netsmb/smb_subr.h>
61 * BSD-style mbufs, vs SysV-style mblks:
62 * One big difference: the mbuf payload is:
63 * m_data ... (m_data + m_len)
64 * In Unix STREAMS, the mblk payload is:
67 * Here are some handy conversion notes:
69 * struct mbuf struct mblk
71 * m->m_nextpkt m->b_next
74 * m->m_dat[] m->b_datap->db_base
75 * &m->m_dat[MLEN] m->b_datap->db_lim
76 * M_TRAILINGSPACE(m) MBLKTAIL(m)
77 * m_freem(m) freemsg(m)
79 * Note that mbufs chains also have a special "packet" header,
80 * which has the length of the whole message. In STREAMS one
81 * typically just calls msgdsize(m) to get that.
88 * MODULE_VERSION(libmchain, 1);
92 #define MBERROR(format, args...) printf("%s(%d): "format, \
93 __FUNCTION__, __LINE__, ## args)
94 #define MBPANIC(format, args...) printf("%s(%d): "format, \
95 __FUNCTION__, __LINE__, ## args)
97 #define MBERROR(...) \
98 smb_errmsg(CE_NOTE, __func__, __VA_ARGS__)
99 #define MBPANIC(...) \
100 smb_errmsg(CE_PANIC, __func__, __VA_ARGS__)
104 * MLEN: The smallest mblk we'll allocate.
106 * There's more to MLEN than you might think.
107 * Some ethernet drivers may send each mblk as a
108 * separate frame, so we want MLEN at least 1K.
109 * We could have used 1K here, but that might
110 * hurt transports that support larger frames.
111 * 4K fits nicely in 3 Ethernet frames (3 * 1500)
112 * leaving about 500 bytes for protocol headers.
119 * Taken from Darwin Sourcecs.
123 * uio_isuserspace - non zero value if the address space
124 * flag is for a user address space (could be 32 or 64 bit).
126 #define uio_isuserspace(uio) (uio->uio_segflg == UIO_USERSPACE)
129 * uio_curriovbase - return the base address of the current iovec associated
130 * with the given uio_t. May return 0.
133 uio_curriovbase(uio_t
*a_uio
)
135 if (a_uio
->uio_iovcnt
< 1) {
138 return ((caddr_t
)((uintptr_t)a_uio
->uio_iov
->iov_base
));
142 * uio_curriovlen - return the length value of the current iovec associated
143 * with the given uio_t.
146 uio_curriovlen(uio_t
*a_uio
)
148 if (a_uio
->uio_iovcnt
< 1) {
151 return ((size_t)a_uio
->uio_iov
->iov_len
);
156 * uio_update - update the given uio_t for a_count of completed IO.
157 * This call decrements the current iovec length and residual IO value
158 * and increments the current iovec base address and offset value.
159 * If the current iovec length is 0 then advance to the next
161 * If the a_count passed in is 0, than only do the advancement
162 * over any 0 length iovec's.
165 uio_update(uio_t
*a_uio
, size_t a_count
)
167 if (a_uio
->uio_iovcnt
< 1) {
172 * if a_count == 0, then we are asking to skip over
176 if (a_count
> a_uio
->uio_iov
->iov_len
) {
177 a_uio
->uio_iov
->iov_base
+= a_uio
->uio_iov
->iov_len
;
178 a_uio
->uio_iov
->iov_len
= 0;
180 a_uio
->uio_iov
->iov_base
+= a_count
;
181 a_uio
->uio_iov
->iov_len
-= a_count
;
183 if (a_uio
->uio_resid
< 0) {
184 a_uio
->uio_resid
= 0;
186 if (a_count
> (size_t)a_uio
->uio_resid
) {
187 a_uio
->uio_loffset
+= a_uio
->uio_resid
;
188 a_uio
->uio_resid
= 0;
190 a_uio
->uio_loffset
+= a_count
;
191 a_uio
->uio_resid
-= a_count
;
195 * advance to next iovec if current one is totally consumed
197 while (a_uio
->uio_iovcnt
> 0 && a_uio
->uio_iov
->iov_len
== 0) {
199 if (a_uio
->uio_iovcnt
> 0) {
206 * This is now used only to extend an existing mblk chain,
207 * so don't need to use allocb_cred_wait here.
211 m_getblk(int size
, int type
)
216 /* Make size at least MLEN. */
219 mblk
= allocb_wait(size
, BPRI_LO
, STR_NOSIG
, &error
);
225 mb_done(struct mbchain
*mbp
)
228 freemsg(mbp
->mb_top
);
231 /* Avoid dangling references */
236 m_length(mblk_t
*mblk
)
240 diff
= (uintptr_t)mblk
->b_datap
->db_lim
-
241 (uintptr_t)mblk
->b_datap
->db_base
;
242 ASSERT(diff
== (uint64_t)((unsigned int)diff
));
243 return ((unsigned int)diff
);
247 mb_initm(struct mbchain
*mbp
, mblk_t
*m
)
249 bzero(mbp
, sizeof (*mbp
));
250 mbp
->mb_top
= mbp
->mb_cur
= m
;
255 mb_init(struct mbchain
*mbp
)
262 * This message will be the head of a new mblk chain,
263 * so we'd like its db_credp set. If we extend this
264 * chain later, we'll just use allocb_wait()
267 mblk
= allocb_cred_wait(MLEN
, STR_NOSIG
, &error
, cr
, NOPID
);
270 * Leave room in this first mblk so we can
271 * prepend a 4-byte NetBIOS header.
272 * See smb_nbst_send()
275 mblk
->b_rptr
= mblk
->b_wptr
;
283 * mb_detach() function returns the value of mbp->mb_top field
284 * and sets its * value to NULL.
288 mb_detach(struct mbchain
*mbp
)
293 mbp
->mb_top
= mbp
->mb_cur
= NULL
;
298 * Returns the length of the mblk_t data.
299 * Should be m_totlen() perhaps?
311 * BSD code set the message header length here, and
312 * returned the length. We don't have that field, so
313 * just return the message length.
316 mb_fixhdr(struct mbchain
*mbp
)
318 return (m_fixhdr(mbp
->mb_top
));
323 * Check if object of size 'size' fit to the current position and
324 * allocate new mbuf if not. Advance pointers and increase len. of mbuf(s).
325 * Return pointer to the object placeholder or NULL if any error occured.
326 * Note: size should be <= MLEN
329 mb_reserve(struct mbchain
*mbp
, int size
)
336 * If the requested size is more than the space left.
337 * Allocate and appenad a new mblk.
339 if (MBLKTAIL(m
) < size
) {
340 mn
= m_getblk(size
, 1);
343 mbp
->mb_cur
= m
->b_cont
= mn
;
347 * If 'size' bytes fits into the buffer, then
348 * 1. increment the write pointer to the size.
349 * 2. return the position from where the memory is reserved.
353 mbp
->mb_count
+= size
;
358 * All mb_put_*() functions perform an actual copy of the data into mbuf
359 * chain. Functions which have le or be suffixes will perform conversion to
360 * the little- or big-endian data formats.
362 * Inline version of mb_put_mem(). Handles the easy case in-line,
363 * and calls mb_put_mem() if crossing mblk boundaries, etc.
365 * We build in a strage way, which may cause these inline functions to not
366 * be inlined. Using macros instead for now.
371 mb_put_inline(struct mbchain
*mbp
, void *src
, int size
)
373 mblk_t
*m
= mbp
->mb_cur
;
375 if (m
!= NULL
&& size
<= MBLKTAIL(m
)) {
379 *(m
->b_wptr
)++ = *p
++;
380 mbp
->mb_count
+= size
;
383 return (mb_put_mem(mbp
, src
, size
, MB_MINLINE
));
385 #define MB_PUT_INLINE(MBP, SRC, SZ) \
386 return (mb_put_inline(MBP, SRC, SZ))
388 #else /* INLINE_WORKS */
390 #define MB_PUT_INLINE(MBP, SRC, SZ) \
391 mblk_t *m = MBP->mb_cur; \
392 if (m != NULL && SZ <= MBLKTAIL(m)) { \
393 uchar_t *p = (void *) SRC; \
396 *(m->b_wptr)++ = *p++; \
397 MBP->mb_count += SZ; \
400 return (mb_put_mem(MBP, SRC, SZ, MB_MINLINE))
402 #endif /* INLINE_WORKS */
405 * Assumes total data length in previous mblks is EVEN.
406 * Might need to compute the offset from mb_top instead.
409 mb_put_padbyte(struct mbchain
*mbp
)
414 dst
= (uintptr_t)mbp
->mb_cur
->b_wptr
;
415 /* only add padding if address is odd */
417 MB_PUT_INLINE(mbp
, &v
, sizeof (v
));
424 mb_put_uint8(struct mbchain
*mbp
, u_int8_t x
)
427 MB_PUT_INLINE(mbp
, &v
, sizeof (v
));
431 mb_put_uint16be(struct mbchain
*mbp
, u_int16_t x
)
433 u_int16_t v
= htobes(x
);
434 MB_PUT_INLINE(mbp
, &v
, sizeof (v
));
438 mb_put_uint16le(struct mbchain
*mbp
, u_int16_t x
)
440 u_int16_t v
= htoles(x
);
441 MB_PUT_INLINE(mbp
, &v
, sizeof (v
));
445 mb_put_uint32be(struct mbchain
*mbp
, u_int32_t x
)
447 u_int32_t v
= htobel(x
);
448 MB_PUT_INLINE(mbp
, &v
, sizeof (v
));
452 mb_put_uint32le(struct mbchain
*mbp
, u_int32_t x
)
454 u_int32_t v
= htolel(x
);
455 MB_PUT_INLINE(mbp
, &v
, sizeof (v
));
459 mb_put_uint64be(struct mbchain
*mbp
, u_int64_t x
)
461 u_int64_t v
= htobeq(x
);
462 MB_PUT_INLINE(mbp
, &v
, sizeof (v
));
466 mb_put_uint64le(struct mbchain
*mbp
, u_int64_t x
)
468 u_int64_t v
= htoleq(x
);
469 MB_PUT_INLINE(mbp
, &v
, sizeof (v
));
473 * mb_put_mem() function copies size bytes of data specified by the source
474 * argument to an mbuf chain. The type argument specifies the method used
478 mb_put_mem(struct mbchain
*mbp
, const void *vsrc
, int size
, int type
)
480 mblk_t
*n
, *m
= mbp
->mb_cur
;
481 c_caddr_t source
= vsrc
;
485 int cplen
, mleft
, count
;
488 ASSERT(diff
== (uint64_t)((int)diff
));
493 if (m
->b_cont
== NULL
) {
495 * Changed m_getm() to m_getblk()
496 * with the requested size, so we
497 * don't need m_getm() anymore.
499 n
= m_getblk(size
, 1);
506 ASSERT(diff
== (uint64_t)((int)diff
));
510 cplen
= mleft
> size
? size
: mleft
;
511 dst
= (caddr_t
)m
->b_wptr
;
514 for (src
= source
, count
= cplen
; count
; count
--)
518 bcopy(source
, dst
, cplen
);
521 if (copyin((void *)source
, dst
, cplen
))
532 mbp
->mb_count
+= cplen
;
539 * Append an mblk to the chain.
542 mb_put_mbuf(struct mbchain
*mbp
, mblk_t
*m
)
544 mblk_t
*nm
, *tail_mb
;
548 tail_mb
= mbp
->mb_cur
;
549 while (tail_mb
->b_cont
!= NULL
)
550 tail_mb
= tail_mb
->b_cont
;
553 * Avoid small frags: Only link if the size of the
554 * new mbuf is larger than the space left in the last
555 * mblk of the chain (tail), otherwise just copy.
559 if (size
> MBLKTAIL(tail_mb
)) {
563 mbp
->mb_count
+= msgdsize(m
);
567 bcopy(m
->b_rptr
, tail_mb
->b_wptr
, size
);
568 tail_mb
->b_wptr
+= size
;
569 mbp
->mb_count
+= size
;
579 * copies a uio scatter/gather list to an mbuf chain.
582 mb_put_uio(struct mbchain
*mbp
, uio_t
*uiop
, size_t size
)
587 mtype
= (uio_isuserspace(uiop
) ? MB_MUSER
: MB_MSYSTEM
);
588 while (size
> 0 && uiop
->uio_resid
) {
589 if (uiop
->uio_iovcnt
<= 0 ||
590 uio_curriovbase(uiop
) == USER_ADDR_NULL
)
592 left
= uio_curriovlen(uiop
);
595 error
= mb_put_mem(mbp
, CAST_DOWN(caddr_t
,
596 uio_curriovbase(uiop
)), left
, mtype
);
599 uio_update(uiop
, left
);
606 * Routines for fetching data from an mbuf chain
610 md_initm(struct mdchain
*mdp
, mblk_t
*m
)
612 bzero(mdp
, sizeof (*mdp
));
613 mdp
->md_top
= mdp
->md_cur
= m
;
614 mdp
->md_pos
= m
->b_rptr
;
618 md_done(struct mdchain
*mdp
)
623 * Deal with the fact that we can error out of
624 * smb_t2_reply or smb_nt_reply without using up
625 * all the "records" added by md_append_record().
627 while ((m
= mdp
->md_top
) != NULL
) {
628 mdp
->md_top
= m
->b_next
;
632 /* Avoid dangling references */
638 * Append a new message (separate mbuf chain).
639 * It is caller responsibility to prevent
640 * multiple calls to fetch/record routines.
641 * Note unusual use of mblk->b_next here.
644 md_append_record(struct mdchain
*mdp
, mblk_t
*top
)
649 if (mdp
->md_top
== NULL
) {
654 /* Get to last message (not b_cont chain) */
661 * Advance mdp->md_top to the next message.
662 * Note unusual use of mblk->b_next here.
665 md_next_record(struct mdchain
*mdp
)
669 if ((top
= mdp
->md_top
) == NULL
)
673 * Get the next message, if any,
674 * stored by md_append_record.
675 * Note: NOT b_cont chain
680 /* Done with old "top". */
685 /* Setup new "top". */
690 * Inline version of md_get_mem(). Handles the easy case in-line,
691 * and calls md_get_mem() if crossing mblk boundaries, etc.
693 #ifdef INLINE_WORKS /* see above */
696 md_get_inline(struct mdchain
*mdp
, void *dst
, int size
)
698 mblk_t
*m
= mdp
->md_cur
;
700 if (m
!= NULL
&& mdp
->md_pos
+ size
<= m
->b_wptr
) {
704 *p
++ = *(mdp
->md_pos
)++;
705 /* no md_count += size */
708 return (md_get_mem(mdp
, dst
, size
, MB_MINLINE
));
710 #define MD_GET_INLINE(MDP, DST, SZ) \
711 error = md_get_inline(MDP, DST, SZ)
713 #else /* INLINE_WORKS */
715 /* Note, sets variable: error */
716 #define MD_GET_INLINE(MDP, DST, SZ) \
717 mblk_t *m = MDP->md_cur; \
718 if (m != NULL && MDP->md_pos + SZ <= m->b_wptr) { \
719 uchar_t *p = (void *) DST; \
722 *p++ = *(mdp->md_pos)++; \
723 /* no md_count += SZ */ \
726 error = md_get_mem(MDP, DST, SZ, MB_MINLINE)
728 #endif /* INLINE_WORKS */
732 md_get_uint8(struct mdchain
*mdp
, u_int8_t
*x
)
737 MD_GET_INLINE(mdp
, &v
, sizeof (v
));
744 md_get_uint16be(struct mdchain
*mdp
, u_int16_t
*x
) {
748 MD_GET_INLINE(mdp
, &v
, sizeof (v
));
755 md_get_uint16le(struct mdchain
*mdp
, u_int16_t
*x
)
760 MD_GET_INLINE(mdp
, &v
, sizeof (v
));
767 md_get_uint32be(struct mdchain
*mdp
, u_int32_t
*x
)
772 MD_GET_INLINE(mdp
, &v
, sizeof (v
));
779 md_get_uint32le(struct mdchain
*mdp
, u_int32_t
*x
)
784 MD_GET_INLINE(mdp
, &v
, sizeof (v
));
791 md_get_uint64be(struct mdchain
*mdp
, u_int64_t
*x
)
796 MD_GET_INLINE(mdp
, &v
, sizeof (v
));
803 md_get_uint64le(struct mdchain
*mdp
, u_int64_t
*x
)
808 MD_GET_INLINE(mdp
, &v
, sizeof (v
));
815 md_get_mem(struct mdchain
*mdp
, void *vdst
, int size
, int type
)
817 mblk_t
*m
= mdp
->md_cur
;
818 caddr_t target
= vdst
;
825 SMBSDEBUG("incomplete copy\n");
830 * Offset in the current MBUF.
833 ASSERT((m
->b_rptr
<= s
) && (s
<= m
->b_wptr
));
835 /* Data remaining. */
836 diff
= (uintptr_t)m
->b_wptr
- (uintptr_t)s
;
837 ASSERT(diff
== (uint64_t)((int)diff
));
841 * Check if the no. of bytes remaining is less than
842 * the bytes requested.
848 mdp
->md_pos
= s
= m
->b_rptr
;
855 mdp
->md_pos
+= count
;
860 if (copyout(s
, target
, count
))
864 bcopy(s
, target
, count
);
877 * Get the next SIZE bytes as a separate mblk.
880 md_get_mbuf(struct mdchain
*mdp
, int size
, mblk_t
**ret
)
889 * Offset in the current MBUF.
893 ASSERT((m
->b_rptr
<= s
) && (s
<= m
->b_wptr
));
894 diff
= (uintptr_t)s
- (uintptr_t)m
->b_rptr
;
895 ASSERT(diff
== (uint64_t)((int)diff
));
898 rm
= m_copym(m
, off
, size
, M_WAITOK
);
907 md_get_uio(struct mdchain
*mdp
, uio_t
*uiop
, size_t size
)
912 mtype
= (uio_isuserspace(uiop
) ? MB_MUSER
: MB_MSYSTEM
);
913 while (size
> 0 && uiop
->uio_resid
) {
914 if (uiop
->uio_iovcnt
<= 0 ||
915 uio_curriovbase(uiop
) == USER_ADDR_NULL
)
917 left
= uio_curriovlen(uiop
);
920 error
= md_get_mem(mdp
, CAST_DOWN(caddr_t
,
921 uio_curriovbase(uiop
)), left
, mtype
);
924 uio_update(uiop
, left
);
931 * Additions for Solaris
935 * concatenate mblk chain n to m.
936 * go till end of data in m.
937 * then add the link of b_cont to n.
955 m_copym(mblk_t
*m
, int off
, int len
, int wait
)
962 if (len
== M_COPYALL
) {
966 if ((off
+ len
) > dsz
)
970 if ((n
= dupmsg(m
)) == NULL
)
975 if (!adjmsg(n
, adj
)) {
981 if (len
!= M_COPYALL
) {
985 adj
= (ssize_t
)len
- (ssize_t
)dsz
;
987 (void) adjmsg(n
, adj
);
995 * Get "rqlen" contiguous bytes into the first mblk of a chain.
1005 ASSERT(diff
== (ptrdiff_t)((int)diff
));
1006 if ((int)diff
< rqlen
) {
1007 /* This should be rare. */
1008 if (!pullupmsg(m
, rqlen
)) {
1009 SMBSDEBUG("pullupmsg failed!\n");
1019 * m_split : split the mblk from the offset(len0) to the end.
1020 * Partition an mbuf chain in two pieces, returning the tail --
1021 * all but the first len0 bytes. In case of failure, it returns NULL and
1022 * attempts to restore the chain to its original state.
1023 * Similar to dupmsg() + adjmsg() on Solaris.
1033 int mbl
, len
= len0
;
1036 #if 0 /* If life were simple, this would be: */
1037 for (m
= m0
; m
&& len
> MBLKL(m
); m
= m
->b_cont
)
1039 #else /* but with LP64 and picky lint we have: */
1040 for (m
= m0
; m
; m
= m
->b_cont
) {
1042 ASSERT(diff
== (ptrdiff_t)((int)diff
));
1053 /* This is the one to split (dupb, adjust) */
1054 if ((n
= dupb(m
)) == 0)
1057 ASSERT(len
<= MBLKL(m
));
1059 m
->b_wptr
= m
->b_rptr
+ len
;
1062 /* Move any b_cont (tail) to the new head. */
1063 n
->b_cont
= m
->b_cont
;