19454b2d8SWarner Losh /*- 2df8bae1dSRodney W. Grimes * Copyright (c) 1982, 1986, 1988, 1991, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 6df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 7df8bae1dSRodney W. Grimes * are met: 8df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 9df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 10df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 12df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 13df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 14df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 15df8bae1dSRodney W. Grimes * without specific prior written permission. 16df8bae1dSRodney W. Grimes * 17df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27df8bae1dSRodney W. Grimes * SUCH DAMAGE. 28df8bae1dSRodney W. Grimes * 29df8bae1dSRodney W. Grimes * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 30df8bae1dSRodney W. Grimes */ 31df8bae1dSRodney W. Grimes 32677b542eSDavid E. O'Brien #include <sys/cdefs.h> 33677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 34677b542eSDavid E. O'Brien 35240ef842SDavid E. O'Brien #include "opt_param.h" 36352d050eSMike Silbersack #include "opt_mbuf_stress_test.h" 376eeac1d9SJulian Elischer #include "opt_mbuf_profiling.h" 38e32a5b94SRobert Watson 39df8bae1dSRodney W. Grimes #include <sys/param.h> 40df8bae1dSRodney W. Grimes #include <sys/systm.h> 41fb919e4dSMark Murray #include <sys/kernel.h> 42beb699c7SMike Silbersack #include <sys/limits.h> 43fb919e4dSMark Murray #include <sys/lock.h> 44f9d0d524SRobert Watson #include <sys/malloc.h> 45df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 46639acc13SGarrett Wollman #include <sys/sysctl.h> 47df8bae1dSRodney W. Grimes #include <sys/domain.h> 48df8bae1dSRodney W. Grimes #include <sys/protosw.h> 49beb699c7SMike Silbersack #include <sys/uio.h> 50fb919e4dSMark Murray 5128f8db14SBruce Evans int max_linkhdr; 5228f8db14SBruce Evans int max_protohdr; 5328f8db14SBruce Evans int max_hdr; 5428f8db14SBruce Evans int max_datalen; 5551710a45SMike Silbersack #ifdef MBUF_STRESS_TEST 5655e9f80dSMike Silbersack int m_defragpackets; 5755e9f80dSMike Silbersack int m_defragbytes; 5855e9f80dSMike Silbersack int m_defraguseless; 5955e9f80dSMike Silbersack int m_defragfailure; 60352d050eSMike Silbersack int m_defragrandomfailures; 61352d050eSMike Silbersack #endif 627d032714SBosko Milekic 637d032714SBosko Milekic /* 647d032714SBosko Milekic * sysctl(8) exported objects 657d032714SBosko Milekic */ 6680444f88SAndre Oppermann SYSCTL_INT(_kern_ipc, KIPC_MAX_LINKHDR, max_linkhdr, CTLFLAG_RD, 6780444f88SAndre Oppermann &max_linkhdr, 0, "Size of largest link layer header"); 6880444f88SAndre Oppermann SYSCTL_INT(_kern_ipc, KIPC_MAX_PROTOHDR, max_protohdr, CTLFLAG_RD, 6980444f88SAndre Oppermann &max_protohdr, 0, "Size of largest protocol layer header"); 7080444f88SAndre Oppermann SYSCTL_INT(_kern_ipc, KIPC_MAX_HDR, max_hdr, CTLFLAG_RD, 7180444f88SAndre Oppermann &max_hdr, 0, "Size of largest link plus protocol header"); 7280444f88SAndre Oppermann SYSCTL_INT(_kern_ipc, KIPC_MAX_DATALEN, max_datalen, CTLFLAG_RD, 7380444f88SAndre Oppermann &max_datalen, 0, "Minimum space left in mbuf after max_hdr"); 7451710a45SMike Silbersack #ifdef MBUF_STRESS_TEST 7555e9f80dSMike Silbersack SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragpackets, CTLFLAG_RD, 7655e9f80dSMike Silbersack &m_defragpackets, 0, ""); 7755e9f80dSMike Silbersack SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragbytes, CTLFLAG_RD, 7855e9f80dSMike Silbersack &m_defragbytes, 0, ""); 7955e9f80dSMike Silbersack SYSCTL_INT(_kern_ipc, OID_AUTO, m_defraguseless, CTLFLAG_RD, 8055e9f80dSMike Silbersack &m_defraguseless, 0, ""); 8155e9f80dSMike Silbersack SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragfailure, CTLFLAG_RD, 8255e9f80dSMike Silbersack &m_defragfailure, 0, ""); 83352d050eSMike Silbersack SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragrandomfailures, CTLFLAG_RW, 84352d050eSMike Silbersack &m_defragrandomfailures, 0, ""); 85352d050eSMike Silbersack #endif 86df8bae1dSRodney W. Grimes 87df8bae1dSRodney W. Grimes /* 88f729ede6SAndre Oppermann * Ensure the correct size of various mbuf parameters. It could be off due 89f729ede6SAndre Oppermann * to compiler-induced padding and alignment artifacts. 90f729ede6SAndre Oppermann */ 91f729ede6SAndre Oppermann CTASSERT(MSIZE - offsetof(struct mbuf, m_dat) == MLEN); 92f729ede6SAndre Oppermann CTASSERT(MSIZE - offsetof(struct mbuf, m_pktdat) == MHLEN); 93f729ede6SAndre Oppermann 94f729ede6SAndre Oppermann /* 95*3d1a9ed3SRobert Watson * mbuf data storage should be 64-bit aligned regardless of architectural 96*3d1a9ed3SRobert Watson * pointer size; check this is the case with and without a packet header. 97*3d1a9ed3SRobert Watson */ 98*3d1a9ed3SRobert Watson CTASSERT(offsetof(struct mbuf, m_dat) % 8 == 0); 99*3d1a9ed3SRobert Watson CTASSERT(offsetof(struct mbuf, m_pktdat) % 8 == 0); 100*3d1a9ed3SRobert Watson 101*3d1a9ed3SRobert Watson /* 102*3d1a9ed3SRobert Watson * While the specific values here don't matter too much (i.e., +/- a few 103*3d1a9ed3SRobert Watson * words), we do want to ensure that changes to these values are carefully 104*3d1a9ed3SRobert Watson * reasoned about and properly documented. This is especially the case as 105*3d1a9ed3SRobert Watson * network-protocol and device-driver modules encode these layouts, and must 106*3d1a9ed3SRobert Watson * be recompiled if the structures change. Check these values at compile time 107*3d1a9ed3SRobert Watson * against the ones documented in comments in mbuf.h. 108*3d1a9ed3SRobert Watson * 109*3d1a9ed3SRobert Watson * NB: Possibly they should be documented there via #define's and not just 110*3d1a9ed3SRobert Watson * comments. 111*3d1a9ed3SRobert Watson */ 112*3d1a9ed3SRobert Watson #if defined(__LP64__) 113*3d1a9ed3SRobert Watson CTASSERT(offsetof(struct mbuf, m_dat) == 32); 114*3d1a9ed3SRobert Watson CTASSERT(sizeof(struct pkthdr) == 56); 115*3d1a9ed3SRobert Watson CTASSERT(sizeof(struct struct_m_ext) == 48); 116*3d1a9ed3SRobert Watson #else 117*3d1a9ed3SRobert Watson CTASSERT(offsetof(struct mbuf, m_dat) == 24); 118*3d1a9ed3SRobert Watson CTASSERT(sizeof(struct pkthdr) == 48); 119*3d1a9ed3SRobert Watson CTASSERT(sizeof(struct struct_m_ext) == 28); 120*3d1a9ed3SRobert Watson #endif 121*3d1a9ed3SRobert Watson 122*3d1a9ed3SRobert Watson /* 12329110f87SGleb Smirnoff * m_get2() allocates minimum mbuf that would fit "size" argument. 12429110f87SGleb Smirnoff */ 12529110f87SGleb Smirnoff struct mbuf * 12641a7572bSGleb Smirnoff m_get2(int size, int how, short type, int flags) 12729110f87SGleb Smirnoff { 12829110f87SGleb Smirnoff struct mb_args args; 12929110f87SGleb Smirnoff struct mbuf *m, *n; 13029110f87SGleb Smirnoff 13129110f87SGleb Smirnoff args.flags = flags; 13229110f87SGleb Smirnoff args.type = type; 13329110f87SGleb Smirnoff 13429110f87SGleb Smirnoff if (size <= MHLEN || (size <= MLEN && (flags & M_PKTHDR) == 0)) 13529110f87SGleb Smirnoff return (uma_zalloc_arg(zone_mbuf, &args, how)); 13629110f87SGleb Smirnoff if (size <= MCLBYTES) 13729110f87SGleb Smirnoff return (uma_zalloc_arg(zone_pack, &args, how)); 1383112ae76SGleb Smirnoff 1393112ae76SGleb Smirnoff if (size > MJUMPAGESIZE) 14029110f87SGleb Smirnoff return (NULL); 14129110f87SGleb Smirnoff 14229110f87SGleb Smirnoff m = uma_zalloc_arg(zone_mbuf, &args, how); 14329110f87SGleb Smirnoff if (m == NULL) 14429110f87SGleb Smirnoff return (NULL); 14529110f87SGleb Smirnoff 1463112ae76SGleb Smirnoff n = uma_zalloc_arg(zone_jumbop, m, how); 14729110f87SGleb Smirnoff if (n == NULL) { 14829110f87SGleb Smirnoff uma_zfree(zone_mbuf, m); 14929110f87SGleb Smirnoff return (NULL); 15029110f87SGleb Smirnoff } 15129110f87SGleb Smirnoff 15229110f87SGleb Smirnoff return (m); 15329110f87SGleb Smirnoff } 15429110f87SGleb Smirnoff 15529110f87SGleb Smirnoff /* 15629110f87SGleb Smirnoff * m_getjcl() returns an mbuf with a cluster of the specified size attached. 15729110f87SGleb Smirnoff * For size it takes MCLBYTES, MJUMPAGESIZE, MJUM9BYTES, MJUM16BYTES. 15829110f87SGleb Smirnoff */ 15929110f87SGleb Smirnoff struct mbuf * 16029110f87SGleb Smirnoff m_getjcl(int how, short type, int flags, int size) 16129110f87SGleb Smirnoff { 16229110f87SGleb Smirnoff struct mb_args args; 16329110f87SGleb Smirnoff struct mbuf *m, *n; 16429110f87SGleb Smirnoff uma_zone_t zone; 16529110f87SGleb Smirnoff 16629110f87SGleb Smirnoff if (size == MCLBYTES) 16729110f87SGleb Smirnoff return m_getcl(how, type, flags); 16829110f87SGleb Smirnoff 16929110f87SGleb Smirnoff args.flags = flags; 17029110f87SGleb Smirnoff args.type = type; 17129110f87SGleb Smirnoff 17229110f87SGleb Smirnoff m = uma_zalloc_arg(zone_mbuf, &args, how); 17329110f87SGleb Smirnoff if (m == NULL) 17429110f87SGleb Smirnoff return (NULL); 17529110f87SGleb Smirnoff 17629110f87SGleb Smirnoff zone = m_getzone(size); 17729110f87SGleb Smirnoff n = uma_zalloc_arg(zone, m, how); 17829110f87SGleb Smirnoff if (n == NULL) { 17929110f87SGleb Smirnoff uma_zfree(zone_mbuf, m); 18029110f87SGleb Smirnoff return (NULL); 18129110f87SGleb Smirnoff } 18229110f87SGleb Smirnoff return (m); 18329110f87SGleb Smirnoff } 18429110f87SGleb Smirnoff 18529110f87SGleb Smirnoff /* 186099a0e58SBosko Milekic * Allocate a given length worth of mbufs and/or clusters (whatever fits 187099a0e58SBosko Milekic * best) and return a pointer to the top of the allocated chain. If an 188099a0e58SBosko Milekic * existing mbuf chain is provided, then we will append the new chain 189099a0e58SBosko Milekic * to the existing one but still return the top of the newly allocated 190099a0e58SBosko Milekic * chain. 191099a0e58SBosko Milekic */ 192099a0e58SBosko Milekic struct mbuf * 1935e20f43dSAndre Oppermann m_getm2(struct mbuf *m, int len, int how, short type, int flags) 194099a0e58SBosko Milekic { 1955e20f43dSAndre Oppermann struct mbuf *mb, *nm = NULL, *mtail = NULL; 196099a0e58SBosko Milekic 1975e20f43dSAndre Oppermann KASSERT(len >= 0, ("%s: len is < 0", __func__)); 198099a0e58SBosko Milekic 1995e20f43dSAndre Oppermann /* Validate flags. */ 2005e20f43dSAndre Oppermann flags &= (M_PKTHDR | M_EOR); 2015e20f43dSAndre Oppermann 2025e20f43dSAndre Oppermann /* Packet header mbuf must be first in chain. */ 2035e20f43dSAndre Oppermann if ((flags & M_PKTHDR) && m != NULL) 2045e20f43dSAndre Oppermann flags &= ~M_PKTHDR; 2055e20f43dSAndre Oppermann 2065e20f43dSAndre Oppermann /* Loop and append maximum sized mbufs to the chain tail. */ 2075e20f43dSAndre Oppermann while (len > 0) { 2085e20f43dSAndre Oppermann if (len > MCLBYTES) 2095e20f43dSAndre Oppermann mb = m_getjcl(how, type, (flags & M_PKTHDR), 2105e20f43dSAndre Oppermann MJUMPAGESIZE); 2115e20f43dSAndre Oppermann else if (len >= MINCLSIZE) 2125e20f43dSAndre Oppermann mb = m_getcl(how, type, (flags & M_PKTHDR)); 2135e20f43dSAndre Oppermann else if (flags & M_PKTHDR) 2145e20f43dSAndre Oppermann mb = m_gethdr(how, type); 215099a0e58SBosko Milekic else 2165e20f43dSAndre Oppermann mb = m_get(how, type); 217099a0e58SBosko Milekic 2185e20f43dSAndre Oppermann /* Fail the whole operation if one mbuf can't be allocated. */ 2195e20f43dSAndre Oppermann if (mb == NULL) { 2205e20f43dSAndre Oppermann if (nm != NULL) 2215e20f43dSAndre Oppermann m_freem(nm); 2225e20f43dSAndre Oppermann return (NULL); 223099a0e58SBosko Milekic } 224099a0e58SBosko Milekic 2255e20f43dSAndre Oppermann /* Book keeping. */ 226b66f2a48SRobert Watson len -= M_SIZE(mb); 227099a0e58SBosko Milekic if (mtail != NULL) 2285e20f43dSAndre Oppermann mtail->m_next = mb; 2295e20f43dSAndre Oppermann else 2305e20f43dSAndre Oppermann nm = mb; 2315e20f43dSAndre Oppermann mtail = mb; 2325e20f43dSAndre Oppermann flags &= ~M_PKTHDR; /* Only valid on the first mbuf. */ 2335e20f43dSAndre Oppermann } 2345e20f43dSAndre Oppermann if (flags & M_EOR) 2355e20f43dSAndre Oppermann mtail->m_flags |= M_EOR; /* Only valid on the last mbuf. */ 2365e20f43dSAndre Oppermann 2375e20f43dSAndre Oppermann /* If mbuf was supplied, append new chain to the end of it. */ 2385e20f43dSAndre Oppermann if (m != NULL) { 2395e20f43dSAndre Oppermann for (mtail = m; mtail->m_next != NULL; mtail = mtail->m_next) 2405e20f43dSAndre Oppermann ; 2415e20f43dSAndre Oppermann mtail->m_next = nm; 2425e20f43dSAndre Oppermann mtail->m_flags &= ~M_EOR; 2435e20f43dSAndre Oppermann } else 2445e20f43dSAndre Oppermann m = nm; 2455e20f43dSAndre Oppermann 2465e20f43dSAndre Oppermann return (m); 247099a0e58SBosko Milekic } 248099a0e58SBosko Milekic 249099a0e58SBosko Milekic /* 250099a0e58SBosko Milekic * Free an entire chain of mbufs and associated external buffers, if 251099a0e58SBosko Milekic * applicable. 252099a0e58SBosko Milekic */ 253099a0e58SBosko Milekic void 254099a0e58SBosko Milekic m_freem(struct mbuf *mb) 255099a0e58SBosko Milekic { 256099a0e58SBosko Milekic 257099a0e58SBosko Milekic while (mb != NULL) 258099a0e58SBosko Milekic mb = m_free(mb); 259099a0e58SBosko Milekic } 260099a0e58SBosko Milekic 2611a996ed1SEdward Tomasz Napierala /*- 262099a0e58SBosko Milekic * Configure a provided mbuf to refer to the provided external storage 263099a0e58SBosko Milekic * buffer and setup a reference count for said buffer. If the setting 264099a0e58SBosko Milekic * up of the reference count fails, the M_EXT bit will not be set. If 265099a0e58SBosko Milekic * successfull, the M_EXT bit is set in the mbuf's flags. 266099a0e58SBosko Milekic * 267099a0e58SBosko Milekic * Arguments: 268099a0e58SBosko Milekic * mb The existing mbuf to which to attach the provided buffer. 269099a0e58SBosko Milekic * buf The address of the provided external storage buffer. 270099a0e58SBosko Milekic * size The size of the provided buffer. 271099a0e58SBosko Milekic * freef A pointer to a routine that is responsible for freeing the 272099a0e58SBosko Milekic * provided external storage buffer. 273099a0e58SBosko Milekic * args A pointer to an argument structure (of any type) to be passed 274099a0e58SBosko Milekic * to the provided freef routine (may be NULL). 275099a0e58SBosko Milekic * flags Any other flags to be passed to the provided mbuf. 276099a0e58SBosko Milekic * type The type that the external storage buffer should be 277099a0e58SBosko Milekic * labeled with. 278099a0e58SBosko Milekic * 279099a0e58SBosko Milekic * Returns: 280099a0e58SBosko Milekic * Nothing. 281099a0e58SBosko Milekic */ 2828c629bdfSGleb Smirnoff int 283099a0e58SBosko Milekic m_extadd(struct mbuf *mb, caddr_t buf, u_int size, 28415c28f87SGleb Smirnoff void (*freef)(struct mbuf *, void *, void *), void *arg1, void *arg2, 2859a736876SAndre Oppermann int flags, int type, int wait) 286099a0e58SBosko Milekic { 28756a4e45aSAndre Oppermann KASSERT(type != EXT_CLUSTER, ("%s: EXT_CLUSTER not allowed", __func__)); 288099a0e58SBosko Milekic 28956a4e45aSAndre Oppermann if (type != EXT_EXTREF) 290fcc34a23SGleb Smirnoff mb->m_ext.ext_cnt = uma_zalloc(zone_ext_refcnt, wait); 2918c629bdfSGleb Smirnoff 292fcc34a23SGleb Smirnoff if (mb->m_ext.ext_cnt == NULL) 2938c629bdfSGleb Smirnoff return (ENOMEM); 2948c629bdfSGleb Smirnoff 295fcc34a23SGleb Smirnoff *(mb->m_ext.ext_cnt) = 1; 296099a0e58SBosko Milekic mb->m_flags |= (M_EXT | flags); 297099a0e58SBosko Milekic mb->m_ext.ext_buf = buf; 298099a0e58SBosko Milekic mb->m_data = mb->m_ext.ext_buf; 299099a0e58SBosko Milekic mb->m_ext.ext_size = size; 300099a0e58SBosko Milekic mb->m_ext.ext_free = freef; 301cf827063SPoul-Henning Kamp mb->m_ext.ext_arg1 = arg1; 302cf827063SPoul-Henning Kamp mb->m_ext.ext_arg2 = arg2; 303099a0e58SBosko Milekic mb->m_ext.ext_type = type; 304894734cbSAndre Oppermann mb->m_ext.ext_flags = 0; 3058c629bdfSGleb Smirnoff 3068c629bdfSGleb Smirnoff return (0); 307099a0e58SBosko Milekic } 308099a0e58SBosko Milekic 309099a0e58SBosko Milekic /* 310099a0e58SBosko Milekic * Non-directly-exported function to clean up after mbufs with M_EXT 31156a4e45aSAndre Oppermann * storage attached to them if the reference count hits 1. 312099a0e58SBosko Milekic */ 313099a0e58SBosko Milekic void 314099a0e58SBosko Milekic mb_free_ext(struct mbuf *m) 315099a0e58SBosko Milekic { 3161fbe6a82SGleb Smirnoff int freembuf; 317457869b9SKip Macy 3181fbe6a82SGleb Smirnoff KASSERT(m->m_flags & M_EXT, ("%s: M_EXT not set on %p", __func__, m)); 3193d2a3ff2SBosko Milekic 3201227f20dSAndre Oppermann /* 3211fbe6a82SGleb Smirnoff * Check if the header is embedded in the cluster. 3221227f20dSAndre Oppermann */ 3231fbe6a82SGleb Smirnoff freembuf = (m->m_flags & M_NOFREE) ? 0 : 1; 3241227f20dSAndre Oppermann 3251fbe6a82SGleb Smirnoff switch (m->m_ext.ext_type) { 3261fbe6a82SGleb Smirnoff case EXT_SFBUF: 3271fbe6a82SGleb Smirnoff sf_ext_free(m->m_ext.ext_arg1, m->m_ext.ext_arg2); 3281fbe6a82SGleb Smirnoff break; 3291fbe6a82SGleb Smirnoff default: 3301fbe6a82SGleb Smirnoff KASSERT(m->m_ext.ext_cnt != NULL, 3311fbe6a82SGleb Smirnoff ("%s: no refcounting pointer on %p", __func__, m)); 3321fbe6a82SGleb Smirnoff /* 3331fbe6a82SGleb Smirnoff * Free attached storage if this mbuf is the only 3341fbe6a82SGleb Smirnoff * reference to it. 3351fbe6a82SGleb Smirnoff */ 3361fbe6a82SGleb Smirnoff if (*(m->m_ext.ext_cnt) != 1) { 3371fbe6a82SGleb Smirnoff if (atomic_fetchadd_int(m->m_ext.ext_cnt, -1) != 1) 3381fbe6a82SGleb Smirnoff break; 3391fbe6a82SGleb Smirnoff } 3401fbe6a82SGleb Smirnoff 34156a4e45aSAndre Oppermann switch (m->m_ext.ext_type) { 342cd5bb63bSAndre Oppermann case EXT_PACKET: /* The packet zone is special. */ 343fcc34a23SGleb Smirnoff if (*(m->m_ext.ext_cnt) == 0) 344fcc34a23SGleb Smirnoff *(m->m_ext.ext_cnt) = 1; 345099a0e58SBosko Milekic uma_zfree(zone_pack, m); 34656a4e45aSAndre Oppermann return; /* Job done. */ 347cd5bb63bSAndre Oppermann case EXT_CLUSTER: 348cd5bb63bSAndre Oppermann uma_zfree(zone_clust, m->m_ext.ext_buf); 34956a4e45aSAndre Oppermann break; 350ec63cb90SAndre Oppermann case EXT_JUMBOP: 351ec63cb90SAndre Oppermann uma_zfree(zone_jumbop, m->m_ext.ext_buf); 352d5269a63SAndre Oppermann break; 35356a4e45aSAndre Oppermann case EXT_JUMBO9: 35456a4e45aSAndre Oppermann uma_zfree(zone_jumbo9, m->m_ext.ext_buf); 35556a4e45aSAndre Oppermann break; 35656a4e45aSAndre Oppermann case EXT_JUMBO16: 35756a4e45aSAndre Oppermann uma_zfree(zone_jumbo16, m->m_ext.ext_buf); 35856a4e45aSAndre Oppermann break; 35956a4e45aSAndre Oppermann case EXT_NET_DRV: 36056a4e45aSAndre Oppermann case EXT_MOD_TYPE: 36156a4e45aSAndre Oppermann case EXT_DISPOSABLE: 362fcc34a23SGleb Smirnoff *(m->m_ext.ext_cnt) = 0; 36356a4e45aSAndre Oppermann uma_zfree(zone_ext_refcnt, __DEVOLATILE(u_int *, 364fcc34a23SGleb Smirnoff m->m_ext.ext_cnt)); 36556a4e45aSAndre Oppermann /* FALLTHROUGH */ 36656a4e45aSAndre Oppermann case EXT_EXTREF: 36756a4e45aSAndre Oppermann KASSERT(m->m_ext.ext_free != NULL, 36856a4e45aSAndre Oppermann ("%s: ext_free not set", __func__)); 36915c28f87SGleb Smirnoff (*(m->m_ext.ext_free))(m, m->m_ext.ext_arg1, 370cf827063SPoul-Henning Kamp m->m_ext.ext_arg2); 37156a4e45aSAndre Oppermann break; 37256a4e45aSAndre Oppermann default: 37356a4e45aSAndre Oppermann KASSERT(m->m_ext.ext_type == 0, 37456a4e45aSAndre Oppermann ("%s: unknown ext_type", __func__)); 3753d2a3ff2SBosko Milekic } 37656a4e45aSAndre Oppermann } 377457869b9SKip Macy 3781fbe6a82SGleb Smirnoff if (freembuf) 37996e12413SBosko Milekic uma_zfree(zone_mbuf, m); 380b5b2ea9aSBosko Milekic } 381099a0e58SBosko Milekic 382099a0e58SBosko Milekic /* 3836bccea7cSRebecca Cran * Attach the cluster from *m to *n, set up m_ext in *n 38456a4e45aSAndre Oppermann * and bump the refcount of the cluster. 38556a4e45aSAndre Oppermann */ 38656a4e45aSAndre Oppermann static void 38756a4e45aSAndre Oppermann mb_dupcl(struct mbuf *n, struct mbuf *m) 38856a4e45aSAndre Oppermann { 38956a4e45aSAndre Oppermann 3901fbe6a82SGleb Smirnoff KASSERT(m->m_flags & M_EXT, ("%s: M_EXT not set on %p", __func__, m)); 3911fbe6a82SGleb Smirnoff KASSERT(!(n->m_flags & M_EXT), ("%s: M_EXT set on %p", __func__, n)); 3921fbe6a82SGleb Smirnoff 3931fbe6a82SGleb Smirnoff switch (m->m_ext.ext_type) { 3941fbe6a82SGleb Smirnoff case EXT_SFBUF: 3951fbe6a82SGleb Smirnoff sf_ext_ref(m->m_ext.ext_arg1, m->m_ext.ext_arg2); 3961fbe6a82SGleb Smirnoff break; 3971fbe6a82SGleb Smirnoff default: 3981fbe6a82SGleb Smirnoff KASSERT(m->m_ext.ext_cnt != NULL, 3991fbe6a82SGleb Smirnoff ("%s: no refcounting pointer on %p", __func__, m)); 400fcc34a23SGleb Smirnoff if (*(m->m_ext.ext_cnt) == 1) 401fcc34a23SGleb Smirnoff *(m->m_ext.ext_cnt) += 1; 40256a4e45aSAndre Oppermann else 403fcc34a23SGleb Smirnoff atomic_add_int(m->m_ext.ext_cnt, 1); 4041fbe6a82SGleb Smirnoff } 4051fbe6a82SGleb Smirnoff 406c71b4037SGleb Smirnoff n->m_ext = m->m_ext; 40756a4e45aSAndre Oppermann n->m_flags |= M_EXT; 40832a8b1d8SColin Percival n->m_flags |= m->m_flags & M_RDONLY; 40956a4e45aSAndre Oppermann } 41056a4e45aSAndre Oppermann 41156a4e45aSAndre Oppermann /* 412ed111688SAndre Oppermann * Clean up mbuf (chain) from any tags and packet headers. 413e0068c3aSAndre Oppermann * If "all" is set then the first mbuf in the chain will be 414e0068c3aSAndre Oppermann * cleaned too. 415ed111688SAndre Oppermann */ 416ed111688SAndre Oppermann void 417651e4e6aSGleb Smirnoff m_demote(struct mbuf *m0, int all, int flags) 418ed111688SAndre Oppermann { 419ed111688SAndre Oppermann struct mbuf *m; 420ed111688SAndre Oppermann 421ed111688SAndre Oppermann for (m = all ? m0 : m0->m_next; m != NULL; m = m->m_next) { 4227ee2d058SGleb Smirnoff KASSERT(m->m_nextpkt == NULL, ("%s: m_nextpkt in m %p, m0 %p", 4237ee2d058SGleb Smirnoff __func__, m, m0)); 424ed111688SAndre Oppermann if (m->m_flags & M_PKTHDR) { 425ed111688SAndre Oppermann m_tag_delete_chain(m, NULL); 426ed111688SAndre Oppermann m->m_flags &= ~M_PKTHDR; 427ed111688SAndre Oppermann bzero(&m->m_pkthdr, sizeof(struct pkthdr)); 428ed111688SAndre Oppermann } 429651e4e6aSGleb Smirnoff m->m_flags = m->m_flags & (M_EXT | M_RDONLY | M_NOFREE | flags); 430ed111688SAndre Oppermann } 431ed111688SAndre Oppermann } 432ed111688SAndre Oppermann 433ed111688SAndre Oppermann /* 434fdcc028dSAndre Oppermann * Sanity checks on mbuf (chain) for use in KASSERT() and general 435fdcc028dSAndre Oppermann * debugging. 436fdcc028dSAndre Oppermann * Returns 0 or panics when bad and 1 on all tests passed. 437fdcc028dSAndre Oppermann * Sanitize, 0 to run M_SANITY_ACTION, 1 to garble things so they 438fdcc028dSAndre Oppermann * blow up later. 439a048affbSAndre Oppermann */ 440a048affbSAndre Oppermann int 441a048affbSAndre Oppermann m_sanity(struct mbuf *m0, int sanitize) 442a048affbSAndre Oppermann { 443a048affbSAndre Oppermann struct mbuf *m; 444a048affbSAndre Oppermann caddr_t a, b; 445a048affbSAndre Oppermann int pktlen = 0; 446a048affbSAndre Oppermann 44721ee3e7aSKip Macy #ifdef INVARIANTS 44821ee3e7aSKip Macy #define M_SANITY_ACTION(s) panic("mbuf %p: " s, m) 44921ee3e7aSKip Macy #else 45021ee3e7aSKip Macy #define M_SANITY_ACTION(s) printf("mbuf %p: " s, m) 45121ee3e7aSKip Macy #endif 452a048affbSAndre Oppermann 453fdcc028dSAndre Oppermann for (m = m0; m != NULL; m = m->m_next) { 454a048affbSAndre Oppermann /* 455a048affbSAndre Oppermann * Basic pointer checks. If any of these fails then some 456a048affbSAndre Oppermann * unrelated kernel memory before or after us is trashed. 457a048affbSAndre Oppermann * No way to recover from that. 458a048affbSAndre Oppermann */ 459b66f2a48SRobert Watson a = M_START(m); 460b66f2a48SRobert Watson b = a + M_SIZE(m); 461a048affbSAndre Oppermann if ((caddr_t)m->m_data < a) 462a048affbSAndre Oppermann M_SANITY_ACTION("m_data outside mbuf data range left"); 463a048affbSAndre Oppermann if ((caddr_t)m->m_data > b) 464a048affbSAndre Oppermann M_SANITY_ACTION("m_data outside mbuf data range right"); 465a048affbSAndre Oppermann if ((caddr_t)m->m_data + m->m_len > b) 466a048affbSAndre Oppermann M_SANITY_ACTION("m_data + m_len exeeds mbuf space"); 467a048affbSAndre Oppermann 468a048affbSAndre Oppermann /* m->m_nextpkt may only be set on first mbuf in chain. */ 469fdcc028dSAndre Oppermann if (m != m0 && m->m_nextpkt != NULL) { 470a048affbSAndre Oppermann if (sanitize) { 471a048affbSAndre Oppermann m_freem(m->m_nextpkt); 472a048affbSAndre Oppermann m->m_nextpkt = (struct mbuf *)0xDEADC0DE; 473a048affbSAndre Oppermann } else 474a048affbSAndre Oppermann M_SANITY_ACTION("m->m_nextpkt on in-chain mbuf"); 475a048affbSAndre Oppermann } 476a048affbSAndre Oppermann 477a048affbSAndre Oppermann /* packet length (not mbuf length!) calculation */ 478a048affbSAndre Oppermann if (m0->m_flags & M_PKTHDR) 479a048affbSAndre Oppermann pktlen += m->m_len; 480a048affbSAndre Oppermann 481a048affbSAndre Oppermann /* m_tags may only be attached to first mbuf in chain. */ 482a048affbSAndre Oppermann if (m != m0 && m->m_flags & M_PKTHDR && 483a048affbSAndre Oppermann !SLIST_EMPTY(&m->m_pkthdr.tags)) { 484a048affbSAndre Oppermann if (sanitize) { 485a048affbSAndre Oppermann m_tag_delete_chain(m, NULL); 486a048affbSAndre Oppermann /* put in 0xDEADC0DE perhaps? */ 487fdcc028dSAndre Oppermann } else 488a048affbSAndre Oppermann M_SANITY_ACTION("m_tags on in-chain mbuf"); 489a048affbSAndre Oppermann } 490a048affbSAndre Oppermann 491a048affbSAndre Oppermann /* M_PKTHDR may only be set on first mbuf in chain */ 492a048affbSAndre Oppermann if (m != m0 && m->m_flags & M_PKTHDR) { 493a048affbSAndre Oppermann if (sanitize) { 494a048affbSAndre Oppermann bzero(&m->m_pkthdr, sizeof(m->m_pkthdr)); 495a048affbSAndre Oppermann m->m_flags &= ~M_PKTHDR; 496a048affbSAndre Oppermann /* put in 0xDEADCODE and leave hdr flag in */ 497a048affbSAndre Oppermann } else 498a048affbSAndre Oppermann M_SANITY_ACTION("M_PKTHDR on in-chain mbuf"); 499a048affbSAndre Oppermann } 500a048affbSAndre Oppermann } 501fdcc028dSAndre Oppermann m = m0; 502fdcc028dSAndre Oppermann if (pktlen && pktlen != m->m_pkthdr.len) { 503a048affbSAndre Oppermann if (sanitize) 504fdcc028dSAndre Oppermann m->m_pkthdr.len = 0; 505a048affbSAndre Oppermann else 506a048affbSAndre Oppermann M_SANITY_ACTION("m_pkthdr.len != mbuf chain length"); 507a048affbSAndre Oppermann } 508a048affbSAndre Oppermann return 1; 509fdcc028dSAndre Oppermann 510fdcc028dSAndre Oppermann #undef M_SANITY_ACTION 511a048affbSAndre Oppermann } 512a048affbSAndre Oppermann 513a048affbSAndre Oppermann 514a048affbSAndre Oppermann /* 5159967cafcSSam Leffler * "Move" mbuf pkthdr from "from" to "to". 516e37b1fcdSRobert Watson * "from" must have M_PKTHDR set, and "to" must be empty. 517e37b1fcdSRobert Watson */ 518e37b1fcdSRobert Watson void 5199967cafcSSam Leffler m_move_pkthdr(struct mbuf *to, struct mbuf *from) 520e37b1fcdSRobert Watson { 521e37b1fcdSRobert Watson 522e37b1fcdSRobert Watson #if 0 5239967cafcSSam Leffler /* see below for why these are not enabled */ 524fe584538SDag-Erling Smørgrav M_ASSERTPKTHDR(to); 525225bff6fSRobert Watson /* Note: with MAC, this may not be a good assertion. */ 5269967cafcSSam Leffler KASSERT(SLIST_EMPTY(&to->m_pkthdr.tags), 5279967cafcSSam Leffler ("m_move_pkthdr: to has tags")); 528e37b1fcdSRobert Watson #endif 529e32a5b94SRobert Watson #ifdef MAC 530225bff6fSRobert Watson /* 531225bff6fSRobert Watson * XXXMAC: It could be this should also occur for non-MAC? 532225bff6fSRobert Watson */ 533e32a5b94SRobert Watson if (to->m_flags & M_PKTHDR) 534225bff6fSRobert Watson m_tag_delete_chain(to, NULL); 535e32a5b94SRobert Watson #endif 536a4e71429SSam Leffler to->m_flags = (from->m_flags & M_COPYFLAGS) | (to->m_flags & M_EXT); 537a4e71429SSam Leffler if ((to->m_flags & M_EXT) == 0) 5389967cafcSSam Leffler to->m_data = to->m_pktdat; 5399967cafcSSam Leffler to->m_pkthdr = from->m_pkthdr; /* especially tags */ 5409967cafcSSam Leffler SLIST_INIT(&from->m_pkthdr.tags); /* purge tags from src */ 5419967cafcSSam Leffler from->m_flags &= ~M_PKTHDR; 5429967cafcSSam Leffler } 5439967cafcSSam Leffler 5449967cafcSSam Leffler /* 5459967cafcSSam Leffler * Duplicate "from"'s mbuf pkthdr in "to". 5469967cafcSSam Leffler * "from" must have M_PKTHDR set, and "to" must be empty. 5479967cafcSSam Leffler * In particular, this does a deep copy of the packet tags. 5489967cafcSSam Leffler */ 5499967cafcSSam Leffler int 5509967cafcSSam Leffler m_dup_pkthdr(struct mbuf *to, struct mbuf *from, int how) 5519967cafcSSam Leffler { 5529967cafcSSam Leffler 5539967cafcSSam Leffler #if 0 5549967cafcSSam Leffler /* 5559967cafcSSam Leffler * The mbuf allocator only initializes the pkthdr 556c95be8b5SGleb Smirnoff * when the mbuf is allocated with m_gethdr(). Many users 557c95be8b5SGleb Smirnoff * (e.g. m_copy*, m_prepend) use m_get() and then 5589967cafcSSam Leffler * smash the pkthdr as needed causing these 5599967cafcSSam Leffler * assertions to trip. For now just disable them. 5609967cafcSSam Leffler */ 561fe584538SDag-Erling Smørgrav M_ASSERTPKTHDR(to); 562225bff6fSRobert Watson /* Note: with MAC, this may not be a good assertion. */ 5639967cafcSSam Leffler KASSERT(SLIST_EMPTY(&to->m_pkthdr.tags), ("m_dup_pkthdr: to has tags")); 5649967cafcSSam Leffler #endif 565063d8114SAlfred Perlstein MBUF_CHECKSLEEP(how); 5669967cafcSSam Leffler #ifdef MAC 5679967cafcSSam Leffler if (to->m_flags & M_PKTHDR) 568225bff6fSRobert Watson m_tag_delete_chain(to, NULL); 5699967cafcSSam Leffler #endif 570df8c7fc9SMike Silbersack to->m_flags = (from->m_flags & M_COPYFLAGS) | (to->m_flags & M_EXT); 571df8c7fc9SMike Silbersack if ((to->m_flags & M_EXT) == 0) 5729967cafcSSam Leffler to->m_data = to->m_pktdat; 573e37b1fcdSRobert Watson to->m_pkthdr = from->m_pkthdr; 5749967cafcSSam Leffler SLIST_INIT(&to->m_pkthdr.tags); 57594985f74SGleb Smirnoff return (m_tag_copy_chain(to, from, how)); 576e37b1fcdSRobert Watson } 577e37b1fcdSRobert Watson 578e37b1fcdSRobert Watson /* 579df8bae1dSRodney W. Grimes * Lesser-used path for M_PREPEND: 580df8bae1dSRodney W. Grimes * allocate new mbuf to prepend to chain, 581df8bae1dSRodney W. Grimes * copy junk along. 582df8bae1dSRodney W. Grimes */ 583df8bae1dSRodney W. Grimes struct mbuf * 584122a814aSBosko Milekic m_prepend(struct mbuf *m, int len, int how) 585df8bae1dSRodney W. Grimes { 586df8bae1dSRodney W. Grimes struct mbuf *mn; 587df8bae1dSRodney W. Grimes 588f8bf8e39SMike Silbersack if (m->m_flags & M_PKTHDR) 589c95be8b5SGleb Smirnoff mn = m_gethdr(how, m->m_type); 590f8bf8e39SMike Silbersack else 591c95be8b5SGleb Smirnoff mn = m_get(how, m->m_type); 592122a814aSBosko Milekic if (mn == NULL) { 593df8bae1dSRodney W. Grimes m_freem(m); 594122a814aSBosko Milekic return (NULL); 595df8bae1dSRodney W. Grimes } 596225bff6fSRobert Watson if (m->m_flags & M_PKTHDR) 597c95be8b5SGleb Smirnoff m_move_pkthdr(mn, m); 598df8bae1dSRodney W. Grimes mn->m_next = m; 599df8bae1dSRodney W. Grimes m = mn; 600ed6a66caSRobert Watson if (len < M_SIZE(m)) 6015288989fSRandall Stewart M_ALIGN(m, len); 602df8bae1dSRodney W. Grimes m->m_len = len; 603df8bae1dSRodney W. Grimes return (m); 604df8bae1dSRodney W. Grimes } 605df8bae1dSRodney W. Grimes 606df8bae1dSRodney W. Grimes /* 607df8bae1dSRodney W. Grimes * Make a copy of an mbuf chain starting "off0" bytes from the beginning, 608df8bae1dSRodney W. Grimes * continuing for "len" bytes. If len is M_COPYALL, copy to end of mbuf. 609eb1b1807SGleb Smirnoff * The wait parameter is a choice of M_WAITOK/M_NOWAIT from caller. 6101c38f2eaSArchie Cobbs * Note that the copy is read-only, because clusters are not copied, 6111c38f2eaSArchie Cobbs * only their reference counts are incremented. 612df8bae1dSRodney W. Grimes */ 613df8bae1dSRodney W. Grimes struct mbuf * 614122a814aSBosko Milekic m_copym(struct mbuf *m, int off0, int len, int wait) 615df8bae1dSRodney W. Grimes { 616122a814aSBosko Milekic struct mbuf *n, **np; 617122a814aSBosko Milekic int off = off0; 618df8bae1dSRodney W. Grimes struct mbuf *top; 619df8bae1dSRodney W. Grimes int copyhdr = 0; 620df8bae1dSRodney W. Grimes 621e0a653ddSAlfred Perlstein KASSERT(off >= 0, ("m_copym, negative off %d", off)); 622e0a653ddSAlfred Perlstein KASSERT(len >= 0, ("m_copym, negative len %d", len)); 623063d8114SAlfred Perlstein MBUF_CHECKSLEEP(wait); 624df8bae1dSRodney W. Grimes if (off == 0 && m->m_flags & M_PKTHDR) 625df8bae1dSRodney W. Grimes copyhdr = 1; 626df8bae1dSRodney W. Grimes while (off > 0) { 627e0a653ddSAlfred Perlstein KASSERT(m != NULL, ("m_copym, offset > size of mbuf chain")); 628df8bae1dSRodney W. Grimes if (off < m->m_len) 629df8bae1dSRodney W. Grimes break; 630df8bae1dSRodney W. Grimes off -= m->m_len; 631df8bae1dSRodney W. Grimes m = m->m_next; 632df8bae1dSRodney W. Grimes } 633df8bae1dSRodney W. Grimes np = ⊤ 634df8bae1dSRodney W. Grimes top = 0; 635df8bae1dSRodney W. Grimes while (len > 0) { 636122a814aSBosko Milekic if (m == NULL) { 637e0a653ddSAlfred Perlstein KASSERT(len == M_COPYALL, 638e0a653ddSAlfred Perlstein ("m_copym, length > size of mbuf chain")); 639df8bae1dSRodney W. Grimes break; 640df8bae1dSRodney W. Grimes } 641f8bf8e39SMike Silbersack if (copyhdr) 642c95be8b5SGleb Smirnoff n = m_gethdr(wait, m->m_type); 643f8bf8e39SMike Silbersack else 644c95be8b5SGleb Smirnoff n = m_get(wait, m->m_type); 645df8bae1dSRodney W. Grimes *np = n; 646122a814aSBosko Milekic if (n == NULL) 647df8bae1dSRodney W. Grimes goto nospace; 648df8bae1dSRodney W. Grimes if (copyhdr) { 6499967cafcSSam Leffler if (!m_dup_pkthdr(n, m, wait)) 6509967cafcSSam Leffler goto nospace; 651df8bae1dSRodney W. Grimes if (len == M_COPYALL) 652df8bae1dSRodney W. Grimes n->m_pkthdr.len -= off0; 653df8bae1dSRodney W. Grimes else 654df8bae1dSRodney W. Grimes n->m_pkthdr.len = len; 655df8bae1dSRodney W. Grimes copyhdr = 0; 656df8bae1dSRodney W. Grimes } 657df8bae1dSRodney W. Grimes n->m_len = min(len, m->m_len - off); 658df8bae1dSRodney W. Grimes if (m->m_flags & M_EXT) { 659df8bae1dSRodney W. Grimes n->m_data = m->m_data + off; 66056a4e45aSAndre Oppermann mb_dupcl(n, m); 661df8bae1dSRodney W. Grimes } else 662df8bae1dSRodney W. Grimes bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t), 663bd395ae8SBosko Milekic (u_int)n->m_len); 664df8bae1dSRodney W. Grimes if (len != M_COPYALL) 665df8bae1dSRodney W. Grimes len -= n->m_len; 666df8bae1dSRodney W. Grimes off = 0; 667df8bae1dSRodney W. Grimes m = m->m_next; 668df8bae1dSRodney W. Grimes np = &n->m_next; 669df8bae1dSRodney W. Grimes } 67008442f8aSBosko Milekic 671df8bae1dSRodney W. Grimes return (top); 672df8bae1dSRodney W. Grimes nospace: 673df8bae1dSRodney W. Grimes m_freem(top); 674122a814aSBosko Milekic return (NULL); 675df8bae1dSRodney W. Grimes } 676df8bae1dSRodney W. Grimes 677df8bae1dSRodney W. Grimes /* 6786a06dea0SGarrett Wollman * Copy an entire packet, including header (which must be present). 6796a06dea0SGarrett Wollman * An optimization of the common case `m_copym(m, 0, M_COPYALL, how)'. 6801c38f2eaSArchie Cobbs * Note that the copy is read-only, because clusters are not copied, 6811c38f2eaSArchie Cobbs * only their reference counts are incremented. 6825fe86675SLuigi Rizzo * Preserve alignment of the first mbuf so if the creator has left 6835fe86675SLuigi Rizzo * some room at the beginning (e.g. for inserting protocol headers) 6845fe86675SLuigi Rizzo * the copies still have the room available. 6856a06dea0SGarrett Wollman */ 6866a06dea0SGarrett Wollman struct mbuf * 687122a814aSBosko Milekic m_copypacket(struct mbuf *m, int how) 6886a06dea0SGarrett Wollman { 6896a06dea0SGarrett Wollman struct mbuf *top, *n, *o; 6906a06dea0SGarrett Wollman 691063d8114SAlfred Perlstein MBUF_CHECKSLEEP(how); 692c95be8b5SGleb Smirnoff n = m_get(how, m->m_type); 6936a06dea0SGarrett Wollman top = n; 694122a814aSBosko Milekic if (n == NULL) 6956a06dea0SGarrett Wollman goto nospace; 6966a06dea0SGarrett Wollman 6979967cafcSSam Leffler if (!m_dup_pkthdr(n, m, how)) 6989967cafcSSam Leffler goto nospace; 6996a06dea0SGarrett Wollman n->m_len = m->m_len; 7006a06dea0SGarrett Wollman if (m->m_flags & M_EXT) { 7016a06dea0SGarrett Wollman n->m_data = m->m_data; 70256a4e45aSAndre Oppermann mb_dupcl(n, m); 7036a06dea0SGarrett Wollman } else { 7045fe86675SLuigi Rizzo n->m_data = n->m_pktdat + (m->m_data - m->m_pktdat ); 7056a06dea0SGarrett Wollman bcopy(mtod(m, char *), mtod(n, char *), n->m_len); 7066a06dea0SGarrett Wollman } 7076a06dea0SGarrett Wollman 7086a06dea0SGarrett Wollman m = m->m_next; 7096a06dea0SGarrett Wollman while (m) { 710c95be8b5SGleb Smirnoff o = m_get(how, m->m_type); 711122a814aSBosko Milekic if (o == NULL) 7126a06dea0SGarrett Wollman goto nospace; 7136a06dea0SGarrett Wollman 7146a06dea0SGarrett Wollman n->m_next = o; 7156a06dea0SGarrett Wollman n = n->m_next; 7166a06dea0SGarrett Wollman 7176a06dea0SGarrett Wollman n->m_len = m->m_len; 7186a06dea0SGarrett Wollman if (m->m_flags & M_EXT) { 7196a06dea0SGarrett Wollman n->m_data = m->m_data; 72056a4e45aSAndre Oppermann mb_dupcl(n, m); 7216a06dea0SGarrett Wollman } else { 7226a06dea0SGarrett Wollman bcopy(mtod(m, char *), mtod(n, char *), n->m_len); 7236a06dea0SGarrett Wollman } 7246a06dea0SGarrett Wollman 7256a06dea0SGarrett Wollman m = m->m_next; 7266a06dea0SGarrett Wollman } 7276a06dea0SGarrett Wollman return top; 7286a06dea0SGarrett Wollman nospace: 7296a06dea0SGarrett Wollman m_freem(top); 730122a814aSBosko Milekic return (NULL); 7316a06dea0SGarrett Wollman } 7326a06dea0SGarrett Wollman 7336a06dea0SGarrett Wollman /* 734df8bae1dSRodney W. Grimes * Copy data from an mbuf chain starting "off" bytes from the beginning, 735df8bae1dSRodney W. Grimes * continuing for "len" bytes, into the indicated buffer. 736df8bae1dSRodney W. Grimes */ 73726f9a767SRodney W. Grimes void 738a8cfc0eeSJulian Elischer m_copydata(const struct mbuf *m, int off, int len, caddr_t cp) 739df8bae1dSRodney W. Grimes { 740bd395ae8SBosko Milekic u_int count; 741df8bae1dSRodney W. Grimes 742e0a653ddSAlfred Perlstein KASSERT(off >= 0, ("m_copydata, negative off %d", off)); 743e0a653ddSAlfred Perlstein KASSERT(len >= 0, ("m_copydata, negative len %d", len)); 744df8bae1dSRodney W. Grimes while (off > 0) { 745e0a653ddSAlfred Perlstein KASSERT(m != NULL, ("m_copydata, offset > size of mbuf chain")); 746df8bae1dSRodney W. Grimes if (off < m->m_len) 747df8bae1dSRodney W. Grimes break; 748df8bae1dSRodney W. Grimes off -= m->m_len; 749df8bae1dSRodney W. Grimes m = m->m_next; 750df8bae1dSRodney W. Grimes } 751df8bae1dSRodney W. Grimes while (len > 0) { 752e0a653ddSAlfred Perlstein KASSERT(m != NULL, ("m_copydata, length > size of mbuf chain")); 753df8bae1dSRodney W. Grimes count = min(m->m_len - off, len); 754df8bae1dSRodney W. Grimes bcopy(mtod(m, caddr_t) + off, cp, count); 755df8bae1dSRodney W. Grimes len -= count; 756df8bae1dSRodney W. Grimes cp += count; 757df8bae1dSRodney W. Grimes off = 0; 758df8bae1dSRodney W. Grimes m = m->m_next; 759df8bae1dSRodney W. Grimes } 760df8bae1dSRodney W. Grimes } 761df8bae1dSRodney W. Grimes 762df8bae1dSRodney W. Grimes /* 7631c38f2eaSArchie Cobbs * Copy a packet header mbuf chain into a completely new chain, including 7641c38f2eaSArchie Cobbs * copying any mbuf clusters. Use this instead of m_copypacket() when 7651c38f2eaSArchie Cobbs * you need a writable copy of an mbuf chain. 7661c38f2eaSArchie Cobbs */ 7671c38f2eaSArchie Cobbs struct mbuf * 768122a814aSBosko Milekic m_dup(struct mbuf *m, int how) 7691c38f2eaSArchie Cobbs { 7701c38f2eaSArchie Cobbs struct mbuf **p, *top = NULL; 7711c38f2eaSArchie Cobbs int remain, moff, nsize; 7721c38f2eaSArchie Cobbs 773063d8114SAlfred Perlstein MBUF_CHECKSLEEP(how); 7741c38f2eaSArchie Cobbs /* Sanity check */ 7751c38f2eaSArchie Cobbs if (m == NULL) 776122a814aSBosko Milekic return (NULL); 777fe584538SDag-Erling Smørgrav M_ASSERTPKTHDR(m); 7781c38f2eaSArchie Cobbs 7791c38f2eaSArchie Cobbs /* While there's more data, get a new mbuf, tack it on, and fill it */ 7801c38f2eaSArchie Cobbs remain = m->m_pkthdr.len; 7811c38f2eaSArchie Cobbs moff = 0; 7821c38f2eaSArchie Cobbs p = ⊤ 7831c38f2eaSArchie Cobbs while (remain > 0 || top == NULL) { /* allow m->m_pkthdr.len == 0 */ 7841c38f2eaSArchie Cobbs struct mbuf *n; 7851c38f2eaSArchie Cobbs 7861c38f2eaSArchie Cobbs /* Get the next new mbuf */ 787099a0e58SBosko Milekic if (remain >= MINCLSIZE) { 788099a0e58SBosko Milekic n = m_getcl(how, m->m_type, 0); 789099a0e58SBosko Milekic nsize = MCLBYTES; 790099a0e58SBosko Milekic } else { 791099a0e58SBosko Milekic n = m_get(how, m->m_type); 792099a0e58SBosko Milekic nsize = MLEN; 793099a0e58SBosko Milekic } 7941c38f2eaSArchie Cobbs if (n == NULL) 7951c38f2eaSArchie Cobbs goto nospace; 796099a0e58SBosko Milekic 797099a0e58SBosko Milekic if (top == NULL) { /* First one, must be PKTHDR */ 798099a0e58SBosko Milekic if (!m_dup_pkthdr(n, m, how)) { 799099a0e58SBosko Milekic m_free(n); 8001c38f2eaSArchie Cobbs goto nospace; 8011c38f2eaSArchie Cobbs } 80263e6f390SEd Maste if ((n->m_flags & M_EXT) == 0) 803099a0e58SBosko Milekic nsize = MHLEN; 8041c38f2eaSArchie Cobbs } 8051c38f2eaSArchie Cobbs n->m_len = 0; 8061c38f2eaSArchie Cobbs 8071c38f2eaSArchie Cobbs /* Link it into the new chain */ 8081c38f2eaSArchie Cobbs *p = n; 8091c38f2eaSArchie Cobbs p = &n->m_next; 8101c38f2eaSArchie Cobbs 8111c38f2eaSArchie Cobbs /* Copy data from original mbuf(s) into new mbuf */ 8121c38f2eaSArchie Cobbs while (n->m_len < nsize && m != NULL) { 8131c38f2eaSArchie Cobbs int chunk = min(nsize - n->m_len, m->m_len - moff); 8141c38f2eaSArchie Cobbs 8151c38f2eaSArchie Cobbs bcopy(m->m_data + moff, n->m_data + n->m_len, chunk); 8161c38f2eaSArchie Cobbs moff += chunk; 8171c38f2eaSArchie Cobbs n->m_len += chunk; 8181c38f2eaSArchie Cobbs remain -= chunk; 8191c38f2eaSArchie Cobbs if (moff == m->m_len) { 8201c38f2eaSArchie Cobbs m = m->m_next; 8211c38f2eaSArchie Cobbs moff = 0; 8221c38f2eaSArchie Cobbs } 8231c38f2eaSArchie Cobbs } 8241c38f2eaSArchie Cobbs 8251c38f2eaSArchie Cobbs /* Check correct total mbuf length */ 8261c38f2eaSArchie Cobbs KASSERT((remain > 0 && m != NULL) || (remain == 0 && m == NULL), 827a48740b6SDavid E. O'Brien ("%s: bogus m_pkthdr.len", __func__)); 8281c38f2eaSArchie Cobbs } 8291c38f2eaSArchie Cobbs return (top); 8301c38f2eaSArchie Cobbs 8311c38f2eaSArchie Cobbs nospace: 8321c38f2eaSArchie Cobbs m_freem(top); 833122a814aSBosko Milekic return (NULL); 8341c38f2eaSArchie Cobbs } 8351c38f2eaSArchie Cobbs 8361c38f2eaSArchie Cobbs /* 837df8bae1dSRodney W. Grimes * Concatenate mbuf chain n to m. 838df8bae1dSRodney W. Grimes * Both chains must be of the same type (e.g. MT_DATA). 839df8bae1dSRodney W. Grimes * Any m_pkthdr is not updated. 840df8bae1dSRodney W. Grimes */ 84126f9a767SRodney W. Grimes void 842122a814aSBosko Milekic m_cat(struct mbuf *m, struct mbuf *n) 843df8bae1dSRodney W. Grimes { 844df8bae1dSRodney W. Grimes while (m->m_next) 845df8bae1dSRodney W. Grimes m = m->m_next; 846df8bae1dSRodney W. Grimes while (n) { 84714d7c5b1SAndre Oppermann if (!M_WRITABLE(m) || 84814d7c5b1SAndre Oppermann M_TRAILINGSPACE(m) < n->m_len) { 849df8bae1dSRodney W. Grimes /* just join the two chains */ 850df8bae1dSRodney W. Grimes m->m_next = n; 851df8bae1dSRodney W. Grimes return; 852df8bae1dSRodney W. Grimes } 853df8bae1dSRodney W. Grimes /* splat the data from one into the other */ 854df8bae1dSRodney W. Grimes bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len, 855df8bae1dSRodney W. Grimes (u_int)n->m_len); 856df8bae1dSRodney W. Grimes m->m_len += n->m_len; 857df8bae1dSRodney W. Grimes n = m_free(n); 858df8bae1dSRodney W. Grimes } 859df8bae1dSRodney W. Grimes } 860df8bae1dSRodney W. Grimes 8611967edbaSGleb Smirnoff /* 8621967edbaSGleb Smirnoff * Concatenate two pkthdr mbuf chains. 8631967edbaSGleb Smirnoff */ 8641967edbaSGleb Smirnoff void 8651967edbaSGleb Smirnoff m_catpkt(struct mbuf *m, struct mbuf *n) 8661967edbaSGleb Smirnoff { 8671967edbaSGleb Smirnoff 8681967edbaSGleb Smirnoff M_ASSERTPKTHDR(m); 8691967edbaSGleb Smirnoff M_ASSERTPKTHDR(n); 8701967edbaSGleb Smirnoff 8711967edbaSGleb Smirnoff m->m_pkthdr.len += n->m_pkthdr.len; 872651e4e6aSGleb Smirnoff m_demote(n, 1, 0); 8731967edbaSGleb Smirnoff 8741967edbaSGleb Smirnoff m_cat(m, n); 8751967edbaSGleb Smirnoff } 8761967edbaSGleb Smirnoff 87726f9a767SRodney W. Grimes void 878122a814aSBosko Milekic m_adj(struct mbuf *mp, int req_len) 879df8bae1dSRodney W. Grimes { 880122a814aSBosko Milekic int len = req_len; 881122a814aSBosko Milekic struct mbuf *m; 882122a814aSBosko Milekic int count; 883df8bae1dSRodney W. Grimes 884df8bae1dSRodney W. Grimes if ((m = mp) == NULL) 885df8bae1dSRodney W. Grimes return; 886df8bae1dSRodney W. Grimes if (len >= 0) { 887df8bae1dSRodney W. Grimes /* 888df8bae1dSRodney W. Grimes * Trim from head. 889df8bae1dSRodney W. Grimes */ 890df8bae1dSRodney W. Grimes while (m != NULL && len > 0) { 891df8bae1dSRodney W. Grimes if (m->m_len <= len) { 892df8bae1dSRodney W. Grimes len -= m->m_len; 893df8bae1dSRodney W. Grimes m->m_len = 0; 894df8bae1dSRodney W. Grimes m = m->m_next; 895df8bae1dSRodney W. Grimes } else { 896df8bae1dSRodney W. Grimes m->m_len -= len; 897df8bae1dSRodney W. Grimes m->m_data += len; 898df8bae1dSRodney W. Grimes len = 0; 899df8bae1dSRodney W. Grimes } 900df8bae1dSRodney W. Grimes } 901df8bae1dSRodney W. Grimes if (mp->m_flags & M_PKTHDR) 902a83baab6SMarko Zec mp->m_pkthdr.len -= (req_len - len); 903df8bae1dSRodney W. Grimes } else { 904df8bae1dSRodney W. Grimes /* 905df8bae1dSRodney W. Grimes * Trim from tail. Scan the mbuf chain, 906df8bae1dSRodney W. Grimes * calculating its length and finding the last mbuf. 907df8bae1dSRodney W. Grimes * If the adjustment only affects this mbuf, then just 908df8bae1dSRodney W. Grimes * adjust and return. Otherwise, rescan and truncate 909df8bae1dSRodney W. Grimes * after the remaining size. 910df8bae1dSRodney W. Grimes */ 911df8bae1dSRodney W. Grimes len = -len; 912df8bae1dSRodney W. Grimes count = 0; 913df8bae1dSRodney W. Grimes for (;;) { 914df8bae1dSRodney W. Grimes count += m->m_len; 915df8bae1dSRodney W. Grimes if (m->m_next == (struct mbuf *)0) 916df8bae1dSRodney W. Grimes break; 917df8bae1dSRodney W. Grimes m = m->m_next; 918df8bae1dSRodney W. Grimes } 919df8bae1dSRodney W. Grimes if (m->m_len >= len) { 920df8bae1dSRodney W. Grimes m->m_len -= len; 921df8bae1dSRodney W. Grimes if (mp->m_flags & M_PKTHDR) 922df8bae1dSRodney W. Grimes mp->m_pkthdr.len -= len; 923df8bae1dSRodney W. Grimes return; 924df8bae1dSRodney W. Grimes } 925df8bae1dSRodney W. Grimes count -= len; 926df8bae1dSRodney W. Grimes if (count < 0) 927df8bae1dSRodney W. Grimes count = 0; 928df8bae1dSRodney W. Grimes /* 929df8bae1dSRodney W. Grimes * Correct length for chain is "count". 930df8bae1dSRodney W. Grimes * Find the mbuf with last data, adjust its length, 931df8bae1dSRodney W. Grimes * and toss data from remaining mbufs on chain. 932df8bae1dSRodney W. Grimes */ 933df8bae1dSRodney W. Grimes m = mp; 934df8bae1dSRodney W. Grimes if (m->m_flags & M_PKTHDR) 935df8bae1dSRodney W. Grimes m->m_pkthdr.len = count; 936df8bae1dSRodney W. Grimes for (; m; m = m->m_next) { 937df8bae1dSRodney W. Grimes if (m->m_len >= count) { 938df8bae1dSRodney W. Grimes m->m_len = count; 93959d8b310SSam Leffler if (m->m_next != NULL) { 94059d8b310SSam Leffler m_freem(m->m_next); 94159d8b310SSam Leffler m->m_next = NULL; 94259d8b310SSam Leffler } 943df8bae1dSRodney W. Grimes break; 944df8bae1dSRodney W. Grimes } 945df8bae1dSRodney W. Grimes count -= m->m_len; 946df8bae1dSRodney W. Grimes } 947df8bae1dSRodney W. Grimes } 948df8bae1dSRodney W. Grimes } 949df8bae1dSRodney W. Grimes 950df8bae1dSRodney W. Grimes /* 951df8bae1dSRodney W. Grimes * Rearange an mbuf chain so that len bytes are contiguous 952a2c36a02SKevin Lo * and in the data area of an mbuf (so that mtod will work 953a2c36a02SKevin Lo * for a structure of size len). Returns the resulting 954df8bae1dSRodney W. Grimes * mbuf chain on success, frees it and returns null on failure. 955df8bae1dSRodney W. Grimes * If there is room, it will add up to max_protohdr-len extra bytes to the 956df8bae1dSRodney W. Grimes * contiguous region in an attempt to avoid being called next time. 957df8bae1dSRodney W. Grimes */ 958df8bae1dSRodney W. Grimes struct mbuf * 959122a814aSBosko Milekic m_pullup(struct mbuf *n, int len) 960df8bae1dSRodney W. Grimes { 961122a814aSBosko Milekic struct mbuf *m; 962122a814aSBosko Milekic int count; 963df8bae1dSRodney W. Grimes int space; 964df8bae1dSRodney W. Grimes 965df8bae1dSRodney W. Grimes /* 966df8bae1dSRodney W. Grimes * If first mbuf has no cluster, and has room for len bytes 967df8bae1dSRodney W. Grimes * without shifting current data, pullup into it, 968df8bae1dSRodney W. Grimes * otherwise allocate a new mbuf to prepend to the chain. 969df8bae1dSRodney W. Grimes */ 970df8bae1dSRodney W. Grimes if ((n->m_flags & M_EXT) == 0 && 971df8bae1dSRodney W. Grimes n->m_data + len < &n->m_dat[MLEN] && n->m_next) { 972df8bae1dSRodney W. Grimes if (n->m_len >= len) 973df8bae1dSRodney W. Grimes return (n); 974df8bae1dSRodney W. Grimes m = n; 975df8bae1dSRodney W. Grimes n = n->m_next; 976df8bae1dSRodney W. Grimes len -= m->m_len; 977df8bae1dSRodney W. Grimes } else { 978df8bae1dSRodney W. Grimes if (len > MHLEN) 979df8bae1dSRodney W. Grimes goto bad; 980c95be8b5SGleb Smirnoff m = m_get(M_NOWAIT, n->m_type); 981122a814aSBosko Milekic if (m == NULL) 982df8bae1dSRodney W. Grimes goto bad; 9839967cafcSSam Leffler if (n->m_flags & M_PKTHDR) 984c95be8b5SGleb Smirnoff m_move_pkthdr(m, n); 985df8bae1dSRodney W. Grimes } 986df8bae1dSRodney W. Grimes space = &m->m_dat[MLEN] - (m->m_data + m->m_len); 987df8bae1dSRodney W. Grimes do { 988df8bae1dSRodney W. Grimes count = min(min(max(len, max_protohdr), space), n->m_len); 989df8bae1dSRodney W. Grimes bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len, 990bd395ae8SBosko Milekic (u_int)count); 991df8bae1dSRodney W. Grimes len -= count; 992df8bae1dSRodney W. Grimes m->m_len += count; 993df8bae1dSRodney W. Grimes n->m_len -= count; 994df8bae1dSRodney W. Grimes space -= count; 995df8bae1dSRodney W. Grimes if (n->m_len) 996df8bae1dSRodney W. Grimes n->m_data += count; 997df8bae1dSRodney W. Grimes else 998df8bae1dSRodney W. Grimes n = m_free(n); 999df8bae1dSRodney W. Grimes } while (len > 0 && n); 1000df8bae1dSRodney W. Grimes if (len > 0) { 1001df8bae1dSRodney W. Grimes (void) m_free(m); 1002df8bae1dSRodney W. Grimes goto bad; 1003df8bae1dSRodney W. Grimes } 1004df8bae1dSRodney W. Grimes m->m_next = n; 1005df8bae1dSRodney W. Grimes return (m); 1006df8bae1dSRodney W. Grimes bad: 1007df8bae1dSRodney W. Grimes m_freem(n); 1008122a814aSBosko Milekic return (NULL); 1009df8bae1dSRodney W. Grimes } 1010df8bae1dSRodney W. Grimes 1011df8bae1dSRodney W. Grimes /* 10127ac139a9SJohn-Mark Gurney * Like m_pullup(), except a new mbuf is always allocated, and we allow 10137ac139a9SJohn-Mark Gurney * the amount of empty space before the data in the new mbuf to be specified 10147ac139a9SJohn-Mark Gurney * (in the event that the caller expects to prepend later). 10157ac139a9SJohn-Mark Gurney */ 10167ac139a9SJohn-Mark Gurney int MSFail; 10177ac139a9SJohn-Mark Gurney 10187ac139a9SJohn-Mark Gurney struct mbuf * 10197ac139a9SJohn-Mark Gurney m_copyup(struct mbuf *n, int len, int dstoff) 10207ac139a9SJohn-Mark Gurney { 10217ac139a9SJohn-Mark Gurney struct mbuf *m; 10227ac139a9SJohn-Mark Gurney int count, space; 10237ac139a9SJohn-Mark Gurney 10247ac139a9SJohn-Mark Gurney if (len > (MHLEN - dstoff)) 10257ac139a9SJohn-Mark Gurney goto bad; 1026c95be8b5SGleb Smirnoff m = m_get(M_NOWAIT, n->m_type); 10277ac139a9SJohn-Mark Gurney if (m == NULL) 10287ac139a9SJohn-Mark Gurney goto bad; 10297ac139a9SJohn-Mark Gurney if (n->m_flags & M_PKTHDR) 1030c95be8b5SGleb Smirnoff m_move_pkthdr(m, n); 10317ac139a9SJohn-Mark Gurney m->m_data += dstoff; 10327ac139a9SJohn-Mark Gurney space = &m->m_dat[MLEN] - (m->m_data + m->m_len); 10337ac139a9SJohn-Mark Gurney do { 10347ac139a9SJohn-Mark Gurney count = min(min(max(len, max_protohdr), space), n->m_len); 10357ac139a9SJohn-Mark Gurney memcpy(mtod(m, caddr_t) + m->m_len, mtod(n, caddr_t), 10367ac139a9SJohn-Mark Gurney (unsigned)count); 10377ac139a9SJohn-Mark Gurney len -= count; 10387ac139a9SJohn-Mark Gurney m->m_len += count; 10397ac139a9SJohn-Mark Gurney n->m_len -= count; 10407ac139a9SJohn-Mark Gurney space -= count; 10417ac139a9SJohn-Mark Gurney if (n->m_len) 10427ac139a9SJohn-Mark Gurney n->m_data += count; 10437ac139a9SJohn-Mark Gurney else 10447ac139a9SJohn-Mark Gurney n = m_free(n); 10457ac139a9SJohn-Mark Gurney } while (len > 0 && n); 10467ac139a9SJohn-Mark Gurney if (len > 0) { 10477ac139a9SJohn-Mark Gurney (void) m_free(m); 10487ac139a9SJohn-Mark Gurney goto bad; 10497ac139a9SJohn-Mark Gurney } 10507ac139a9SJohn-Mark Gurney m->m_next = n; 10517ac139a9SJohn-Mark Gurney return (m); 10527ac139a9SJohn-Mark Gurney bad: 10537ac139a9SJohn-Mark Gurney m_freem(n); 10547ac139a9SJohn-Mark Gurney MSFail++; 10557ac139a9SJohn-Mark Gurney return (NULL); 10567ac139a9SJohn-Mark Gurney } 10577ac139a9SJohn-Mark Gurney 10587ac139a9SJohn-Mark Gurney /* 1059df8bae1dSRodney W. Grimes * Partition an mbuf chain in two pieces, returning the tail -- 1060df8bae1dSRodney W. Grimes * all but the first len0 bytes. In case of failure, it returns NULL and 1061df8bae1dSRodney W. Grimes * attempts to restore the chain to its original state. 106248d183faSArchie Cobbs * 106348d183faSArchie Cobbs * Note that the resulting mbufs might be read-only, because the new 106448d183faSArchie Cobbs * mbuf can end up sharing an mbuf cluster with the original mbuf if 106548d183faSArchie Cobbs * the "breaking point" happens to lie within a cluster mbuf. Use the 106648d183faSArchie Cobbs * M_WRITABLE() macro to check for this case. 1067df8bae1dSRodney W. Grimes */ 1068df8bae1dSRodney W. Grimes struct mbuf * 1069122a814aSBosko Milekic m_split(struct mbuf *m0, int len0, int wait) 1070df8bae1dSRodney W. Grimes { 1071122a814aSBosko Milekic struct mbuf *m, *n; 1072bd395ae8SBosko Milekic u_int len = len0, remain; 1073df8bae1dSRodney W. Grimes 1074063d8114SAlfred Perlstein MBUF_CHECKSLEEP(wait); 1075df8bae1dSRodney W. Grimes for (m = m0; m && len > m->m_len; m = m->m_next) 1076df8bae1dSRodney W. Grimes len -= m->m_len; 1077122a814aSBosko Milekic if (m == NULL) 1078122a814aSBosko Milekic return (NULL); 1079df8bae1dSRodney W. Grimes remain = m->m_len - len; 108021f39848SGleb Smirnoff if (m0->m_flags & M_PKTHDR && remain == 0) { 108121f39848SGleb Smirnoff n = m_gethdr(wait, m0->m_type); 108277badb18SGleb Smirnoff if (n == NULL) 108321f39848SGleb Smirnoff return (NULL); 108421f39848SGleb Smirnoff n->m_next = m->m_next; 108521f39848SGleb Smirnoff m->m_next = NULL; 108621f39848SGleb Smirnoff n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif; 108721f39848SGleb Smirnoff n->m_pkthdr.len = m0->m_pkthdr.len - len0; 108821f39848SGleb Smirnoff m0->m_pkthdr.len = len0; 108921f39848SGleb Smirnoff return (n); 109021f39848SGleb Smirnoff } else if (m0->m_flags & M_PKTHDR) { 1091c95be8b5SGleb Smirnoff n = m_gethdr(wait, m0->m_type); 1092122a814aSBosko Milekic if (n == NULL) 1093122a814aSBosko Milekic return (NULL); 1094df8bae1dSRodney W. Grimes n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif; 1095df8bae1dSRodney W. Grimes n->m_pkthdr.len = m0->m_pkthdr.len - len0; 1096df8bae1dSRodney W. Grimes m0->m_pkthdr.len = len0; 1097df8bae1dSRodney W. Grimes if (m->m_flags & M_EXT) 1098df8bae1dSRodney W. Grimes goto extpacket; 1099df8bae1dSRodney W. Grimes if (remain > MHLEN) { 1100df8bae1dSRodney W. Grimes /* m can't be the lead packet */ 1101ed6a66caSRobert Watson M_ALIGN(n, 0); 1102df8bae1dSRodney W. Grimes n->m_next = m_split(m, len, wait); 1103122a814aSBosko Milekic if (n->m_next == NULL) { 1104df8bae1dSRodney W. Grimes (void) m_free(n); 1105122a814aSBosko Milekic return (NULL); 110640376987SJeffrey Hsu } else { 110740376987SJeffrey Hsu n->m_len = 0; 1108df8bae1dSRodney W. Grimes return (n); 110940376987SJeffrey Hsu } 1110df8bae1dSRodney W. Grimes } else 1111ed6a66caSRobert Watson M_ALIGN(n, remain); 1112df8bae1dSRodney W. Grimes } else if (remain == 0) { 1113df8bae1dSRodney W. Grimes n = m->m_next; 1114122a814aSBosko Milekic m->m_next = NULL; 1115df8bae1dSRodney W. Grimes return (n); 1116df8bae1dSRodney W. Grimes } else { 1117c95be8b5SGleb Smirnoff n = m_get(wait, m->m_type); 1118122a814aSBosko Milekic if (n == NULL) 1119122a814aSBosko Milekic return (NULL); 1120df8bae1dSRodney W. Grimes M_ALIGN(n, remain); 1121df8bae1dSRodney W. Grimes } 1122df8bae1dSRodney W. Grimes extpacket: 1123df8bae1dSRodney W. Grimes if (m->m_flags & M_EXT) { 1124df8bae1dSRodney W. Grimes n->m_data = m->m_data + len; 112556a4e45aSAndre Oppermann mb_dupcl(n, m); 1126df8bae1dSRodney W. Grimes } else { 1127df8bae1dSRodney W. Grimes bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain); 1128df8bae1dSRodney W. Grimes } 1129df8bae1dSRodney W. Grimes n->m_len = remain; 1130df8bae1dSRodney W. Grimes m->m_len = len; 1131df8bae1dSRodney W. Grimes n->m_next = m->m_next; 1132122a814aSBosko Milekic m->m_next = NULL; 1133df8bae1dSRodney W. Grimes return (n); 1134df8bae1dSRodney W. Grimes } 1135df8bae1dSRodney W. Grimes /* 1136df8bae1dSRodney W. Grimes * Routine to copy from device local memory into mbufs. 1137f5eece3fSBosko Milekic * Note that `off' argument is offset into first mbuf of target chain from 1138f5eece3fSBosko Milekic * which to begin copying the data to. 1139df8bae1dSRodney W. Grimes */ 1140df8bae1dSRodney W. Grimes struct mbuf * 1141f5eece3fSBosko Milekic m_devget(char *buf, int totlen, int off, struct ifnet *ifp, 1142122a814aSBosko Milekic void (*copy)(char *from, caddr_t to, u_int len)) 1143df8bae1dSRodney W. Grimes { 1144122a814aSBosko Milekic struct mbuf *m; 1145099a0e58SBosko Milekic struct mbuf *top = NULL, **mp = ⊤ 1146f5eece3fSBosko Milekic int len; 1147df8bae1dSRodney W. Grimes 1148f5eece3fSBosko Milekic if (off < 0 || off > MHLEN) 1149f5eece3fSBosko Milekic return (NULL); 1150f5eece3fSBosko Milekic 1151df8bae1dSRodney W. Grimes while (totlen > 0) { 1152099a0e58SBosko Milekic if (top == NULL) { /* First one, must be PKTHDR */ 1153f5eece3fSBosko Milekic if (totlen + off >= MINCLSIZE) { 1154eb1b1807SGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1155f5eece3fSBosko Milekic len = MCLBYTES; 1156df8bae1dSRodney W. Grimes } else { 1157eb1b1807SGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA); 1158099a0e58SBosko Milekic len = MHLEN; 1159099a0e58SBosko Milekic 1160099a0e58SBosko Milekic /* Place initial small packet/header at end of mbuf */ 1161099a0e58SBosko Milekic if (m && totlen + off + max_linkhdr <= MLEN) { 1162df8bae1dSRodney W. Grimes m->m_data += max_linkhdr; 1163f5eece3fSBosko Milekic len -= max_linkhdr; 1164df8bae1dSRodney W. Grimes } 1165f5eece3fSBosko Milekic } 1166099a0e58SBosko Milekic if (m == NULL) 1167099a0e58SBosko Milekic return NULL; 1168099a0e58SBosko Milekic m->m_pkthdr.rcvif = ifp; 1169099a0e58SBosko Milekic m->m_pkthdr.len = totlen; 1170099a0e58SBosko Milekic } else { 1171099a0e58SBosko Milekic if (totlen + off >= MINCLSIZE) { 1172eb1b1807SGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, 0); 1173099a0e58SBosko Milekic len = MCLBYTES; 1174099a0e58SBosko Milekic } else { 1175eb1b1807SGleb Smirnoff m = m_get(M_NOWAIT, MT_DATA); 1176099a0e58SBosko Milekic len = MLEN; 1177099a0e58SBosko Milekic } 1178099a0e58SBosko Milekic if (m == NULL) { 1179099a0e58SBosko Milekic m_freem(top); 1180099a0e58SBosko Milekic return NULL; 1181099a0e58SBosko Milekic } 1182099a0e58SBosko Milekic } 1183f5eece3fSBosko Milekic if (off) { 1184f5eece3fSBosko Milekic m->m_data += off; 1185f5eece3fSBosko Milekic len -= off; 1186f5eece3fSBosko Milekic off = 0; 1187f5eece3fSBosko Milekic } 1188f5eece3fSBosko Milekic m->m_len = len = min(totlen, len); 1189df8bae1dSRodney W. Grimes if (copy) 1190bd395ae8SBosko Milekic copy(buf, mtod(m, caddr_t), (u_int)len); 1191df8bae1dSRodney W. Grimes else 1192bd395ae8SBosko Milekic bcopy(buf, mtod(m, caddr_t), (u_int)len); 1193f5eece3fSBosko Milekic buf += len; 1194df8bae1dSRodney W. Grimes *mp = m; 1195df8bae1dSRodney W. Grimes mp = &m->m_next; 1196df8bae1dSRodney W. Grimes totlen -= len; 1197df8bae1dSRodney W. Grimes } 1198df8bae1dSRodney W. Grimes return (top); 1199df8bae1dSRodney W. Grimes } 1200c5789ba3SPoul-Henning Kamp 1201c5789ba3SPoul-Henning Kamp /* 1202c5789ba3SPoul-Henning Kamp * Copy data from a buffer back into the indicated mbuf chain, 1203c5789ba3SPoul-Henning Kamp * starting "off" bytes from the beginning, extending the mbuf 1204c5789ba3SPoul-Henning Kamp * chain if necessary. 1205c5789ba3SPoul-Henning Kamp */ 1206c5789ba3SPoul-Henning Kamp void 120724665342SLuigi Rizzo m_copyback(struct mbuf *m0, int off, int len, c_caddr_t cp) 1208c5789ba3SPoul-Henning Kamp { 1209122a814aSBosko Milekic int mlen; 1210122a814aSBosko Milekic struct mbuf *m = m0, *n; 1211c5789ba3SPoul-Henning Kamp int totlen = 0; 1212c5789ba3SPoul-Henning Kamp 1213122a814aSBosko Milekic if (m0 == NULL) 1214c5789ba3SPoul-Henning Kamp return; 1215c5789ba3SPoul-Henning Kamp while (off > (mlen = m->m_len)) { 1216c5789ba3SPoul-Henning Kamp off -= mlen; 1217c5789ba3SPoul-Henning Kamp totlen += mlen; 1218122a814aSBosko Milekic if (m->m_next == NULL) { 1219eb1b1807SGleb Smirnoff n = m_get(M_NOWAIT, m->m_type); 1220122a814aSBosko Milekic if (n == NULL) 1221c5789ba3SPoul-Henning Kamp goto out; 1222099a0e58SBosko Milekic bzero(mtod(n, caddr_t), MLEN); 1223c5789ba3SPoul-Henning Kamp n->m_len = min(MLEN, len + off); 1224c5789ba3SPoul-Henning Kamp m->m_next = n; 1225c5789ba3SPoul-Henning Kamp } 1226c5789ba3SPoul-Henning Kamp m = m->m_next; 1227c5789ba3SPoul-Henning Kamp } 1228c5789ba3SPoul-Henning Kamp while (len > 0) { 1229129c5c81SAlexander Motin if (m->m_next == NULL && (len > m->m_len - off)) { 1230129c5c81SAlexander Motin m->m_len += min(len - (m->m_len - off), 1231129c5c81SAlexander Motin M_TRAILINGSPACE(m)); 1232129c5c81SAlexander Motin } 1233c5789ba3SPoul-Henning Kamp mlen = min (m->m_len - off, len); 1234bd395ae8SBosko Milekic bcopy(cp, off + mtod(m, caddr_t), (u_int)mlen); 1235c5789ba3SPoul-Henning Kamp cp += mlen; 1236c5789ba3SPoul-Henning Kamp len -= mlen; 1237c5789ba3SPoul-Henning Kamp mlen += off; 1238c5789ba3SPoul-Henning Kamp off = 0; 1239c5789ba3SPoul-Henning Kamp totlen += mlen; 1240c5789ba3SPoul-Henning Kamp if (len == 0) 1241c5789ba3SPoul-Henning Kamp break; 1242122a814aSBosko Milekic if (m->m_next == NULL) { 1243eb1b1807SGleb Smirnoff n = m_get(M_NOWAIT, m->m_type); 1244122a814aSBosko Milekic if (n == NULL) 1245c5789ba3SPoul-Henning Kamp break; 1246c5789ba3SPoul-Henning Kamp n->m_len = min(MLEN, len); 1247c5789ba3SPoul-Henning Kamp m->m_next = n; 1248c5789ba3SPoul-Henning Kamp } 1249c5789ba3SPoul-Henning Kamp m = m->m_next; 1250c5789ba3SPoul-Henning Kamp } 1251c5789ba3SPoul-Henning Kamp out: if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) 1252c5789ba3SPoul-Henning Kamp m->m_pkthdr.len = totlen; 1253c5789ba3SPoul-Henning Kamp } 1254ce4a64f7SPoul-Henning Kamp 125537621fd5SBruce M Simpson /* 12564873d175SSam Leffler * Append the specified data to the indicated mbuf chain, 12574873d175SSam Leffler * Extend the mbuf chain if the new data does not fit in 12584873d175SSam Leffler * existing space. 12594873d175SSam Leffler * 12604873d175SSam Leffler * Return 1 if able to complete the job; otherwise 0. 12614873d175SSam Leffler */ 12624873d175SSam Leffler int 12634873d175SSam Leffler m_append(struct mbuf *m0, int len, c_caddr_t cp) 12644873d175SSam Leffler { 12654873d175SSam Leffler struct mbuf *m, *n; 12664873d175SSam Leffler int remainder, space; 12674873d175SSam Leffler 12684873d175SSam Leffler for (m = m0; m->m_next != NULL; m = m->m_next) 12694873d175SSam Leffler ; 12704873d175SSam Leffler remainder = len; 12714873d175SSam Leffler space = M_TRAILINGSPACE(m); 12724873d175SSam Leffler if (space > 0) { 12734873d175SSam Leffler /* 12744873d175SSam Leffler * Copy into available space. 12754873d175SSam Leffler */ 12764873d175SSam Leffler if (space > remainder) 12774873d175SSam Leffler space = remainder; 12784873d175SSam Leffler bcopy(cp, mtod(m, caddr_t) + m->m_len, space); 12794873d175SSam Leffler m->m_len += space; 12804873d175SSam Leffler cp += space, remainder -= space; 12814873d175SSam Leffler } 12824873d175SSam Leffler while (remainder > 0) { 12834873d175SSam Leffler /* 12844873d175SSam Leffler * Allocate a new mbuf; could check space 12854873d175SSam Leffler * and allocate a cluster instead. 12864873d175SSam Leffler */ 1287eb1b1807SGleb Smirnoff n = m_get(M_NOWAIT, m->m_type); 12884873d175SSam Leffler if (n == NULL) 12894873d175SSam Leffler break; 12904873d175SSam Leffler n->m_len = min(MLEN, remainder); 1291a37c415eSSam Leffler bcopy(cp, mtod(n, caddr_t), n->m_len); 1292a37c415eSSam Leffler cp += n->m_len, remainder -= n->m_len; 12934873d175SSam Leffler m->m_next = n; 12944873d175SSam Leffler m = n; 12954873d175SSam Leffler } 12964873d175SSam Leffler if (m0->m_flags & M_PKTHDR) 12974873d175SSam Leffler m0->m_pkthdr.len += len - remainder; 12984873d175SSam Leffler return (remainder == 0); 12994873d175SSam Leffler } 13004873d175SSam Leffler 13014873d175SSam Leffler /* 130237621fd5SBruce M Simpson * Apply function f to the data in an mbuf chain starting "off" bytes from 130337621fd5SBruce M Simpson * the beginning, continuing for "len" bytes. 130437621fd5SBruce M Simpson */ 130537621fd5SBruce M Simpson int 130637621fd5SBruce M Simpson m_apply(struct mbuf *m, int off, int len, 130754065297SBruce M Simpson int (*f)(void *, void *, u_int), void *arg) 130837621fd5SBruce M Simpson { 130954065297SBruce M Simpson u_int count; 131037621fd5SBruce M Simpson int rval; 131137621fd5SBruce M Simpson 131237621fd5SBruce M Simpson KASSERT(off >= 0, ("m_apply, negative off %d", off)); 131337621fd5SBruce M Simpson KASSERT(len >= 0, ("m_apply, negative len %d", len)); 131437621fd5SBruce M Simpson while (off > 0) { 131537621fd5SBruce M Simpson KASSERT(m != NULL, ("m_apply, offset > size of mbuf chain")); 131637621fd5SBruce M Simpson if (off < m->m_len) 131737621fd5SBruce M Simpson break; 131837621fd5SBruce M Simpson off -= m->m_len; 131937621fd5SBruce M Simpson m = m->m_next; 132037621fd5SBruce M Simpson } 132137621fd5SBruce M Simpson while (len > 0) { 132237621fd5SBruce M Simpson KASSERT(m != NULL, ("m_apply, offset > size of mbuf chain")); 132337621fd5SBruce M Simpson count = min(m->m_len - off, len); 132437621fd5SBruce M Simpson rval = (*f)(arg, mtod(m, caddr_t) + off, count); 132537621fd5SBruce M Simpson if (rval) 132637621fd5SBruce M Simpson return (rval); 132737621fd5SBruce M Simpson len -= count; 132837621fd5SBruce M Simpson off = 0; 132937621fd5SBruce M Simpson m = m->m_next; 133037621fd5SBruce M Simpson } 133137621fd5SBruce M Simpson return (0); 133237621fd5SBruce M Simpson } 133337621fd5SBruce M Simpson 133437621fd5SBruce M Simpson /* 133537621fd5SBruce M Simpson * Return a pointer to mbuf/offset of location in mbuf chain. 133637621fd5SBruce M Simpson */ 133737621fd5SBruce M Simpson struct mbuf * 133837621fd5SBruce M Simpson m_getptr(struct mbuf *m, int loc, int *off) 133937621fd5SBruce M Simpson { 134037621fd5SBruce M Simpson 134137621fd5SBruce M Simpson while (loc >= 0) { 134254065297SBruce M Simpson /* Normal end of search. */ 134337621fd5SBruce M Simpson if (m->m_len > loc) { 134437621fd5SBruce M Simpson *off = loc; 134537621fd5SBruce M Simpson return (m); 134637621fd5SBruce M Simpson } else { 134737621fd5SBruce M Simpson loc -= m->m_len; 134837621fd5SBruce M Simpson if (m->m_next == NULL) { 134937621fd5SBruce M Simpson if (loc == 0) { 135054065297SBruce M Simpson /* Point at the end of valid data. */ 135137621fd5SBruce M Simpson *off = m->m_len; 135237621fd5SBruce M Simpson return (m); 135354065297SBruce M Simpson } 135437621fd5SBruce M Simpson return (NULL); 135554065297SBruce M Simpson } 135637621fd5SBruce M Simpson m = m->m_next; 135737621fd5SBruce M Simpson } 135837621fd5SBruce M Simpson } 135937621fd5SBruce M Simpson return (NULL); 136037621fd5SBruce M Simpson } 136137621fd5SBruce M Simpson 1362ce4a64f7SPoul-Henning Kamp void 13637b125090SJohn-Mark Gurney m_print(const struct mbuf *m, int maxlen) 1364ce4a64f7SPoul-Henning Kamp { 1365ce4a64f7SPoul-Henning Kamp int len; 13667b125090SJohn-Mark Gurney int pdata; 13676357e7b5SEivind Eklund const struct mbuf *m2; 1368ce4a64f7SPoul-Henning Kamp 13697e949c46SKenneth D. Merry if (m == NULL) { 13707e949c46SKenneth D. Merry printf("mbuf: %p\n", m); 13717e949c46SKenneth D. Merry return; 13727e949c46SKenneth D. Merry } 13737e949c46SKenneth D. Merry 13747b125090SJohn-Mark Gurney if (m->m_flags & M_PKTHDR) 1375ce4a64f7SPoul-Henning Kamp len = m->m_pkthdr.len; 13767b125090SJohn-Mark Gurney else 13777b125090SJohn-Mark Gurney len = -1; 1378ce4a64f7SPoul-Henning Kamp m2 = m; 13797b125090SJohn-Mark Gurney while (m2 != NULL && (len == -1 || len)) { 13807b125090SJohn-Mark Gurney pdata = m2->m_len; 13817b125090SJohn-Mark Gurney if (maxlen != -1 && pdata > maxlen) 13827b125090SJohn-Mark Gurney pdata = maxlen; 13837b125090SJohn-Mark Gurney printf("mbuf: %p len: %d, next: %p, %b%s", m2, m2->m_len, 13847b125090SJohn-Mark Gurney m2->m_next, m2->m_flags, "\20\20freelist\17skipfw" 13857b125090SJohn-Mark Gurney "\11proto5\10proto4\7proto3\6proto2\5proto1\4rdonly" 13867b125090SJohn-Mark Gurney "\3eor\2pkthdr\1ext", pdata ? "" : "\n"); 13877b125090SJohn-Mark Gurney if (pdata) 138845e0d0aaSJohn-Mark Gurney printf(", %*D\n", pdata, (u_char *)m2->m_data, "-"); 13897b125090SJohn-Mark Gurney if (len != -1) 1390ce4a64f7SPoul-Henning Kamp len -= m2->m_len; 1391ce4a64f7SPoul-Henning Kamp m2 = m2->m_next; 1392ce4a64f7SPoul-Henning Kamp } 13937b125090SJohn-Mark Gurney if (len > 0) 13947b125090SJohn-Mark Gurney printf("%d bytes unaccounted for.\n", len); 1395ce4a64f7SPoul-Henning Kamp return; 1396ce4a64f7SPoul-Henning Kamp } 13973f2e06c5SPoul-Henning Kamp 1398bd395ae8SBosko Milekic u_int 13993f2e06c5SPoul-Henning Kamp m_fixhdr(struct mbuf *m0) 14003f2e06c5SPoul-Henning Kamp { 1401bd395ae8SBosko Milekic u_int len; 14023f2e06c5SPoul-Henning Kamp 1403ac6e585dSPoul-Henning Kamp len = m_length(m0, NULL); 14043f2e06c5SPoul-Henning Kamp m0->m_pkthdr.len = len; 1405ac6e585dSPoul-Henning Kamp return (len); 1406ac6e585dSPoul-Henning Kamp } 1407ac6e585dSPoul-Henning Kamp 1408bd395ae8SBosko Milekic u_int 1409ac6e585dSPoul-Henning Kamp m_length(struct mbuf *m0, struct mbuf **last) 1410ac6e585dSPoul-Henning Kamp { 1411ac6e585dSPoul-Henning Kamp struct mbuf *m; 1412bd395ae8SBosko Milekic u_int len; 1413ac6e585dSPoul-Henning Kamp 1414ac6e585dSPoul-Henning Kamp len = 0; 1415ac6e585dSPoul-Henning Kamp for (m = m0; m != NULL; m = m->m_next) { 1416ac6e585dSPoul-Henning Kamp len += m->m_len; 1417ac6e585dSPoul-Henning Kamp if (m->m_next == NULL) 1418ac6e585dSPoul-Henning Kamp break; 1419ac6e585dSPoul-Henning Kamp } 1420ac6e585dSPoul-Henning Kamp if (last != NULL) 1421ac6e585dSPoul-Henning Kamp *last = m; 1422ac6e585dSPoul-Henning Kamp return (len); 14233f2e06c5SPoul-Henning Kamp } 142455e9f80dSMike Silbersack 142555e9f80dSMike Silbersack /* 142655e9f80dSMike Silbersack * Defragment a mbuf chain, returning the shortest possible 142755e9f80dSMike Silbersack * chain of mbufs and clusters. If allocation fails and 142855e9f80dSMike Silbersack * this cannot be completed, NULL will be returned, but 142955e9f80dSMike Silbersack * the passed in chain will be unchanged. Upon success, 143055e9f80dSMike Silbersack * the original chain will be freed, and the new chain 143155e9f80dSMike Silbersack * will be returned. 143255e9f80dSMike Silbersack * 143355e9f80dSMike Silbersack * If a non-packet header is passed in, the original 143455e9f80dSMike Silbersack * mbuf (chain?) will be returned unharmed. 143555e9f80dSMike Silbersack */ 143655e9f80dSMike Silbersack struct mbuf * 143755e9f80dSMike Silbersack m_defrag(struct mbuf *m0, int how) 143855e9f80dSMike Silbersack { 143955e9f80dSMike Silbersack struct mbuf *m_new = NULL, *m_final = NULL; 144055e9f80dSMike Silbersack int progress = 0, length; 144155e9f80dSMike Silbersack 1442063d8114SAlfred Perlstein MBUF_CHECKSLEEP(how); 144355e9f80dSMike Silbersack if (!(m0->m_flags & M_PKTHDR)) 144455e9f80dSMike Silbersack return (m0); 144555e9f80dSMike Silbersack 1446f8bf8e39SMike Silbersack m_fixhdr(m0); /* Needed sanity check */ 1447f8bf8e39SMike Silbersack 1448352d050eSMike Silbersack #ifdef MBUF_STRESS_TEST 1449352d050eSMike Silbersack if (m_defragrandomfailures) { 1450352d050eSMike Silbersack int temp = arc4random() & 0xff; 1451352d050eSMike Silbersack if (temp == 0xba) 1452352d050eSMike Silbersack goto nospace; 1453352d050eSMike Silbersack } 1454352d050eSMike Silbersack #endif 145555e9f80dSMike Silbersack 145655e9f80dSMike Silbersack if (m0->m_pkthdr.len > MHLEN) 145755e9f80dSMike Silbersack m_final = m_getcl(how, MT_DATA, M_PKTHDR); 145855e9f80dSMike Silbersack else 145955e9f80dSMike Silbersack m_final = m_gethdr(how, MT_DATA); 146055e9f80dSMike Silbersack 146155e9f80dSMike Silbersack if (m_final == NULL) 146255e9f80dSMike Silbersack goto nospace; 146355e9f80dSMike Silbersack 1464a89ec05eSPeter Wemm if (m_dup_pkthdr(m_final, m0, how) == 0) 146555e9f80dSMike Silbersack goto nospace; 146655e9f80dSMike Silbersack 146755e9f80dSMike Silbersack m_new = m_final; 146855e9f80dSMike Silbersack 146955e9f80dSMike Silbersack while (progress < m0->m_pkthdr.len) { 147055e9f80dSMike Silbersack length = m0->m_pkthdr.len - progress; 147155e9f80dSMike Silbersack if (length > MCLBYTES) 147255e9f80dSMike Silbersack length = MCLBYTES; 147355e9f80dSMike Silbersack 147455e9f80dSMike Silbersack if (m_new == NULL) { 147555e9f80dSMike Silbersack if (length > MLEN) 147655e9f80dSMike Silbersack m_new = m_getcl(how, MT_DATA, 0); 147755e9f80dSMike Silbersack else 147855e9f80dSMike Silbersack m_new = m_get(how, MT_DATA); 147955e9f80dSMike Silbersack if (m_new == NULL) 148055e9f80dSMike Silbersack goto nospace; 148155e9f80dSMike Silbersack } 148255e9f80dSMike Silbersack 148355e9f80dSMike Silbersack m_copydata(m0, progress, length, mtod(m_new, caddr_t)); 148455e9f80dSMike Silbersack progress += length; 148555e9f80dSMike Silbersack m_new->m_len = length; 148655e9f80dSMike Silbersack if (m_new != m_final) 148755e9f80dSMike Silbersack m_cat(m_final, m_new); 148855e9f80dSMike Silbersack m_new = NULL; 148955e9f80dSMike Silbersack } 149051710a45SMike Silbersack #ifdef MBUF_STRESS_TEST 149155e9f80dSMike Silbersack if (m0->m_next == NULL) 149255e9f80dSMike Silbersack m_defraguseless++; 149351710a45SMike Silbersack #endif 149455e9f80dSMike Silbersack m_freem(m0); 149555e9f80dSMike Silbersack m0 = m_final; 149651710a45SMike Silbersack #ifdef MBUF_STRESS_TEST 149755e9f80dSMike Silbersack m_defragpackets++; 149855e9f80dSMike Silbersack m_defragbytes += m0->m_pkthdr.len; 149951710a45SMike Silbersack #endif 150055e9f80dSMike Silbersack return (m0); 150155e9f80dSMike Silbersack nospace: 150251710a45SMike Silbersack #ifdef MBUF_STRESS_TEST 150355e9f80dSMike Silbersack m_defragfailure++; 150451710a45SMike Silbersack #endif 150555e9f80dSMike Silbersack if (m_final) 150655e9f80dSMike Silbersack m_freem(m_final); 150755e9f80dSMike Silbersack return (NULL); 150855e9f80dSMike Silbersack } 15093390d476SMike Silbersack 1510eeb76a18SSam Leffler /* 1511eeb76a18SSam Leffler * Defragment an mbuf chain, returning at most maxfrags separate 1512eeb76a18SSam Leffler * mbufs+clusters. If this is not possible NULL is returned and 1513eeb76a18SSam Leffler * the original mbuf chain is left in it's present (potentially 1514eeb76a18SSam Leffler * modified) state. We use two techniques: collapsing consecutive 1515eeb76a18SSam Leffler * mbufs and replacing consecutive mbufs by a cluster. 1516eeb76a18SSam Leffler * 1517eeb76a18SSam Leffler * NB: this should really be named m_defrag but that name is taken 1518eeb76a18SSam Leffler */ 1519eeb76a18SSam Leffler struct mbuf * 1520eeb76a18SSam Leffler m_collapse(struct mbuf *m0, int how, int maxfrags) 1521eeb76a18SSam Leffler { 1522eeb76a18SSam Leffler struct mbuf *m, *n, *n2, **prev; 1523eeb76a18SSam Leffler u_int curfrags; 1524eeb76a18SSam Leffler 1525eeb76a18SSam Leffler /* 1526eeb76a18SSam Leffler * Calculate the current number of frags. 1527eeb76a18SSam Leffler */ 1528eeb76a18SSam Leffler curfrags = 0; 1529eeb76a18SSam Leffler for (m = m0; m != NULL; m = m->m_next) 1530eeb76a18SSam Leffler curfrags++; 1531eeb76a18SSam Leffler /* 1532eeb76a18SSam Leffler * First, try to collapse mbufs. Note that we always collapse 1533eeb76a18SSam Leffler * towards the front so we don't need to deal with moving the 1534eeb76a18SSam Leffler * pkthdr. This may be suboptimal if the first mbuf has much 1535eeb76a18SSam Leffler * less data than the following. 1536eeb76a18SSam Leffler */ 1537eeb76a18SSam Leffler m = m0; 1538eeb76a18SSam Leffler again: 1539eeb76a18SSam Leffler for (;;) { 1540eeb76a18SSam Leffler n = m->m_next; 1541eeb76a18SSam Leffler if (n == NULL) 1542eeb76a18SSam Leffler break; 154314d7c5b1SAndre Oppermann if (M_WRITABLE(m) && 1544eeb76a18SSam Leffler n->m_len < M_TRAILINGSPACE(m)) { 1545eeb76a18SSam Leffler bcopy(mtod(n, void *), mtod(m, char *) + m->m_len, 1546eeb76a18SSam Leffler n->m_len); 1547eeb76a18SSam Leffler m->m_len += n->m_len; 1548eeb76a18SSam Leffler m->m_next = n->m_next; 1549eeb76a18SSam Leffler m_free(n); 1550eeb76a18SSam Leffler if (--curfrags <= maxfrags) 1551eeb76a18SSam Leffler return m0; 1552eeb76a18SSam Leffler } else 1553eeb76a18SSam Leffler m = n; 1554eeb76a18SSam Leffler } 1555eeb76a18SSam Leffler KASSERT(maxfrags > 1, 1556eeb76a18SSam Leffler ("maxfrags %u, but normal collapse failed", maxfrags)); 1557eeb76a18SSam Leffler /* 1558eeb76a18SSam Leffler * Collapse consecutive mbufs to a cluster. 1559eeb76a18SSam Leffler */ 1560eeb76a18SSam Leffler prev = &m0->m_next; /* NB: not the first mbuf */ 1561eeb76a18SSam Leffler while ((n = *prev) != NULL) { 1562eeb76a18SSam Leffler if ((n2 = n->m_next) != NULL && 1563eeb76a18SSam Leffler n->m_len + n2->m_len < MCLBYTES) { 1564eeb76a18SSam Leffler m = m_getcl(how, MT_DATA, 0); 1565eeb76a18SSam Leffler if (m == NULL) 1566eeb76a18SSam Leffler goto bad; 1567eeb76a18SSam Leffler bcopy(mtod(n, void *), mtod(m, void *), n->m_len); 1568eeb76a18SSam Leffler bcopy(mtod(n2, void *), mtod(m, char *) + n->m_len, 1569eeb76a18SSam Leffler n2->m_len); 1570eeb76a18SSam Leffler m->m_len = n->m_len + n2->m_len; 1571eeb76a18SSam Leffler m->m_next = n2->m_next; 1572eeb76a18SSam Leffler *prev = m; 1573eeb76a18SSam Leffler m_free(n); 1574eeb76a18SSam Leffler m_free(n2); 1575eeb76a18SSam Leffler if (--curfrags <= maxfrags) /* +1 cl -2 mbufs */ 1576eeb76a18SSam Leffler return m0; 1577eeb76a18SSam Leffler /* 1578eeb76a18SSam Leffler * Still not there, try the normal collapse 1579eeb76a18SSam Leffler * again before we allocate another cluster. 1580eeb76a18SSam Leffler */ 1581eeb76a18SSam Leffler goto again; 1582eeb76a18SSam Leffler } 1583eeb76a18SSam Leffler prev = &n->m_next; 1584eeb76a18SSam Leffler } 1585eeb76a18SSam Leffler /* 1586eeb76a18SSam Leffler * No place where we can collapse to a cluster; punt. 1587eeb76a18SSam Leffler * This can occur if, for example, you request 2 frags 1588eeb76a18SSam Leffler * but the packet requires that both be clusters (we 1589eeb76a18SSam Leffler * never reallocate the first mbuf to avoid moving the 1590eeb76a18SSam Leffler * packet header). 1591eeb76a18SSam Leffler */ 1592eeb76a18SSam Leffler bad: 1593eeb76a18SSam Leffler return NULL; 1594eeb76a18SSam Leffler } 1595eeb76a18SSam Leffler 15963390d476SMike Silbersack #ifdef MBUF_STRESS_TEST 15973390d476SMike Silbersack 15983390d476SMike Silbersack /* 15993390d476SMike Silbersack * Fragment an mbuf chain. There's no reason you'd ever want to do 16003390d476SMike Silbersack * this in normal usage, but it's great for stress testing various 16013390d476SMike Silbersack * mbuf consumers. 16023390d476SMike Silbersack * 16033390d476SMike Silbersack * If fragmentation is not possible, the original chain will be 16043390d476SMike Silbersack * returned. 16053390d476SMike Silbersack * 16063390d476SMike Silbersack * Possible length values: 16073390d476SMike Silbersack * 0 no fragmentation will occur 16083390d476SMike Silbersack * > 0 each fragment will be of the specified length 16093390d476SMike Silbersack * -1 each fragment will be the same random value in length 16103390d476SMike Silbersack * -2 each fragment's length will be entirely random 16113390d476SMike Silbersack * (Random values range from 1 to 256) 16123390d476SMike Silbersack */ 16133390d476SMike Silbersack struct mbuf * 16143390d476SMike Silbersack m_fragment(struct mbuf *m0, int how, int length) 16153390d476SMike Silbersack { 16163390d476SMike Silbersack struct mbuf *m_new = NULL, *m_final = NULL; 16173390d476SMike Silbersack int progress = 0; 16183390d476SMike Silbersack 16193390d476SMike Silbersack if (!(m0->m_flags & M_PKTHDR)) 16203390d476SMike Silbersack return (m0); 16213390d476SMike Silbersack 16223390d476SMike Silbersack if ((length == 0) || (length < -2)) 16233390d476SMike Silbersack return (m0); 16243390d476SMike Silbersack 16253390d476SMike Silbersack m_fixhdr(m0); /* Needed sanity check */ 16263390d476SMike Silbersack 16273390d476SMike Silbersack m_final = m_getcl(how, MT_DATA, M_PKTHDR); 16283390d476SMike Silbersack 16293390d476SMike Silbersack if (m_final == NULL) 16303390d476SMike Silbersack goto nospace; 16313390d476SMike Silbersack 16328dee2f67SMike Silbersack if (m_dup_pkthdr(m_final, m0, how) == 0) 16333390d476SMike Silbersack goto nospace; 16343390d476SMike Silbersack 16353390d476SMike Silbersack m_new = m_final; 16363390d476SMike Silbersack 16373390d476SMike Silbersack if (length == -1) 16383390d476SMike Silbersack length = 1 + (arc4random() & 255); 16393390d476SMike Silbersack 16403390d476SMike Silbersack while (progress < m0->m_pkthdr.len) { 16413390d476SMike Silbersack int fraglen; 16423390d476SMike Silbersack 16433390d476SMike Silbersack if (length > 0) 16443390d476SMike Silbersack fraglen = length; 16453390d476SMike Silbersack else 16463390d476SMike Silbersack fraglen = 1 + (arc4random() & 255); 16473390d476SMike Silbersack if (fraglen > m0->m_pkthdr.len - progress) 16483390d476SMike Silbersack fraglen = m0->m_pkthdr.len - progress; 16493390d476SMike Silbersack 16503390d476SMike Silbersack if (fraglen > MCLBYTES) 16513390d476SMike Silbersack fraglen = MCLBYTES; 16523390d476SMike Silbersack 16533390d476SMike Silbersack if (m_new == NULL) { 16543390d476SMike Silbersack m_new = m_getcl(how, MT_DATA, 0); 16553390d476SMike Silbersack if (m_new == NULL) 16563390d476SMike Silbersack goto nospace; 16573390d476SMike Silbersack } 16583390d476SMike Silbersack 16593390d476SMike Silbersack m_copydata(m0, progress, fraglen, mtod(m_new, caddr_t)); 16603390d476SMike Silbersack progress += fraglen; 16613390d476SMike Silbersack m_new->m_len = fraglen; 16623390d476SMike Silbersack if (m_new != m_final) 16633390d476SMike Silbersack m_cat(m_final, m_new); 16643390d476SMike Silbersack m_new = NULL; 16653390d476SMike Silbersack } 16663390d476SMike Silbersack m_freem(m0); 16673390d476SMike Silbersack m0 = m_final; 16683390d476SMike Silbersack return (m0); 16693390d476SMike Silbersack nospace: 16703390d476SMike Silbersack if (m_final) 16713390d476SMike Silbersack m_freem(m_final); 16723390d476SMike Silbersack /* Return the original chain on failure */ 16733390d476SMike Silbersack return (m0); 16743390d476SMike Silbersack } 16753390d476SMike Silbersack 16763390d476SMike Silbersack #endif 1677beb699c7SMike Silbersack 16785e20f43dSAndre Oppermann /* 16795e20f43dSAndre Oppermann * Copy the contents of uio into a properly sized mbuf chain. 16805e20f43dSAndre Oppermann */ 1681beb699c7SMike Silbersack struct mbuf * 16825e20f43dSAndre Oppermann m_uiotombuf(struct uio *uio, int how, int len, int align, int flags) 1683beb699c7SMike Silbersack { 16845e20f43dSAndre Oppermann struct mbuf *m, *mb; 1685526d0bd5SKonstantin Belousov int error, length; 1686526d0bd5SKonstantin Belousov ssize_t total; 16875e20f43dSAndre Oppermann int progress = 0; 1688beb699c7SMike Silbersack 16895e20f43dSAndre Oppermann /* 16905e20f43dSAndre Oppermann * len can be zero or an arbitrary large value bound by 16915e20f43dSAndre Oppermann * the total data supplied by the uio. 16925e20f43dSAndre Oppermann */ 1693beb699c7SMike Silbersack if (len > 0) 1694beb699c7SMike Silbersack total = min(uio->uio_resid, len); 1695beb699c7SMike Silbersack else 1696beb699c7SMike Silbersack total = uio->uio_resid; 16975e20f43dSAndre Oppermann 16985e20f43dSAndre Oppermann /* 16995e20f43dSAndre Oppermann * The smallest unit returned by m_getm2() is a single mbuf 17009128ec21SAndrew Thompson * with pkthdr. We can't align past it. 17015e20f43dSAndre Oppermann */ 170275ae2570SMaksim Yevmenkin if (align >= MHLEN) 1703beb699c7SMike Silbersack return (NULL); 17045e20f43dSAndre Oppermann 17057c32173bSAndre Oppermann /* 17067c32173bSAndre Oppermann * Give us the full allocation or nothing. 17077c32173bSAndre Oppermann * If len is zero return the smallest empty mbuf. 17087c32173bSAndre Oppermann */ 17097c32173bSAndre Oppermann m = m_getm2(NULL, max(total + align, 1), how, MT_DATA, flags); 17105e20f43dSAndre Oppermann if (m == NULL) 17115e20f43dSAndre Oppermann return (NULL); 17125e20f43dSAndre Oppermann m->m_data += align; 17135e20f43dSAndre Oppermann 17145e20f43dSAndre Oppermann /* Fill all mbufs with uio data and update header information. */ 17155e20f43dSAndre Oppermann for (mb = m; mb != NULL; mb = mb->m_next) { 17165e20f43dSAndre Oppermann length = min(M_TRAILINGSPACE(mb), total - progress); 17175e20f43dSAndre Oppermann 17185e20f43dSAndre Oppermann error = uiomove(mtod(mb, void *), length, uio); 17195e20f43dSAndre Oppermann if (error) { 17205e20f43dSAndre Oppermann m_freem(m); 17215e20f43dSAndre Oppermann return (NULL); 17225e20f43dSAndre Oppermann } 17235e20f43dSAndre Oppermann 17245e20f43dSAndre Oppermann mb->m_len = length; 17255e20f43dSAndre Oppermann progress += length; 17265e20f43dSAndre Oppermann if (flags & M_PKTHDR) 17275e20f43dSAndre Oppermann m->m_pkthdr.len += length; 17285e20f43dSAndre Oppermann } 17295e20f43dSAndre Oppermann KASSERT(progress == total, ("%s: progress != total", __func__)); 17305e20f43dSAndre Oppermann 17315e20f43dSAndre Oppermann return (m); 1732beb699c7SMike Silbersack } 1733ab8ab90cSSam Leffler 1734ab8ab90cSSam Leffler /* 1735bc05b2f6SAndre Oppermann * Copy an mbuf chain into a uio limited by len if set. 1736bc05b2f6SAndre Oppermann */ 1737bc05b2f6SAndre Oppermann int 1738bc05b2f6SAndre Oppermann m_mbuftouio(struct uio *uio, struct mbuf *m, int len) 1739bc05b2f6SAndre Oppermann { 1740bc05b2f6SAndre Oppermann int error, length, total; 1741bc05b2f6SAndre Oppermann int progress = 0; 1742bc05b2f6SAndre Oppermann 1743bc05b2f6SAndre Oppermann if (len > 0) 1744bc05b2f6SAndre Oppermann total = min(uio->uio_resid, len); 1745bc05b2f6SAndre Oppermann else 1746bc05b2f6SAndre Oppermann total = uio->uio_resid; 1747bc05b2f6SAndre Oppermann 1748bc05b2f6SAndre Oppermann /* Fill the uio with data from the mbufs. */ 1749bc05b2f6SAndre Oppermann for (; m != NULL; m = m->m_next) { 1750bc05b2f6SAndre Oppermann length = min(m->m_len, total - progress); 1751bc05b2f6SAndre Oppermann 1752bc05b2f6SAndre Oppermann error = uiomove(mtod(m, void *), length, uio); 1753bc05b2f6SAndre Oppermann if (error) 1754bc05b2f6SAndre Oppermann return (error); 1755bc05b2f6SAndre Oppermann 1756bc05b2f6SAndre Oppermann progress += length; 1757bc05b2f6SAndre Oppermann } 1758bc05b2f6SAndre Oppermann 1759bc05b2f6SAndre Oppermann return (0); 1760bc05b2f6SAndre Oppermann } 1761bc05b2f6SAndre Oppermann 1762bc05b2f6SAndre Oppermann /* 176347e2996eSSam Leffler * Create a writable copy of the mbuf chain. While doing this 176447e2996eSSam Leffler * we compact the chain with a goal of producing a chain with 176547e2996eSSam Leffler * at most two mbufs. The second mbuf in this chain is likely 176647e2996eSSam Leffler * to be a cluster. The primary purpose of this work is to create 176747e2996eSSam Leffler * a writable packet for encryption, compression, etc. The 176847e2996eSSam Leffler * secondary goal is to linearize the data so the data can be 176947e2996eSSam Leffler * passed to crypto hardware in the most efficient manner possible. 177047e2996eSSam Leffler */ 177147e2996eSSam Leffler struct mbuf * 177247e2996eSSam Leffler m_unshare(struct mbuf *m0, int how) 177347e2996eSSam Leffler { 177447e2996eSSam Leffler struct mbuf *m, *mprev; 177547e2996eSSam Leffler struct mbuf *n, *mfirst, *mlast; 177647e2996eSSam Leffler int len, off; 177747e2996eSSam Leffler 177847e2996eSSam Leffler mprev = NULL; 177947e2996eSSam Leffler for (m = m0; m != NULL; m = mprev->m_next) { 178047e2996eSSam Leffler /* 178147e2996eSSam Leffler * Regular mbufs are ignored unless there's a cluster 178247e2996eSSam Leffler * in front of it that we can use to coalesce. We do 178347e2996eSSam Leffler * the latter mainly so later clusters can be coalesced 178447e2996eSSam Leffler * also w/o having to handle them specially (i.e. convert 178547e2996eSSam Leffler * mbuf+cluster -> cluster). This optimization is heavily 178647e2996eSSam Leffler * influenced by the assumption that we're running over 178747e2996eSSam Leffler * Ethernet where MCLBYTES is large enough that the max 178847e2996eSSam Leffler * packet size will permit lots of coalescing into a 178947e2996eSSam Leffler * single cluster. This in turn permits efficient 179047e2996eSSam Leffler * crypto operations, especially when using hardware. 179147e2996eSSam Leffler */ 179247e2996eSSam Leffler if ((m->m_flags & M_EXT) == 0) { 179347e2996eSSam Leffler if (mprev && (mprev->m_flags & M_EXT) && 179447e2996eSSam Leffler m->m_len <= M_TRAILINGSPACE(mprev)) { 179547e2996eSSam Leffler /* XXX: this ignores mbuf types */ 179647e2996eSSam Leffler memcpy(mtod(mprev, caddr_t) + mprev->m_len, 179747e2996eSSam Leffler mtod(m, caddr_t), m->m_len); 179847e2996eSSam Leffler mprev->m_len += m->m_len; 179947e2996eSSam Leffler mprev->m_next = m->m_next; /* unlink from chain */ 180047e2996eSSam Leffler m_free(m); /* reclaim mbuf */ 180147e2996eSSam Leffler #if 0 180247e2996eSSam Leffler newipsecstat.ips_mbcoalesced++; 180347e2996eSSam Leffler #endif 180447e2996eSSam Leffler } else { 180547e2996eSSam Leffler mprev = m; 180647e2996eSSam Leffler } 180747e2996eSSam Leffler continue; 180847e2996eSSam Leffler } 180947e2996eSSam Leffler /* 181047e2996eSSam Leffler * Writable mbufs are left alone (for now). 181147e2996eSSam Leffler */ 181247e2996eSSam Leffler if (M_WRITABLE(m)) { 181347e2996eSSam Leffler mprev = m; 181447e2996eSSam Leffler continue; 181547e2996eSSam Leffler } 181647e2996eSSam Leffler 181747e2996eSSam Leffler /* 181847e2996eSSam Leffler * Not writable, replace with a copy or coalesce with 181947e2996eSSam Leffler * the previous mbuf if possible (since we have to copy 182047e2996eSSam Leffler * it anyway, we try to reduce the number of mbufs and 182147e2996eSSam Leffler * clusters so that future work is easier). 182247e2996eSSam Leffler */ 182347e2996eSSam Leffler KASSERT(m->m_flags & M_EXT, ("m_flags 0x%x", m->m_flags)); 182447e2996eSSam Leffler /* NB: we only coalesce into a cluster or larger */ 182547e2996eSSam Leffler if (mprev != NULL && (mprev->m_flags & M_EXT) && 182647e2996eSSam Leffler m->m_len <= M_TRAILINGSPACE(mprev)) { 182747e2996eSSam Leffler /* XXX: this ignores mbuf types */ 182847e2996eSSam Leffler memcpy(mtod(mprev, caddr_t) + mprev->m_len, 182947e2996eSSam Leffler mtod(m, caddr_t), m->m_len); 183047e2996eSSam Leffler mprev->m_len += m->m_len; 183147e2996eSSam Leffler mprev->m_next = m->m_next; /* unlink from chain */ 183247e2996eSSam Leffler m_free(m); /* reclaim mbuf */ 183347e2996eSSam Leffler #if 0 183447e2996eSSam Leffler newipsecstat.ips_clcoalesced++; 183547e2996eSSam Leffler #endif 183647e2996eSSam Leffler continue; 183747e2996eSSam Leffler } 183847e2996eSSam Leffler 183947e2996eSSam Leffler /* 18405368b81eSGleb Smirnoff * Allocate new space to hold the copy and copy the data. 18415368b81eSGleb Smirnoff * We deal with jumbo mbufs (i.e. m_len > MCLBYTES) by 18425368b81eSGleb Smirnoff * splitting them into clusters. We could just malloc a 18435368b81eSGleb Smirnoff * buffer and make it external but too many device drivers 18445368b81eSGleb Smirnoff * don't know how to break up the non-contiguous memory when 18455368b81eSGleb Smirnoff * doing DMA. 184647e2996eSSam Leffler */ 184747e2996eSSam Leffler n = m_getcl(how, m->m_type, m->m_flags); 184847e2996eSSam Leffler if (n == NULL) { 184947e2996eSSam Leffler m_freem(m0); 185047e2996eSSam Leffler return (NULL); 185147e2996eSSam Leffler } 185247e2996eSSam Leffler len = m->m_len; 185347e2996eSSam Leffler off = 0; 185447e2996eSSam Leffler mfirst = n; 185547e2996eSSam Leffler mlast = NULL; 185647e2996eSSam Leffler for (;;) { 185747e2996eSSam Leffler int cc = min(len, MCLBYTES); 185847e2996eSSam Leffler memcpy(mtod(n, caddr_t), mtod(m, caddr_t) + off, cc); 185947e2996eSSam Leffler n->m_len = cc; 186047e2996eSSam Leffler if (mlast != NULL) 186147e2996eSSam Leffler mlast->m_next = n; 186247e2996eSSam Leffler mlast = n; 186347e2996eSSam Leffler #if 0 186447e2996eSSam Leffler newipsecstat.ips_clcopied++; 186547e2996eSSam Leffler #endif 186647e2996eSSam Leffler 186747e2996eSSam Leffler len -= cc; 186847e2996eSSam Leffler if (len <= 0) 186947e2996eSSam Leffler break; 187047e2996eSSam Leffler off += cc; 187147e2996eSSam Leffler 187247e2996eSSam Leffler n = m_getcl(how, m->m_type, m->m_flags); 187347e2996eSSam Leffler if (n == NULL) { 187447e2996eSSam Leffler m_freem(mfirst); 187547e2996eSSam Leffler m_freem(m0); 187647e2996eSSam Leffler return (NULL); 187747e2996eSSam Leffler } 187847e2996eSSam Leffler } 187947e2996eSSam Leffler n->m_next = m->m_next; 188047e2996eSSam Leffler if (mprev == NULL) 188147e2996eSSam Leffler m0 = mfirst; /* new head of chain */ 188247e2996eSSam Leffler else 188347e2996eSSam Leffler mprev->m_next = mfirst; /* replace old mbuf */ 188447e2996eSSam Leffler m_free(m); /* release old mbuf */ 188547e2996eSSam Leffler mprev = mfirst; 188647e2996eSSam Leffler } 188747e2996eSSam Leffler return (m0); 188847e2996eSSam Leffler } 18896eeac1d9SJulian Elischer 18906eeac1d9SJulian Elischer #ifdef MBUF_PROFILING 18916eeac1d9SJulian Elischer 18926eeac1d9SJulian Elischer #define MP_BUCKETS 32 /* don't just change this as things may overflow.*/ 18936eeac1d9SJulian Elischer struct mbufprofile { 18942182c0cfSJulian Elischer uintmax_t wasted[MP_BUCKETS]; 18952182c0cfSJulian Elischer uintmax_t used[MP_BUCKETS]; 18962182c0cfSJulian Elischer uintmax_t segments[MP_BUCKETS]; 18976eeac1d9SJulian Elischer } mbprof; 18986eeac1d9SJulian Elischer 18996eeac1d9SJulian Elischer #define MP_MAXDIGITS 21 /* strlen("16,000,000,000,000,000,000") == 21 */ 19006eeac1d9SJulian Elischer #define MP_NUMLINES 6 19016eeac1d9SJulian Elischer #define MP_NUMSPERLINE 16 19026eeac1d9SJulian Elischer #define MP_EXTRABYTES 64 /* > strlen("used:\nwasted:\nsegments:\n") */ 19036eeac1d9SJulian Elischer /* work out max space needed and add a bit of spare space too */ 19046eeac1d9SJulian Elischer #define MP_MAXLINE ((MP_MAXDIGITS+1) * MP_NUMSPERLINE) 19056eeac1d9SJulian Elischer #define MP_BUFSIZE ((MP_MAXLINE * MP_NUMLINES) + 1 + MP_EXTRABYTES) 19066eeac1d9SJulian Elischer 19076eeac1d9SJulian Elischer char mbprofbuf[MP_BUFSIZE]; 19086eeac1d9SJulian Elischer 19096eeac1d9SJulian Elischer void 19106eeac1d9SJulian Elischer m_profile(struct mbuf *m) 19116eeac1d9SJulian Elischer { 19126eeac1d9SJulian Elischer int segments = 0; 19136eeac1d9SJulian Elischer int used = 0; 19146eeac1d9SJulian Elischer int wasted = 0; 19156eeac1d9SJulian Elischer 19166eeac1d9SJulian Elischer while (m) { 19176eeac1d9SJulian Elischer segments++; 19186eeac1d9SJulian Elischer used += m->m_len; 19196eeac1d9SJulian Elischer if (m->m_flags & M_EXT) { 19206eeac1d9SJulian Elischer wasted += MHLEN - sizeof(m->m_ext) + 19216eeac1d9SJulian Elischer m->m_ext.ext_size - m->m_len; 19226eeac1d9SJulian Elischer } else { 19236eeac1d9SJulian Elischer if (m->m_flags & M_PKTHDR) 19246eeac1d9SJulian Elischer wasted += MHLEN - m->m_len; 19256eeac1d9SJulian Elischer else 19266eeac1d9SJulian Elischer wasted += MLEN - m->m_len; 19276eeac1d9SJulian Elischer } 19286eeac1d9SJulian Elischer m = m->m_next; 19296eeac1d9SJulian Elischer } 19306eeac1d9SJulian Elischer /* be paranoid.. it helps */ 19316eeac1d9SJulian Elischer if (segments > MP_BUCKETS - 1) 19326eeac1d9SJulian Elischer segments = MP_BUCKETS - 1; 19336eeac1d9SJulian Elischer if (used > 100000) 19346eeac1d9SJulian Elischer used = 100000; 19356eeac1d9SJulian Elischer if (wasted > 100000) 19366eeac1d9SJulian Elischer wasted = 100000; 19376eeac1d9SJulian Elischer /* store in the appropriate bucket */ 19386eeac1d9SJulian Elischer /* don't bother locking. if it's slightly off, so what? */ 19396eeac1d9SJulian Elischer mbprof.segments[segments]++; 19406eeac1d9SJulian Elischer mbprof.used[fls(used)]++; 19416eeac1d9SJulian Elischer mbprof.wasted[fls(wasted)]++; 19426eeac1d9SJulian Elischer } 19436eeac1d9SJulian Elischer 19446eeac1d9SJulian Elischer static void 19456eeac1d9SJulian Elischer mbprof_textify(void) 19466eeac1d9SJulian Elischer { 19476eeac1d9SJulian Elischer int offset; 19486eeac1d9SJulian Elischer char *c; 194960ae52f7SEd Schouten uint64_t *p; 19506eeac1d9SJulian Elischer 19516eeac1d9SJulian Elischer p = &mbprof.wasted[0]; 19526eeac1d9SJulian Elischer c = mbprofbuf; 19536eeac1d9SJulian Elischer offset = snprintf(c, MP_MAXLINE + 10, 19546eeac1d9SJulian Elischer "wasted:\n" 19552182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju " 19562182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju\n", 19576eeac1d9SJulian Elischer p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], 19586eeac1d9SJulian Elischer p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); 19596eeac1d9SJulian Elischer #ifdef BIG_ARRAY 19606eeac1d9SJulian Elischer p = &mbprof.wasted[16]; 19616eeac1d9SJulian Elischer c += offset; 19626eeac1d9SJulian Elischer offset = snprintf(c, MP_MAXLINE, 19632182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju " 19642182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju\n", 19656eeac1d9SJulian Elischer p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], 19666eeac1d9SJulian Elischer p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); 19676eeac1d9SJulian Elischer #endif 19686eeac1d9SJulian Elischer p = &mbprof.used[0]; 19696eeac1d9SJulian Elischer c += offset; 19706eeac1d9SJulian Elischer offset = snprintf(c, MP_MAXLINE + 10, 19716eeac1d9SJulian Elischer "used:\n" 19722182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju " 19732182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju\n", 19746eeac1d9SJulian Elischer p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], 19756eeac1d9SJulian Elischer p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); 19766eeac1d9SJulian Elischer #ifdef BIG_ARRAY 19776eeac1d9SJulian Elischer p = &mbprof.used[16]; 19786eeac1d9SJulian Elischer c += offset; 19796eeac1d9SJulian Elischer offset = snprintf(c, MP_MAXLINE, 19802182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju " 19812182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju\n", 19826eeac1d9SJulian Elischer p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], 19836eeac1d9SJulian Elischer p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); 19846eeac1d9SJulian Elischer #endif 19856eeac1d9SJulian Elischer p = &mbprof.segments[0]; 19866eeac1d9SJulian Elischer c += offset; 19876eeac1d9SJulian Elischer offset = snprintf(c, MP_MAXLINE + 10, 19886eeac1d9SJulian Elischer "segments:\n" 19892182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju " 19902182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju\n", 19916eeac1d9SJulian Elischer p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], 19926eeac1d9SJulian Elischer p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); 19936eeac1d9SJulian Elischer #ifdef BIG_ARRAY 19946eeac1d9SJulian Elischer p = &mbprof.segments[16]; 19956eeac1d9SJulian Elischer c += offset; 19966eeac1d9SJulian Elischer offset = snprintf(c, MP_MAXLINE, 19972182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %ju " 19982182c0cfSJulian Elischer "%ju %ju %ju %ju %ju %ju %ju %jju", 19996eeac1d9SJulian Elischer p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], 20006eeac1d9SJulian Elischer p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); 20016eeac1d9SJulian Elischer #endif 20026eeac1d9SJulian Elischer } 20036eeac1d9SJulian Elischer 20046eeac1d9SJulian Elischer static int 20056eeac1d9SJulian Elischer mbprof_handler(SYSCTL_HANDLER_ARGS) 20066eeac1d9SJulian Elischer { 20076eeac1d9SJulian Elischer int error; 20086eeac1d9SJulian Elischer 20096eeac1d9SJulian Elischer mbprof_textify(); 20106eeac1d9SJulian Elischer error = SYSCTL_OUT(req, mbprofbuf, strlen(mbprofbuf) + 1); 20116eeac1d9SJulian Elischer return (error); 20126eeac1d9SJulian Elischer } 20136eeac1d9SJulian Elischer 20146eeac1d9SJulian Elischer static int 20156eeac1d9SJulian Elischer mbprof_clr_handler(SYSCTL_HANDLER_ARGS) 20166eeac1d9SJulian Elischer { 20176eeac1d9SJulian Elischer int clear, error; 20186eeac1d9SJulian Elischer 20196eeac1d9SJulian Elischer clear = 0; 20206eeac1d9SJulian Elischer error = sysctl_handle_int(oidp, &clear, 0, req); 20216eeac1d9SJulian Elischer if (error || !req->newptr) 20226eeac1d9SJulian Elischer return (error); 20236eeac1d9SJulian Elischer 20246eeac1d9SJulian Elischer if (clear) { 20256eeac1d9SJulian Elischer bzero(&mbprof, sizeof(mbprof)); 20266eeac1d9SJulian Elischer } 20276eeac1d9SJulian Elischer 20286eeac1d9SJulian Elischer return (error); 20296eeac1d9SJulian Elischer } 20306eeac1d9SJulian Elischer 20316eeac1d9SJulian Elischer 20326eeac1d9SJulian Elischer SYSCTL_PROC(_kern_ipc, OID_AUTO, mbufprofile, CTLTYPE_STRING|CTLFLAG_RD, 20336eeac1d9SJulian Elischer NULL, 0, mbprof_handler, "A", "mbuf profiling statistics"); 20346eeac1d9SJulian Elischer 20356eeac1d9SJulian Elischer SYSCTL_PROC(_kern_ipc, OID_AUTO, mbufprofileclr, CTLTYPE_INT|CTLFLAG_RW, 20366eeac1d9SJulian Elischer NULL, 0, mbprof_clr_handler, "I", "clear mbuf profiling statistics"); 20376eeac1d9SJulian Elischer #endif 20386eeac1d9SJulian Elischer 2039