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