michael@0: /*- michael@0: * Copyright (c) 1982, 1986, 1988, 1993 michael@0: * The Regents of the University of California. michael@0: * All rights reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * 3. Neither the name of the University nor the names of its contributors michael@0: * may be used to endorse or promote products derived from this software michael@0: * without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND michael@0: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE michael@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE michael@0: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE michael@0: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL michael@0: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS michael@0: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) michael@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT michael@0: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY michael@0: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF michael@0: * SUCH DAMAGE. michael@0: * michael@0: */ michael@0: michael@0: /* michael@0: * __Userspace__ version of /usr/src/sys/kern/kern_mbuf.c michael@0: * We are initializing two zones for Mbufs and Clusters. michael@0: * michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: /* #include This defines MSIZE 256 */ michael@0: #if !defined(SCTP_SIMPLE_ALLOCATOR) michael@0: #include "umem.h" michael@0: #endif michael@0: #include "user_mbuf.h" michael@0: #include "user_environment.h" michael@0: #include "user_atomic.h" michael@0: #include "netinet/sctp_pcb.h" michael@0: michael@0: struct mbstat mbstat; michael@0: #define KIPC_MAX_LINKHDR 4 /* int: max length of link header (see sys/sysclt.h) */ michael@0: #define KIPC_MAX_PROTOHDR 5 /* int: max length of network header (see sys/sysclt.h)*/ michael@0: int max_linkhdr = KIPC_MAX_LINKHDR; michael@0: int max_protohdr = KIPC_MAX_PROTOHDR; /* Size of largest protocol layer header. */ michael@0: michael@0: /* michael@0: * Zones from which we allocate. michael@0: */ michael@0: sctp_zone_t zone_mbuf; michael@0: sctp_zone_t zone_clust; michael@0: sctp_zone_t zone_ext_refcnt; michael@0: michael@0: /* __Userspace__ clust_mb_args will be passed as callback data to mb_ctor_clust michael@0: * and mb_dtor_clust. michael@0: * Note: I had to use struct clust_args as an encapsulation for an mbuf pointer. michael@0: * struct mbuf * clust_mb_args; does not work. michael@0: */ michael@0: struct clust_args clust_mb_args; michael@0: michael@0: michael@0: /* __Userspace__ michael@0: * Local prototypes. michael@0: */ michael@0: static int mb_ctor_mbuf(void *, void *, int); michael@0: static int mb_ctor_clust(void *, void *, int); michael@0: static void mb_dtor_mbuf(void *, void *); michael@0: static void mb_dtor_clust(void *, void *); michael@0: michael@0: michael@0: /***************** Functions taken from user_mbuf.h *************/ michael@0: michael@0: static int mbuf_constructor_dup(struct mbuf *m, int pkthdr, short type) michael@0: { michael@0: int flags = pkthdr; michael@0: if (type == MT_NOINIT) michael@0: return (0); michael@0: michael@0: m->m_next = NULL; michael@0: m->m_nextpkt = NULL; michael@0: m->m_len = 0; michael@0: m->m_flags = flags; michael@0: m->m_type = type; michael@0: if (flags & M_PKTHDR) { michael@0: m->m_data = m->m_pktdat; michael@0: m->m_pkthdr.rcvif = NULL; michael@0: m->m_pkthdr.len = 0; michael@0: m->m_pkthdr.header = NULL; michael@0: m->m_pkthdr.csum_flags = 0; michael@0: m->m_pkthdr.csum_data = 0; michael@0: m->m_pkthdr.tso_segsz = 0; michael@0: m->m_pkthdr.ether_vtag = 0; michael@0: SLIST_INIT(&m->m_pkthdr.tags); michael@0: } else michael@0: m->m_data = m->m_dat; michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: /* __Userspace__ */ michael@0: struct mbuf * michael@0: m_get(int how, short type) michael@0: { michael@0: struct mbuf *mret; michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: struct mb_args mbuf_mb_args; michael@0: michael@0: /* The following setter function is not yet being enclosed within michael@0: * #if USING_MBUF_CONSTRUCTOR - #endif, until I have thoroughly tested michael@0: * mb_dtor_mbuf. See comment there michael@0: */ michael@0: mbuf_mb_args.flags = 0; michael@0: mbuf_mb_args.type = type; michael@0: #endif michael@0: /* Mbuf master zone, zone_mbuf, has already been michael@0: * created in mbuf_init() */ michael@0: mret = SCTP_ZONE_GET(zone_mbuf, struct mbuf); michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: mb_ctor_mbuf(mret, &mbuf_mb_args, 0); michael@0: #endif michael@0: /*mret = ((struct mbuf *)umem_cache_alloc(zone_mbuf, UMEM_DEFAULT));*/ michael@0: michael@0: /* There are cases when an object available in the current CPU's michael@0: * loaded magazine and in those cases the object's constructor is not applied. michael@0: * If that is the case, then we are duplicating constructor initialization here, michael@0: * so that the mbuf is properly constructed before returning it. michael@0: */ michael@0: if (mret) { michael@0: #if USING_MBUF_CONSTRUCTOR michael@0: if (! (mret->m_type == type) ) { michael@0: mbuf_constructor_dup(mret, 0, type); michael@0: } michael@0: #else michael@0: mbuf_constructor_dup(mret, 0, type); michael@0: #endif michael@0: michael@0: } michael@0: return mret; michael@0: } michael@0: michael@0: michael@0: /* __Userspace__ */ michael@0: struct mbuf * michael@0: m_gethdr(int how, short type) michael@0: { michael@0: struct mbuf *mret; michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: struct mb_args mbuf_mb_args; michael@0: michael@0: /* The following setter function is not yet being enclosed within michael@0: * #if USING_MBUF_CONSTRUCTOR - #endif, until I have thoroughly tested michael@0: * mb_dtor_mbuf. See comment there michael@0: */ michael@0: mbuf_mb_args.flags = M_PKTHDR; michael@0: mbuf_mb_args.type = type; michael@0: #endif michael@0: mret = SCTP_ZONE_GET(zone_mbuf, struct mbuf); michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: mb_ctor_mbuf(mret, &mbuf_mb_args, 0); michael@0: #endif michael@0: /*mret = ((struct mbuf *)umem_cache_alloc(zone_mbuf, UMEM_DEFAULT));*/ michael@0: /* There are cases when an object available in the current CPU's michael@0: * loaded magazine and in those cases the object's constructor is not applied. michael@0: * If that is the case, then we are duplicating constructor initialization here, michael@0: * so that the mbuf is properly constructed before returning it. michael@0: */ michael@0: if (mret) { michael@0: #if USING_MBUF_CONSTRUCTOR michael@0: if (! ((mret->m_flags & M_PKTHDR) && (mret->m_type == type)) ) { michael@0: mbuf_constructor_dup(mret, M_PKTHDR, type); michael@0: } michael@0: #else michael@0: mbuf_constructor_dup(mret, M_PKTHDR, type); michael@0: #endif michael@0: } michael@0: return mret; michael@0: } michael@0: michael@0: /* __Userspace__ */ michael@0: struct mbuf * michael@0: m_free(struct mbuf *m) michael@0: { michael@0: michael@0: struct mbuf *n = m->m_next; michael@0: michael@0: if (m->m_flags & M_EXT) michael@0: mb_free_ext(m); michael@0: else if ((m->m_flags & M_NOFREE) == 0) { michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: mb_dtor_mbuf(m, NULL); michael@0: #endif michael@0: SCTP_ZONE_FREE(zone_mbuf, m); michael@0: } michael@0: /*umem_cache_free(zone_mbuf, m);*/ michael@0: return (n); michael@0: } michael@0: michael@0: michael@0: static int clust_constructor_dup(caddr_t m_clust, struct mbuf* m) michael@0: { michael@0: u_int *refcnt; michael@0: int type, size; michael@0: michael@0: /* Assigning cluster of MCLBYTES. TODO: Add jumbo frame functionality */ michael@0: type = EXT_CLUSTER; michael@0: size = MCLBYTES; michael@0: michael@0: refcnt = SCTP_ZONE_GET(zone_ext_refcnt, u_int); michael@0: /*refcnt = (u_int *)umem_cache_alloc(zone_ext_refcnt, UMEM_DEFAULT);*/ michael@0: if (refcnt == NULL) { michael@0: #if !defined(SCTP_SIMPLE_ALLOCATOR) michael@0: umem_reap(); michael@0: #endif michael@0: refcnt = SCTP_ZONE_GET(zone_ext_refcnt, u_int); michael@0: /*refcnt = (u_int *)umem_cache_alloc(zone_ext_refcnt, UMEM_DEFAULT);*/ michael@0: } michael@0: *refcnt = 1; michael@0: if (m != NULL) { michael@0: m->m_ext.ext_buf = (caddr_t)m_clust; michael@0: m->m_data = m->m_ext.ext_buf; michael@0: m->m_flags |= M_EXT; michael@0: m->m_ext.ext_free = NULL; michael@0: m->m_ext.ext_args = NULL; michael@0: m->m_ext.ext_size = size; michael@0: m->m_ext.ext_type = type; michael@0: m->m_ext.ref_cnt = refcnt; michael@0: } michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: michael@0: michael@0: /* __Userspace__ */ michael@0: void michael@0: m_clget(struct mbuf *m, int how) michael@0: { michael@0: caddr_t mclust_ret; michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: struct clust_args clust_mb_args; michael@0: #endif michael@0: if (m->m_flags & M_EXT) { michael@0: SCTPDBG(SCTP_DEBUG_USR, "%s: %p mbuf already has cluster\n", __func__, (void *)m); michael@0: } michael@0: m->m_ext.ext_buf = (char *)NULL; michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: clust_mb_args.parent_mbuf = m; michael@0: #endif michael@0: mclust_ret = SCTP_ZONE_GET(zone_clust, char); michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: mb_ctor_clust(mclust_ret, &clust_mb_args, 0); michael@0: #endif michael@0: /*mclust_ret = umem_cache_alloc(zone_clust, UMEM_DEFAULT);*/ michael@0: /* michael@0: On a cluster allocation failure, call umem_reap() and retry. michael@0: */ michael@0: michael@0: if (mclust_ret == NULL) { michael@0: #if !defined(SCTP_SIMPLE_ALLOCATOR) michael@0: /* mclust_ret = SCTP_ZONE_GET(zone_clust, char); michael@0: mb_ctor_clust(mclust_ret, &clust_mb_args, 0); michael@0: #else*/ michael@0: umem_reap(); michael@0: mclust_ret = SCTP_ZONE_GET(zone_clust, char); michael@0: #endif michael@0: /*mclust_ret = umem_cache_alloc(zone_clust, UMEM_DEFAULT);*/ michael@0: if (NULL == mclust_ret) { michael@0: SCTPDBG(SCTP_DEBUG_USR, "Memory allocation failure in %s\n", __func__); michael@0: } michael@0: } michael@0: michael@0: #if USING_MBUF_CONSTRUCTOR michael@0: if ((m->m_ext.ext_buf == NULL)) { michael@0: clust_constructor_dup(mclust_ret, m); michael@0: } michael@0: #else michael@0: clust_constructor_dup(mclust_ret, m); michael@0: #endif michael@0: } michael@0: michael@0: /* michael@0: * Unlink a tag from the list of tags associated with an mbuf. michael@0: */ michael@0: static __inline void michael@0: m_tag_unlink(struct mbuf *m, struct m_tag *t) michael@0: { michael@0: michael@0: SLIST_REMOVE(&m->m_pkthdr.tags, t, m_tag, m_tag_link); michael@0: } michael@0: michael@0: /* michael@0: * Reclaim resources associated with a tag. michael@0: */ michael@0: static __inline void michael@0: m_tag_free(struct m_tag *t) michael@0: { michael@0: michael@0: (*t->m_tag_free)(t); michael@0: } michael@0: michael@0: /* michael@0: * Set up the contents of a tag. Note that this does not fill in the free michael@0: * method; the caller is expected to do that. michael@0: * michael@0: * XXX probably should be called m_tag_init, but that was already taken. michael@0: */ michael@0: static __inline void michael@0: m_tag_setup(struct m_tag *t, u_int32_t cookie, int type, int len) michael@0: { michael@0: michael@0: t->m_tag_id = type; michael@0: t->m_tag_len = len; michael@0: t->m_tag_cookie = cookie; michael@0: } michael@0: michael@0: /************ End functions from user_mbuf.h ******************/ michael@0: michael@0: michael@0: michael@0: /************ End functions to substitute umem_cache_alloc and umem_cache_free **************/ michael@0: michael@0: /* __Userspace__ michael@0: * TODO: mbuf_init must be called in the initialization routines michael@0: * of userspace stack. michael@0: */ michael@0: void michael@0: mbuf_init(void *dummy) michael@0: { michael@0: michael@0: /* michael@0: * __Userspace__Configure UMA zones for Mbufs and Clusters. michael@0: * (TODO: m_getcl() - using packet secondary zone). michael@0: * There is no provision for trash_init and trash_fini in umem. michael@0: * michael@0: */ michael@0: /* zone_mbuf = umem_cache_create(MBUF_MEM_NAME, MSIZE, 0, michael@0: mb_ctor_mbuf, mb_dtor_mbuf, NULL, michael@0: &mbuf_mb_args, michael@0: NULL, 0); michael@0: zone_mbuf = umem_cache_create(MBUF_MEM_NAME, MSIZE, 0, NULL, NULL, NULL, NULL, NULL, 0);*/ michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: SCTP_ZONE_INIT(zone_mbuf, MBUF_MEM_NAME, MSIZE, 0); michael@0: #else michael@0: zone_mbuf = umem_cache_create(MBUF_MEM_NAME, MSIZE, 0, michael@0: mb_ctor_mbuf, mb_dtor_mbuf, NULL, michael@0: NUULL, michael@0: NULL, 0); michael@0: #endif michael@0: /*zone_ext_refcnt = umem_cache_create(MBUF_EXTREFCNT_MEM_NAME, sizeof(u_int), 0, michael@0: NULL, NULL, NULL, michael@0: NULL, michael@0: NULL, 0);*/ michael@0: SCTP_ZONE_INIT(zone_ext_refcnt, MBUF_EXTREFCNT_MEM_NAME, sizeof(u_int), 0); michael@0: michael@0: /*zone_clust = umem_cache_create(MBUF_CLUSTER_MEM_NAME, MCLBYTES, 0, michael@0: mb_ctor_clust, mb_dtor_clust, NULL, michael@0: &clust_mb_args, michael@0: NULL, 0); michael@0: zone_clust = umem_cache_create(MBUF_CLUSTER_MEM_NAME, MCLBYTES, 0, NULL, NULL, NULL, NULL, NULL,0);*/ michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: SCTP_ZONE_INIT(zone_clust, MBUF_CLUSTER_MEM_NAME, MCLBYTES, 0); michael@0: #else michael@0: zone_clust = umem_cache_create(MBUF_CLUSTER_MEM_NAME, MCLBYTES, 0, michael@0: mb_ctor_clust, mb_dtor_clust, NULL, michael@0: &clust_mb_args, michael@0: NULL, 0); michael@0: #endif michael@0: michael@0: /* uma_prealloc() goes here... */ michael@0: michael@0: /* __Userspace__ Add umem_reap here for low memory situation? michael@0: * michael@0: */ michael@0: michael@0: michael@0: /* michael@0: * [Re]set counters and local statistics knobs. michael@0: * michael@0: */ michael@0: michael@0: mbstat.m_mbufs = 0; michael@0: mbstat.m_mclusts = 0; michael@0: mbstat.m_drain = 0; michael@0: mbstat.m_msize = MSIZE; michael@0: mbstat.m_mclbytes = MCLBYTES; michael@0: mbstat.m_minclsize = MINCLSIZE; michael@0: mbstat.m_mlen = MLEN; michael@0: mbstat.m_mhlen = MHLEN; michael@0: mbstat.m_numtypes = MT_NTYPES; michael@0: michael@0: mbstat.m_mcfail = mbstat.m_mpfail = 0; michael@0: mbstat.sf_iocnt = 0; michael@0: mbstat.sf_allocwait = mbstat.sf_allocfail = 0; michael@0: michael@0: } michael@0: michael@0: michael@0: michael@0: /* michael@0: * __Userspace__ michael@0: * michael@0: * Constructor for Mbuf master zone. We have a different constructor michael@0: * for allocating the cluster. michael@0: * michael@0: * The 'arg' pointer points to a mb_args structure which michael@0: * contains call-specific information required to support the michael@0: * mbuf allocation API. See user_mbuf.h. michael@0: * michael@0: * The flgs parameter below can be UMEM_DEFAULT or UMEM_NOFAIL depending on what michael@0: * was passed when umem_cache_alloc was called. michael@0: * TODO: Use UMEM_NOFAIL in umem_cache_alloc and also define a failure handler michael@0: * and call umem_nofail_callback(my_failure_handler) in the stack initialization routines michael@0: * The advantage of using UMEM_NOFAIL is that we don't have to check if umem_cache_alloc michael@0: * was successful or not. The failure handler would take care of it, if we use the UMEM_NOFAIL michael@0: * flag. michael@0: * michael@0: * NOTE Ref: http://docs.sun.com/app/docs/doc/819-2243/6n4i099p2?l=en&a=view&q=umem_zalloc) michael@0: * The umem_nofail_callback() function sets the **process-wide** UMEM_NOFAIL callback. michael@0: * It also mentions that umem_nofail_callback is Evolving. michael@0: * michael@0: */ michael@0: static int michael@0: mb_ctor_mbuf(void *mem, void *arg, int flgs) michael@0: { michael@0: #if USING_MBUF_CONSTRUCTOR michael@0: struct mbuf *m; michael@0: struct mb_args *args; michael@0: michael@0: int flags; michael@0: short type; michael@0: michael@0: m = (struct mbuf *)mem; michael@0: args = (struct mb_args *)arg; michael@0: flags = args->flags; michael@0: type = args->type; michael@0: michael@0: /* michael@0: * The mbuf is initialized later. michael@0: * michael@0: */ michael@0: if (type == MT_NOINIT) michael@0: return (0); michael@0: michael@0: m->m_next = NULL; michael@0: m->m_nextpkt = NULL; michael@0: m->m_len = 0; michael@0: m->m_flags = flags; michael@0: m->m_type = type; michael@0: if (flags & M_PKTHDR) { michael@0: m->m_data = m->m_pktdat; michael@0: m->m_pkthdr.rcvif = NULL; michael@0: m->m_pkthdr.len = 0; michael@0: m->m_pkthdr.header = NULL; michael@0: m->m_pkthdr.csum_flags = 0; michael@0: m->m_pkthdr.csum_data = 0; michael@0: m->m_pkthdr.tso_segsz = 0; michael@0: m->m_pkthdr.ether_vtag = 0; michael@0: SLIST_INIT(&m->m_pkthdr.tags); michael@0: } else michael@0: m->m_data = m->m_dat; michael@0: #endif michael@0: return (0); michael@0: } michael@0: michael@0: michael@0: /* michael@0: * __Userspace__ michael@0: * The Mbuf master zone destructor. michael@0: * This would be called in response to umem_cache_destroy michael@0: * TODO: Recheck if this is what we want to do in this destructor. michael@0: * (Note: the number of times mb_dtor_mbuf is called is equal to the michael@0: * number of individual mbufs allocated from zone_mbuf. michael@0: */ michael@0: static void michael@0: mb_dtor_mbuf(void *mem, void *arg) michael@0: { michael@0: struct mbuf *m; michael@0: michael@0: m = (struct mbuf *)mem; michael@0: if ((m->m_flags & M_PKTHDR) != 0) { michael@0: m_tag_delete_chain(m, NULL); michael@0: } michael@0: } michael@0: michael@0: michael@0: /* __Userspace__ michael@0: * The Cluster zone constructor. michael@0: * michael@0: * Here the 'arg' pointer points to the Mbuf which we michael@0: * are configuring cluster storage for. If 'arg' is michael@0: * empty we allocate just the cluster without setting michael@0: * the mbuf to it. See mbuf.h. michael@0: */ michael@0: static int michael@0: mb_ctor_clust(void *mem, void *arg, int flgs) michael@0: { michael@0: michael@0: #if USING_MBUF_CONSTRUCTOR michael@0: struct mbuf *m; michael@0: struct clust_args * cla; michael@0: u_int *refcnt; michael@0: int type, size; michael@0: sctp_zone_t zone; michael@0: michael@0: /* Assigning cluster of MCLBYTES. TODO: Add jumbo frame functionality */ michael@0: type = EXT_CLUSTER; michael@0: zone = zone_clust; michael@0: size = MCLBYTES; michael@0: michael@0: cla = (struct clust_args *)arg; michael@0: m = cla->parent_mbuf; michael@0: michael@0: refcnt = SCTP_ZONE_GET(zone_ext_refcnt, u_int); michael@0: /*refcnt = (u_int *)umem_cache_alloc(zone_ext_refcnt, UMEM_DEFAULT);*/ michael@0: *refcnt = 1; michael@0: michael@0: if (m != NULL) { michael@0: m->m_ext.ext_buf = (caddr_t)mem; michael@0: m->m_data = m->m_ext.ext_buf; michael@0: m->m_flags |= M_EXT; michael@0: m->m_ext.ext_free = NULL; michael@0: m->m_ext.ext_args = NULL; michael@0: m->m_ext.ext_size = size; michael@0: m->m_ext.ext_type = type; michael@0: m->m_ext.ref_cnt = refcnt; michael@0: } michael@0: #endif michael@0: return (0); michael@0: } michael@0: michael@0: /* __Userspace__ */ michael@0: static void michael@0: mb_dtor_clust(void *mem, void *arg) michael@0: { michael@0: michael@0: /* mem is of type caddr_t. In sys/types.h we have typedef char * caddr_t; */ michael@0: /* mb_dtor_clust is called at time of umem_cache_destroy() (the number of times michael@0: * mb_dtor_clust is called is equal to the number of individual mbufs allocated michael@0: * from zone_clust. Similarly for mb_dtor_mbuf). michael@0: * At this point the following: michael@0: * struct mbuf *m; michael@0: * m = (struct mbuf *)arg; michael@0: * assert (*(m->m_ext.ref_cnt) == 0); is not meaningful since m->m_ext.ref_cnt = NULL; michael@0: * has been done in mb_free_ext(). michael@0: */ michael@0: michael@0: } michael@0: michael@0: michael@0: michael@0: michael@0: /* Unlink and free a packet tag. */ michael@0: void michael@0: m_tag_delete(struct mbuf *m, struct m_tag *t) michael@0: { michael@0: KASSERT(m && t, ("m_tag_delete: null argument, m %p t %p", (void *)m, (void *)t)); michael@0: m_tag_unlink(m, t); michael@0: m_tag_free(t); michael@0: } michael@0: michael@0: michael@0: /* Unlink and free a packet tag chain, starting from given tag. */ michael@0: void michael@0: m_tag_delete_chain(struct mbuf *m, struct m_tag *t) michael@0: { michael@0: michael@0: struct m_tag *p, *q; michael@0: michael@0: KASSERT(m, ("m_tag_delete_chain: null mbuf")); michael@0: if (t != NULL) michael@0: p = t; michael@0: else michael@0: p = SLIST_FIRST(&m->m_pkthdr.tags); michael@0: if (p == NULL) michael@0: return; michael@0: while ((q = SLIST_NEXT(p, m_tag_link)) != NULL) michael@0: m_tag_delete(m, q); michael@0: m_tag_delete(m, p); michael@0: } michael@0: michael@0: #if 0 michael@0: static void michael@0: sctp_print_mbuf_chain(struct mbuf *m) michael@0: { michael@0: SCTP_DEBUG_USR(SCTP_DEBUG_USR, "Printing mbuf chain %p.\n", (void *)m); michael@0: for(; m; m=m->m_next) { michael@0: SCTP_DEBUG_USR(SCTP_DEBUG_USR, "%p: m_len = %ld, m_type = %x, m_next = %p.\n", (void *)m, m->m_len, m->m_type, (void *)m->m_next); michael@0: if (m->m_flags & M_EXT) michael@0: SCTP_DEBUG_USR(SCTP_DEBUG_USR, "%p: extend_size = %d, extend_buffer = %p, ref_cnt = %d.\n", (void *)m, m->m_ext.ext_size, (void *)m->m_ext.ext_buf, *(m->m_ext.ref_cnt)); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: /* michael@0: * Free an entire chain of mbufs and associated external buffers, if michael@0: * applicable. michael@0: */ michael@0: void michael@0: m_freem(struct mbuf *mb) michael@0: { michael@0: while (mb != NULL) michael@0: mb = m_free(mb); michael@0: } michael@0: michael@0: /* michael@0: * __Userspace__ michael@0: * clean mbufs with M_EXT storage attached to them michael@0: * if the reference count hits 1. michael@0: */ michael@0: void michael@0: mb_free_ext(struct mbuf *m) michael@0: { michael@0: michael@0: int skipmbuf; michael@0: michael@0: KASSERT((m->m_flags & M_EXT) == M_EXT, ("%s: M_EXT not set", __func__)); michael@0: KASSERT(m->m_ext.ref_cnt != NULL, ("%s: ref_cnt not set", __func__)); michael@0: michael@0: /* michael@0: * check if the header is embedded in the cluster michael@0: */ michael@0: skipmbuf = (m->m_flags & M_NOFREE); michael@0: michael@0: /* Free the external attached storage if this michael@0: * mbuf is the only reference to it. michael@0: *__Userspace__ TODO: jumbo frames michael@0: * michael@0: */ michael@0: /* NOTE: We had the same code that SCTP_DECREMENT_AND_CHECK_REFCOUNT michael@0: reduces to here before but the IPHONE malloc commit had changed michael@0: this to compare to 0 instead of 1 (see next line). Why? michael@0: . .. this caused a huge memory leak in Linux. michael@0: */ michael@0: #ifdef IPHONE michael@0: if (atomic_fetchadd_int(m->m_ext.ref_cnt, -1) == 0) michael@0: #else michael@0: if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(m->m_ext.ref_cnt)) michael@0: #endif michael@0: { michael@0: if (m->m_ext.ext_type == EXT_CLUSTER){ michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: mb_dtor_clust(m->m_ext.ext_buf, &clust_mb_args); michael@0: #endif michael@0: SCTP_ZONE_FREE(zone_clust, m->m_ext.ext_buf); michael@0: SCTP_ZONE_FREE(zone_ext_refcnt, (u_int*)m->m_ext.ref_cnt); michael@0: m->m_ext.ref_cnt = NULL; michael@0: } michael@0: } michael@0: michael@0: if (skipmbuf) michael@0: return; michael@0: michael@0: michael@0: /* __Userspace__ Also freeing the storage for ref_cnt michael@0: * Free this mbuf back to the mbuf zone with all m_ext michael@0: * information purged. michael@0: */ michael@0: m->m_ext.ext_buf = NULL; michael@0: m->m_ext.ext_free = NULL; michael@0: m->m_ext.ext_args = NULL; michael@0: m->m_ext.ref_cnt = NULL; michael@0: m->m_ext.ext_size = 0; michael@0: m->m_ext.ext_type = 0; michael@0: m->m_flags &= ~M_EXT; michael@0: #if defined(SCTP_SIMPLE_ALLOCATOR) michael@0: mb_dtor_mbuf(m, NULL); michael@0: #endif michael@0: SCTP_ZONE_FREE(zone_mbuf, m); michael@0: michael@0: /*umem_cache_free(zone_mbuf, m);*/ michael@0: } michael@0: michael@0: /* michael@0: * "Move" mbuf pkthdr from "from" to "to". michael@0: * "from" must have M_PKTHDR set, and "to" must be empty. michael@0: */ michael@0: void michael@0: m_move_pkthdr(struct mbuf *to, struct mbuf *from) michael@0: { michael@0: michael@0: to->m_flags = (from->m_flags & M_COPYFLAGS) | (to->m_flags & M_EXT); michael@0: if ((to->m_flags & M_EXT) == 0) michael@0: to->m_data = to->m_pktdat; michael@0: to->m_pkthdr = from->m_pkthdr; /* especially tags */ michael@0: SLIST_INIT(&from->m_pkthdr.tags); /* purge tags from src */ michael@0: from->m_flags &= ~M_PKTHDR; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Rearange an mbuf chain so that len bytes are contiguous michael@0: * and in the data area of an mbuf (so that mtod and dtom michael@0: * will work for a structure of size len). Returns the resulting michael@0: * mbuf chain on success, frees it and returns null on failure. michael@0: * If there is room, it will add up to max_protohdr-len extra bytes to the michael@0: * contiguous region in an attempt to avoid being called next time. michael@0: */ michael@0: struct mbuf * michael@0: m_pullup(struct mbuf *n, int len) michael@0: { michael@0: struct mbuf *m; michael@0: int count; michael@0: int space; michael@0: michael@0: /* michael@0: * If first mbuf has no cluster, and has room for len bytes michael@0: * without shifting current data, pullup into it, michael@0: * otherwise allocate a new mbuf to prepend to the chain. michael@0: */ michael@0: if ((n->m_flags & M_EXT) == 0 && michael@0: n->m_data + len < &n->m_dat[MLEN] && n->m_next) { michael@0: if (n->m_len >= len) michael@0: return (n); michael@0: m = n; michael@0: n = n->m_next; michael@0: len -= m->m_len; michael@0: } else { michael@0: if (len > MHLEN) michael@0: goto bad; michael@0: MGET(m, M_NOWAIT, n->m_type); michael@0: if (m == NULL) michael@0: goto bad; michael@0: m->m_len = 0; michael@0: if (n->m_flags & M_PKTHDR) michael@0: M_MOVE_PKTHDR(m, n); michael@0: } michael@0: space = &m->m_dat[MLEN] - (m->m_data + m->m_len); michael@0: do { michael@0: count = min(min(max(len, max_protohdr), space), n->m_len); michael@0: bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len, michael@0: (u_int)count); michael@0: len -= count; michael@0: m->m_len += count; michael@0: n->m_len -= count; michael@0: space -= count; michael@0: if (n->m_len) michael@0: n->m_data += count; michael@0: else michael@0: n = m_free(n); michael@0: } while (len > 0 && n); michael@0: if (len > 0) { michael@0: (void) m_free(m); michael@0: goto bad; michael@0: } michael@0: m->m_next = n; michael@0: return (m); michael@0: bad: michael@0: m_freem(n); michael@0: mbstat.m_mpfail++; /* XXX: No consistency. */ michael@0: return (NULL); michael@0: } michael@0: michael@0: michael@0: static struct mbuf * michael@0: m_dup1(struct mbuf *m, int off, int len, int wait) michael@0: { michael@0: struct mbuf *n = NULL; michael@0: int copyhdr; michael@0: michael@0: if (len > MCLBYTES) michael@0: return NULL; michael@0: if (off == 0 && (m->m_flags & M_PKTHDR) != 0) michael@0: copyhdr = 1; michael@0: else michael@0: copyhdr = 0; michael@0: if (len >= MINCLSIZE) { michael@0: if (copyhdr == 1) { michael@0: m_clget(n, wait); /* TODO: include code for copying the header */ michael@0: m_dup_pkthdr(n, m, wait); michael@0: } else michael@0: m_clget(n, wait); michael@0: } else { michael@0: if (copyhdr == 1) michael@0: n = m_gethdr(wait, m->m_type); michael@0: else michael@0: n = m_get(wait, m->m_type); michael@0: } michael@0: if (!n) michael@0: return NULL; /* ENOBUFS */ michael@0: michael@0: if (copyhdr && !m_dup_pkthdr(n, m, wait)) { michael@0: m_free(n); michael@0: return NULL; michael@0: } michael@0: m_copydata(m, off, len, mtod(n, caddr_t)); michael@0: n->m_len = len; michael@0: return n; michael@0: } michael@0: michael@0: michael@0: /* Taken from sys/kern/uipc_mbuf2.c */ michael@0: struct mbuf * michael@0: m_pulldown(struct mbuf *m, int off, int len, int *offp) michael@0: { michael@0: struct mbuf *n, *o; michael@0: int hlen, tlen, olen; michael@0: int writable; michael@0: michael@0: /* check invalid arguments. */ michael@0: KASSERT(m, ("m == NULL in m_pulldown()")); michael@0: if (len > MCLBYTES) { michael@0: m_freem(m); michael@0: return NULL; /* impossible */ michael@0: } michael@0: michael@0: #ifdef PULLDOWN_DEBUG michael@0: { michael@0: struct mbuf *t; michael@0: SCTP_DEBUG_USR(SCTP_DEBUG_USR, "before:"); michael@0: for (t = m; t; t = t->m_next) michael@0: SCTP_DEBUG_USR(SCTP_DEBUG_USR, " %d", t->m_len); michael@0: SCTP_DEBUG_USR(SCTP_DEBUG_USR, "\n"); michael@0: } michael@0: #endif michael@0: n = m; michael@0: while (n != NULL && off > 0) { michael@0: if (n->m_len > off) michael@0: break; michael@0: off -= n->m_len; michael@0: n = n->m_next; michael@0: } michael@0: /* be sure to point non-empty mbuf */ michael@0: while (n != NULL && n->m_len == 0) michael@0: n = n->m_next; michael@0: if (!n) { michael@0: m_freem(m); michael@0: return NULL; /* mbuf chain too short */ michael@0: } michael@0: michael@0: writable = 0; michael@0: if ((n->m_flags & M_EXT) == 0 || michael@0: (n->m_ext.ext_type == EXT_CLUSTER && M_WRITABLE(n))) michael@0: writable = 1; michael@0: michael@0: /* michael@0: * the target data is on . michael@0: * if we got enough data on the mbuf "n", we're done. michael@0: */ michael@0: if ((off == 0 || offp) && len <= n->m_len - off && writable) michael@0: goto ok; michael@0: michael@0: /* michael@0: * when len <= n->m_len - off and off != 0, it is a special case. michael@0: * len bytes from sits in single mbuf, but the caller does michael@0: * not like the starting position (off). michael@0: * chop the current mbuf into two pieces, set off to 0. michael@0: */ michael@0: if (len <= n->m_len - off) { michael@0: o = m_dup1(n, off, n->m_len - off, M_NOWAIT); michael@0: if (o == NULL) { michael@0: m_freem(m); michael@0: return NULL; /* ENOBUFS */ michael@0: } michael@0: n->m_len = off; michael@0: o->m_next = n->m_next; michael@0: n->m_next = o; michael@0: n = n->m_next; michael@0: off = 0; michael@0: goto ok; michael@0: } michael@0: /* michael@0: * we need to take hlen from and tlen from m_next, 0>, michael@0: * and construct contiguous mbuf with m_len == len. michael@0: * note that hlen + tlen == len, and tlen > 0. michael@0: */ michael@0: hlen = n->m_len - off; michael@0: tlen = len - hlen; michael@0: michael@0: /* michael@0: * ensure that we have enough trailing data on mbuf chain. michael@0: * if not, we can do nothing about the chain. michael@0: */ michael@0: olen = 0; michael@0: for (o = n->m_next; o != NULL; o = o->m_next) michael@0: olen += o->m_len; michael@0: if (hlen + olen < len) { michael@0: m_freem(m); michael@0: return NULL; /* mbuf chain too short */ michael@0: } michael@0: michael@0: /* michael@0: * easy cases first. michael@0: * we need to use m_copydata() to get data from m_next, 0>. michael@0: */ michael@0: if ((off == 0 || offp) && M_TRAILINGSPACE(n) >= tlen michael@0: && writable) { michael@0: m_copydata(n->m_next, 0, tlen, mtod(n, caddr_t) + n->m_len); michael@0: n->m_len += tlen; michael@0: m_adj(n->m_next, tlen); michael@0: goto ok; michael@0: } michael@0: michael@0: if ((off == 0 || offp) && M_LEADINGSPACE(n->m_next) >= hlen michael@0: && writable) { michael@0: n->m_next->m_data -= hlen; michael@0: n->m_next->m_len += hlen; michael@0: bcopy(mtod(n, caddr_t) + off, mtod(n->m_next, caddr_t), hlen); michael@0: n->m_len -= hlen; michael@0: n = n->m_next; michael@0: off = 0; michael@0: goto ok; michael@0: } michael@0: michael@0: /* michael@0: * now, we need to do the hard way. don't m_copy as there's no room michael@0: * on both end. michael@0: */ michael@0: if (len > MLEN) michael@0: m_clget(o, M_NOWAIT); michael@0: /* o = m_getcl(M_NOWAIT, m->m_type, 0);*/ michael@0: else michael@0: o = m_get(M_NOWAIT, m->m_type); michael@0: if (!o) { michael@0: m_freem(m); michael@0: return NULL; /* ENOBUFS */ michael@0: } michael@0: /* get hlen from into */ michael@0: o->m_len = hlen; michael@0: bcopy(mtod(n, caddr_t) + off, mtod(o, caddr_t), hlen); michael@0: n->m_len -= hlen; michael@0: /* get tlen from m_next, 0> into */ michael@0: m_copydata(n->m_next, 0, tlen, mtod(o, caddr_t) + o->m_len); michael@0: o->m_len += tlen; michael@0: m_adj(n->m_next, tlen); michael@0: o->m_next = n->m_next; michael@0: n->m_next = o; michael@0: n = o; michael@0: off = 0; michael@0: ok: michael@0: #ifdef PULLDOWN_DEBUG michael@0: { michael@0: struct mbuf *t; michael@0: SCTP_DEBUG_USR(SCTP_DEBUG_USR, "after:"); michael@0: for (t = m; t; t = t->m_next) michael@0: SCTP_DEBUG_USR(SCTP_DEBUG_USR, "%c%d", t == n ? '*' : ' ', t->m_len); michael@0: SCTP_DEBUG_USR(SCTP_DEBUG_USR, " (off=%d)\n", off); michael@0: } michael@0: #endif michael@0: if (offp) michael@0: *offp = off; michael@0: return n; michael@0: } michael@0: michael@0: /* michael@0: * Attach the the cluster from *m to *n, set up m_ext in *n michael@0: * and bump the refcount of the cluster. michael@0: */ michael@0: static void michael@0: mb_dupcl(struct mbuf *n, struct mbuf *m) michael@0: { michael@0: KASSERT((m->m_flags & M_EXT) == M_EXT, ("%s: M_EXT not set", __func__)); michael@0: KASSERT(m->m_ext.ref_cnt != NULL, ("%s: ref_cnt not set", __func__)); michael@0: KASSERT((n->m_flags & M_EXT) == 0, ("%s: M_EXT set", __func__)); michael@0: michael@0: if (*(m->m_ext.ref_cnt) == 1) michael@0: *(m->m_ext.ref_cnt) += 1; michael@0: else michael@0: atomic_add_int(m->m_ext.ref_cnt, 1); michael@0: n->m_ext.ext_buf = m->m_ext.ext_buf; michael@0: n->m_ext.ext_free = m->m_ext.ext_free; michael@0: n->m_ext.ext_args = m->m_ext.ext_args; michael@0: n->m_ext.ext_size = m->m_ext.ext_size; michael@0: n->m_ext.ref_cnt = m->m_ext.ref_cnt; michael@0: n->m_ext.ext_type = m->m_ext.ext_type; michael@0: n->m_flags |= M_EXT; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Make a copy of an mbuf chain starting "off0" bytes from the beginning, michael@0: * continuing for "len" bytes. If len is M_COPYALL, copy to end of mbuf. michael@0: * The wait parameter is a choice of M_TRYWAIT/M_NOWAIT from caller. michael@0: * Note that the copy is read-only, because clusters are not copied, michael@0: * only their reference counts are incremented. michael@0: */ michael@0: michael@0: struct mbuf * michael@0: m_copym(struct mbuf *m, int off0, int len, int wait) michael@0: { michael@0: struct mbuf *n, **np; michael@0: int off = off0; michael@0: struct mbuf *top; michael@0: int copyhdr = 0; michael@0: michael@0: KASSERT(off >= 0, ("m_copym, negative off %d", off)); michael@0: KASSERT(len >= 0, ("m_copym, negative len %d", len)); michael@0: michael@0: if (off == 0 && m->m_flags & M_PKTHDR) michael@0: copyhdr = 1; michael@0: while (off > 0) { michael@0: KASSERT(m != NULL, ("m_copym, offset > size of mbuf chain")); michael@0: if (off < m->m_len) michael@0: break; michael@0: off -= m->m_len; michael@0: m = m->m_next; michael@0: } michael@0: np = ⊤ michael@0: top = 0; michael@0: while (len > 0) { michael@0: if (m == NULL) { michael@0: KASSERT(len == M_COPYALL, ("m_copym, length > size of mbuf chain")); michael@0: break; michael@0: } michael@0: if (copyhdr) michael@0: MGETHDR(n, wait, m->m_type); michael@0: else michael@0: MGET(n, wait, m->m_type); michael@0: *np = n; michael@0: if (n == NULL) michael@0: goto nospace; michael@0: if (copyhdr) { michael@0: if (!m_dup_pkthdr(n, m, wait)) michael@0: goto nospace; michael@0: if (len == M_COPYALL) michael@0: n->m_pkthdr.len -= off0; michael@0: else michael@0: n->m_pkthdr.len = len; michael@0: copyhdr = 0; michael@0: } michael@0: n->m_len = min(len, m->m_len - off); michael@0: if (m->m_flags & M_EXT) { michael@0: n->m_data = m->m_data + off; michael@0: mb_dupcl(n, m); michael@0: } else michael@0: bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t), michael@0: (u_int)n->m_len); michael@0: if (len != M_COPYALL) michael@0: len -= n->m_len; michael@0: off = 0; michael@0: m = m->m_next; michael@0: np = &n->m_next; michael@0: } michael@0: if (top == NULL) michael@0: mbstat.m_mcfail++; /* XXX: No consistency. */ michael@0: michael@0: return (top); michael@0: nospace: michael@0: m_freem(top); michael@0: mbstat.m_mcfail++; /* XXX: No consistency. */ michael@0: return (NULL); michael@0: } michael@0: michael@0: michael@0: int michael@0: m_tag_copy_chain(struct mbuf *to, struct mbuf *from, int how) michael@0: { michael@0: struct m_tag *p, *t, *tprev = NULL; michael@0: michael@0: KASSERT(to && from, ("m_tag_copy_chain: null argument, to %p from %p", (void *)to, (void *)from)); michael@0: m_tag_delete_chain(to, NULL); michael@0: SLIST_FOREACH(p, &from->m_pkthdr.tags, m_tag_link) { michael@0: t = m_tag_copy(p, how); michael@0: if (t == NULL) { michael@0: m_tag_delete_chain(to, NULL); michael@0: return 0; michael@0: } michael@0: if (tprev == NULL) michael@0: SLIST_INSERT_HEAD(&to->m_pkthdr.tags, t, m_tag_link); michael@0: else michael@0: SLIST_INSERT_AFTER(tprev, t, m_tag_link); michael@0: tprev = t; michael@0: } michael@0: return 1; michael@0: } michael@0: michael@0: /* michael@0: * Duplicate "from"'s mbuf pkthdr in "to". michael@0: * "from" must have M_PKTHDR set, and "to" must be empty. michael@0: * In particular, this does a deep copy of the packet tags. michael@0: */ michael@0: int michael@0: m_dup_pkthdr(struct mbuf *to, struct mbuf *from, int how) michael@0: { michael@0: michael@0: to->m_flags = (from->m_flags & M_COPYFLAGS) | (to->m_flags & M_EXT); michael@0: if ((to->m_flags & M_EXT) == 0) michael@0: to->m_data = to->m_pktdat; michael@0: to->m_pkthdr = from->m_pkthdr; michael@0: SLIST_INIT(&to->m_pkthdr.tags); michael@0: return (m_tag_copy_chain(to, from, MBTOM(how))); michael@0: } michael@0: michael@0: /* Copy a single tag. */ michael@0: struct m_tag * michael@0: m_tag_copy(struct m_tag *t, int how) michael@0: { michael@0: struct m_tag *p; michael@0: michael@0: KASSERT(t, ("m_tag_copy: null tag")); michael@0: p = m_tag_alloc(t->m_tag_cookie, t->m_tag_id, t->m_tag_len, how); michael@0: if (p == NULL) michael@0: return (NULL); michael@0: bcopy(t + 1, p + 1, t->m_tag_len); /* Copy the data */ michael@0: return p; michael@0: } michael@0: michael@0: /* Get a packet tag structure along with specified data following. */ michael@0: struct m_tag * michael@0: m_tag_alloc(u_int32_t cookie, int type, int len, int wait) michael@0: { michael@0: struct m_tag *t; michael@0: michael@0: if (len < 0) michael@0: return NULL; michael@0: t = malloc(len + sizeof(struct m_tag)); michael@0: if (t == NULL) michael@0: return NULL; michael@0: m_tag_setup(t, cookie, type, len); michael@0: t->m_tag_free = m_tag_free_default; michael@0: return t; michael@0: } michael@0: michael@0: /* Free a packet tag. */ michael@0: void michael@0: m_tag_free_default(struct m_tag *t) michael@0: { michael@0: free(t); michael@0: } michael@0: michael@0: /* michael@0: * Copy data from a buffer back into the indicated mbuf chain, michael@0: * starting "off" bytes from the beginning, extending the mbuf michael@0: * chain if necessary. michael@0: */ michael@0: void michael@0: m_copyback(struct mbuf *m0, int off, int len, caddr_t cp) michael@0: { michael@0: int mlen; michael@0: struct mbuf *m = m0, *n; michael@0: int totlen = 0; michael@0: michael@0: if (m0 == NULL) michael@0: return; michael@0: while (off > (mlen = m->m_len)) { michael@0: off -= mlen; michael@0: totlen += mlen; michael@0: if (m->m_next == NULL) { michael@0: n = m_get(M_NOWAIT, m->m_type); michael@0: if (n == NULL) michael@0: goto out; michael@0: bzero(mtod(n, caddr_t), MLEN); michael@0: n->m_len = min(MLEN, len + off); michael@0: m->m_next = n; michael@0: } michael@0: m = m->m_next; michael@0: } michael@0: while (len > 0) { michael@0: mlen = min (m->m_len - off, len); michael@0: bcopy(cp, off + mtod(m, caddr_t), (u_int)mlen); michael@0: cp += mlen; michael@0: len -= mlen; michael@0: mlen += off; michael@0: off = 0; michael@0: totlen += mlen; michael@0: if (len == 0) michael@0: break; michael@0: if (m->m_next == NULL) { michael@0: n = m_get(M_NOWAIT, m->m_type); michael@0: if (n == NULL) michael@0: break; michael@0: n->m_len = min(MLEN, len); michael@0: m->m_next = n; michael@0: } michael@0: m = m->m_next; michael@0: } michael@0: out: if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) michael@0: m->m_pkthdr.len = totlen; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Lesser-used path for M_PREPEND: michael@0: * allocate new mbuf to prepend to chain, michael@0: * copy junk along. michael@0: */ michael@0: struct mbuf * michael@0: m_prepend(struct mbuf *m, int len, int how) michael@0: { michael@0: struct mbuf *mn; michael@0: michael@0: if (m->m_flags & M_PKTHDR) michael@0: MGETHDR(mn, how, m->m_type); michael@0: else michael@0: MGET(mn, how, m->m_type); michael@0: if (mn == NULL) { michael@0: m_freem(m); michael@0: return (NULL); michael@0: } michael@0: if (m->m_flags & M_PKTHDR) michael@0: M_MOVE_PKTHDR(mn, m); michael@0: mn->m_next = m; michael@0: m = mn; michael@0: if(m->m_flags & M_PKTHDR) { michael@0: if (len < MHLEN) michael@0: MH_ALIGN(m, len); michael@0: } else { michael@0: if (len < MLEN) michael@0: M_ALIGN(m, len); michael@0: } michael@0: m->m_len = len; michael@0: return (m); michael@0: } michael@0: michael@0: /* michael@0: * Copy data from an mbuf chain starting "off" bytes from the beginning, michael@0: * continuing for "len" bytes, into the indicated buffer. michael@0: */ michael@0: void michael@0: m_copydata(const struct mbuf *m, int off, int len, caddr_t cp) michael@0: { michael@0: u_int count; michael@0: michael@0: KASSERT(off >= 0, ("m_copydata, negative off %d", off)); michael@0: KASSERT(len >= 0, ("m_copydata, negative len %d", len)); michael@0: while (off > 0) { michael@0: KASSERT(m != NULL, ("m_copydata, offset > size of mbuf chain")); michael@0: if (off < m->m_len) michael@0: break; michael@0: off -= m->m_len; michael@0: m = m->m_next; michael@0: } michael@0: while (len > 0) { michael@0: KASSERT(m != NULL, ("m_copydata, length > size of mbuf chain")); michael@0: count = min(m->m_len - off, len); michael@0: bcopy(mtod(m, caddr_t) + off, cp, count); michael@0: len -= count; michael@0: cp += count; michael@0: off = 0; michael@0: m = m->m_next; michael@0: } michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Concatenate mbuf chain n to m. michael@0: * Both chains must be of the same type (e.g. MT_DATA). michael@0: * Any m_pkthdr is not updated. michael@0: */ michael@0: void michael@0: m_cat(struct mbuf *m, struct mbuf *n) michael@0: { michael@0: while (m->m_next) michael@0: m = m->m_next; michael@0: while (n) { michael@0: if (m->m_flags & M_EXT || michael@0: m->m_data + m->m_len + n->m_len >= &m->m_dat[MLEN]) { michael@0: /* just join the two chains */ michael@0: m->m_next = n; michael@0: return; michael@0: } michael@0: /* splat the data from one into the other */ michael@0: bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len, (u_int)n->m_len); michael@0: m->m_len += n->m_len; michael@0: n = m_free(n); michael@0: } michael@0: } michael@0: michael@0: michael@0: void michael@0: m_adj(struct mbuf *mp, int req_len) michael@0: { michael@0: int len = req_len; michael@0: struct mbuf *m; michael@0: int count; michael@0: michael@0: if ((m = mp) == NULL) michael@0: return; michael@0: if (len >= 0) { michael@0: /* michael@0: * Trim from head. michael@0: */ michael@0: while (m != NULL && len > 0) { michael@0: if (m->m_len <= len) { michael@0: len -= m->m_len; michael@0: m->m_len = 0; michael@0: m = m->m_next; michael@0: } else { michael@0: m->m_len -= len; michael@0: m->m_data += len; michael@0: len = 0; michael@0: } michael@0: } michael@0: m = mp; michael@0: if (mp->m_flags & M_PKTHDR) michael@0: m->m_pkthdr.len -= (req_len - len); michael@0: } else { michael@0: /* michael@0: * Trim from tail. Scan the mbuf chain, michael@0: * calculating its length and finding the last mbuf. michael@0: * If the adjustment only affects this mbuf, then just michael@0: * adjust and return. Otherwise, rescan and truncate michael@0: * after the remaining size. michael@0: */ michael@0: len = -len; michael@0: count = 0; michael@0: for (;;) { michael@0: count += m->m_len; michael@0: if (m->m_next == (struct mbuf *)0) michael@0: break; michael@0: m = m->m_next; michael@0: } michael@0: if (m->m_len >= len) { michael@0: m->m_len -= len; michael@0: if (mp->m_flags & M_PKTHDR) michael@0: mp->m_pkthdr.len -= len; michael@0: return; michael@0: } michael@0: count -= len; michael@0: if (count < 0) michael@0: count = 0; michael@0: /* michael@0: * Correct length for chain is "count". michael@0: * Find the mbuf with last data, adjust its length, michael@0: * and toss data from remaining mbufs on chain. michael@0: */ michael@0: m = mp; michael@0: if (m->m_flags & M_PKTHDR) michael@0: m->m_pkthdr.len = count; michael@0: for (; m; m = m->m_next) { michael@0: if (m->m_len >= count) { michael@0: m->m_len = count; michael@0: if (m->m_next != NULL) { michael@0: m_freem(m->m_next); michael@0: m->m_next = NULL; michael@0: } michael@0: break; michael@0: } michael@0: count -= m->m_len; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: /* m_split is used within sctp_handle_cookie_echo. */ michael@0: michael@0: /* michael@0: * Partition an mbuf chain in two pieces, returning the tail -- michael@0: * all but the first len0 bytes. In case of failure, it returns NULL and michael@0: * attempts to restore the chain to its original state. michael@0: * michael@0: * Note that the resulting mbufs might be read-only, because the new michael@0: * mbuf can end up sharing an mbuf cluster with the original mbuf if michael@0: * the "breaking point" happens to lie within a cluster mbuf. Use the michael@0: * M_WRITABLE() macro to check for this case. michael@0: */ michael@0: struct mbuf * michael@0: m_split(struct mbuf *m0, int len0, int wait) michael@0: { michael@0: struct mbuf *m, *n; michael@0: u_int len = len0, remain; michael@0: michael@0: /* MBUF_CHECKSLEEP(wait); */ michael@0: for (m = m0; m && (int)len > m->m_len; m = m->m_next) michael@0: len -= m->m_len; michael@0: if (m == NULL) michael@0: return (NULL); michael@0: remain = m->m_len - len; michael@0: if (m0->m_flags & M_PKTHDR) { michael@0: MGETHDR(n, wait, m0->m_type); michael@0: if (n == NULL) michael@0: return (NULL); michael@0: n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif; michael@0: n->m_pkthdr.len = m0->m_pkthdr.len - len0; michael@0: m0->m_pkthdr.len = len0; michael@0: if (m->m_flags & M_EXT) michael@0: goto extpacket; michael@0: if (remain > MHLEN) { michael@0: /* m can't be the lead packet */ michael@0: MH_ALIGN(n, 0); michael@0: n->m_next = m_split(m, len, wait); michael@0: if (n->m_next == NULL) { michael@0: (void) m_free(n); michael@0: return (NULL); michael@0: } else { michael@0: n->m_len = 0; michael@0: return (n); michael@0: } michael@0: } else michael@0: MH_ALIGN(n, remain); michael@0: } else if (remain == 0) { michael@0: n = m->m_next; michael@0: m->m_next = NULL; michael@0: return (n); michael@0: } else { michael@0: MGET(n, wait, m->m_type); michael@0: if (n == NULL) michael@0: return (NULL); michael@0: M_ALIGN(n, remain); michael@0: } michael@0: extpacket: michael@0: if (m->m_flags & M_EXT) { michael@0: n->m_data = m->m_data + len; michael@0: mb_dupcl(n, m); michael@0: } else { michael@0: bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain); michael@0: } michael@0: n->m_len = remain; michael@0: m->m_len = len; michael@0: n->m_next = m->m_next; michael@0: m->m_next = NULL; michael@0: return (n); michael@0: } michael@0: michael@0: michael@0: michael@0: michael@0: int michael@0: pack_send_buffer(caddr_t buffer, struct mbuf* mb){ michael@0: michael@0: int count_to_copy; michael@0: int total_count_copied = 0; michael@0: int offset = 0; michael@0: michael@0: do { michael@0: count_to_copy = mb->m_len; michael@0: bcopy(mtod(mb, caddr_t), buffer+offset, count_to_copy); michael@0: offset += count_to_copy; michael@0: total_count_copied += count_to_copy; michael@0: mb = mb->m_next; michael@0: } while(mb); michael@0: michael@0: return (total_count_copied); michael@0: }