1099a0e58SBosko Milekic /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 38a36da99SPedro F. Giffuni * 48076cb52SBosko Milekic * Copyright (c) 2004, 2005, 58076cb52SBosko Milekic * Bosko Milekic <bmilekic@FreeBSD.org>. All rights reserved. 6099a0e58SBosko Milekic * 7099a0e58SBosko Milekic * Redistribution and use in source and binary forms, with or without 8099a0e58SBosko Milekic * modification, are permitted provided that the following conditions 9099a0e58SBosko Milekic * are met: 10099a0e58SBosko Milekic * 1. Redistributions of source code must retain the above copyright 11099a0e58SBosko Milekic * notice unmodified, this list of conditions and the following 12099a0e58SBosko Milekic * disclaimer. 13099a0e58SBosko Milekic * 2. Redistributions in binary form must reproduce the above copyright 14099a0e58SBosko Milekic * notice, this list of conditions and the following disclaimer in the 15099a0e58SBosko Milekic * documentation and/or other materials provided with the distribution. 16099a0e58SBosko Milekic * 17099a0e58SBosko Milekic * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18099a0e58SBosko Milekic * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19099a0e58SBosko Milekic * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20099a0e58SBosko Milekic * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21099a0e58SBosko Milekic * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22099a0e58SBosko Milekic * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23099a0e58SBosko Milekic * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24099a0e58SBosko Milekic * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25099a0e58SBosko Milekic * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26099a0e58SBosko Milekic * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27099a0e58SBosko Milekic * SUCH DAMAGE. 28099a0e58SBosko Milekic */ 29099a0e58SBosko Milekic 30099a0e58SBosko Milekic #include <sys/cdefs.h> 31099a0e58SBosko Milekic #include "opt_param.h" 32b2e60773SJohn Baldwin #include "opt_kern_tls.h" 33099a0e58SBosko Milekic 34099a0e58SBosko Milekic #include <sys/param.h> 353937ee75SConrad Meyer #include <sys/conf.h> 369978bd99SMark Johnston #include <sys/domainset.h> 37099a0e58SBosko Milekic #include <sys/malloc.h> 38099a0e58SBosko Milekic #include <sys/systm.h> 39099a0e58SBosko Milekic #include <sys/mbuf.h> 40099a0e58SBosko Milekic #include <sys/eventhandler.h> 41099a0e58SBosko Milekic #include <sys/kernel.h> 42b2e60773SJohn Baldwin #include <sys/ktls.h> 435475ca5aSMark Johnston #include <sys/limits.h> 4454503a13SJonathan T. Looney #include <sys/lock.h> 4554503a13SJonathan T. Looney #include <sys/mutex.h> 46b2e60773SJohn Baldwin #include <sys/refcount.h> 4782334850SJohn Baldwin #include <sys/sf_buf.h> 48099a0e58SBosko Milekic #include <sys/smp.h> 49fb3bc596SJohn Baldwin #include <sys/socket.h> 50099a0e58SBosko Milekic #include <sys/sysctl.h> 51099a0e58SBosko Milekic 52fb3bc596SJohn Baldwin #include <net/if.h> 53fb3bc596SJohn Baldwin #include <net/if_var.h> 54fb3bc596SJohn Baldwin 55099a0e58SBosko Milekic #include <vm/vm.h> 56c45c0034SAlan Cox #include <vm/vm_extern.h> 57c45c0034SAlan Cox #include <vm/vm_kern.h> 58099a0e58SBosko Milekic #include <vm/vm_page.h> 5984d746deSRick Macklem #include <vm/vm_pageout.h> 6037140716SAndre Oppermann #include <vm/vm_map.h> 61099a0e58SBosko Milekic #include <vm/uma.h> 62121f0509SMike Silbersack #include <vm/uma_dbg.h> 63099a0e58SBosko Milekic 64840327e5SBrooks Davis _Static_assert(MJUMPAGESIZE > MCLBYTES, 65840327e5SBrooks Davis "Cluster must be smaller than a jumbo page"); 66840327e5SBrooks Davis 67099a0e58SBosko Milekic /* 68099a0e58SBosko Milekic * In FreeBSD, Mbufs and Mbuf Clusters are allocated from UMA 69099a0e58SBosko Milekic * Zones. 70099a0e58SBosko Milekic * 71099a0e58SBosko Milekic * Mbuf Clusters (2K, contiguous) are allocated from the Cluster 72099a0e58SBosko Milekic * Zone. The Zone can be capped at kern.ipc.nmbclusters, if the 73099a0e58SBosko Milekic * administrator so desires. 74099a0e58SBosko Milekic * 7503270b59SJeff Roberson * Mbufs are allocated from a UMA Primary Zone called the Mbuf 76099a0e58SBosko Milekic * Zone. 77099a0e58SBosko Milekic * 78099a0e58SBosko Milekic * Additionally, FreeBSD provides a Packet Zone, which it 7903270b59SJeff Roberson * configures as a Secondary Zone to the Mbuf Primary Zone, 8003270b59SJeff Roberson * thus sharing backend Slab kegs with the Mbuf Primary Zone. 81099a0e58SBosko Milekic * 82099a0e58SBosko Milekic * Thus common-case allocations and locking are simplified: 83099a0e58SBosko Milekic * 84099a0e58SBosko Milekic * m_clget() m_getcl() 85099a0e58SBosko Milekic * | | 86099a0e58SBosko Milekic * | .------------>[(Packet Cache)] m_get(), m_gethdr() 87099a0e58SBosko Milekic * | | [ Packet ] | 88099a0e58SBosko Milekic * [(Cluster Cache)] [ Secondary ] [ (Mbuf Cache) ] 8903270b59SJeff Roberson * [ Cluster Zone ] [ Zone ] [ Mbuf Primary Zone ] 90099a0e58SBosko Milekic * | \________ | 91099a0e58SBosko Milekic * [ Cluster Keg ] \ / 92099a0e58SBosko Milekic * | [ Mbuf Keg ] 93099a0e58SBosko Milekic * [ Cluster Slabs ] | 94099a0e58SBosko Milekic * | [ Mbuf Slabs ] 95099a0e58SBosko Milekic * \____________(VM)_________________/ 9656a4e45aSAndre Oppermann * 9756a4e45aSAndre Oppermann * 98fcf90618SGleb Smirnoff * Whenever an object is allocated with uma_zalloc() out of 9956a4e45aSAndre Oppermann * one of the Zones its _ctor_ function is executed. The same 100fcf90618SGleb Smirnoff * for any deallocation through uma_zfree() the _dtor_ function 10156a4e45aSAndre Oppermann * is executed. 10256a4e45aSAndre Oppermann * 10303270b59SJeff Roberson * Caches are per-CPU and are filled from the Primary Zone. 10456a4e45aSAndre Oppermann * 105fcf90618SGleb Smirnoff * Whenever an object is allocated from the underlying global 10656a4e45aSAndre Oppermann * memory pool it gets pre-initialized with the _zinit_ functions. 107e3043798SPedro F. Giffuni * When the Keg's are overfull objects get decommissioned with 10856a4e45aSAndre Oppermann * _zfini_ functions and free'd back to the global memory pool. 10956a4e45aSAndre Oppermann * 110099a0e58SBosko Milekic */ 111099a0e58SBosko Milekic 112ead46972SAndre Oppermann int nmbufs; /* limits number of mbufs */ 11356a4e45aSAndre Oppermann int nmbclusters; /* limits number of mbuf clusters */ 114ec63cb90SAndre Oppermann int nmbjumbop; /* limits number of page size jumbo clusters */ 11556a4e45aSAndre Oppermann int nmbjumbo9; /* limits number of 9k jumbo clusters */ 11656a4e45aSAndre Oppermann int nmbjumbo16; /* limits number of 16k jumbo clusters */ 117099a0e58SBosko Milekic 118fcaa890cSMark Johnston bool mb_use_ext_pgs = false; /* use M_EXTPG mbufs for sendfile & TLS */ 119fcaa890cSMark Johnston 120fcaa890cSMark Johnston static int 121fcaa890cSMark Johnston sysctl_mb_use_ext_pgs(SYSCTL_HANDLER_ARGS) 122fcaa890cSMark Johnston { 123fcaa890cSMark Johnston int error, extpg; 124fcaa890cSMark Johnston 125fcaa890cSMark Johnston extpg = mb_use_ext_pgs; 126fcaa890cSMark Johnston error = sysctl_handle_int(oidp, &extpg, 0, req); 127fcaa890cSMark Johnston if (error == 0 && req->newptr != NULL) { 128fcaa890cSMark Johnston if (extpg != 0 && !PMAP_HAS_DMAP) 129fcaa890cSMark Johnston error = EOPNOTSUPP; 130fcaa890cSMark Johnston else 131fcaa890cSMark Johnston mb_use_ext_pgs = extpg != 0; 132fcaa890cSMark Johnston } 133fcaa890cSMark Johnston return (error); 134fcaa890cSMark Johnston } 1356a88498eSZhenlei Huang SYSCTL_PROC(_kern_ipc, OID_AUTO, mb_use_ext_pgs, 1366a88498eSZhenlei Huang CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH, 1376a88498eSZhenlei Huang &mb_use_ext_pgs, 0, sysctl_mb_use_ext_pgs, "IU", 138b2e60773SJohn Baldwin "Use unmapped mbufs for sendfile(2) and TLS offload"); 139cec06a3eSJohn Baldwin 140e0c00addSAndre Oppermann static quad_t maxmbufmem; /* overall real memory limit for all mbufs */ 141e0c00addSAndre Oppermann 142af3b2549SHans Petter Selasky SYSCTL_QUAD(_kern_ipc, OID_AUTO, maxmbufmem, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, &maxmbufmem, 0, 143b6f49c23SHiren Panchasara "Maximum real memory allocatable to various mbuf types"); 144e0c00addSAndre Oppermann 145fb3bc596SJohn Baldwin static counter_u64_t snd_tag_count; 146fb3bc596SJohn Baldwin SYSCTL_COUNTER_U64(_kern_ipc, OID_AUTO, num_snd_tags, CTLFLAG_RW, 147fb3bc596SJohn Baldwin &snd_tag_count, "# of active mbuf send tags"); 148fb3bc596SJohn Baldwin 14962938659SBjoern A. Zeeb /* 15037140716SAndre Oppermann * tunable_mbinit() has to be run before any mbuf allocations are done. 15162938659SBjoern A. Zeeb */ 152099a0e58SBosko Milekic static void 153099a0e58SBosko Milekic tunable_mbinit(void *dummy) 154099a0e58SBosko Milekic { 155e0c00addSAndre Oppermann quad_t realmem; 156fcaa890cSMark Johnston int extpg; 15737140716SAndre Oppermann 15837140716SAndre Oppermann /* 15937140716SAndre Oppermann * The default limit for all mbuf related memory is 1/2 of all 16037140716SAndre Oppermann * available kernel memory (physical or kmem). 16137140716SAndre Oppermann * At most it can be 3/4 of available kernel memory. 16237140716SAndre Oppermann */ 1635df87b21SJeff Roberson realmem = qmin((quad_t)physmem * PAGE_SIZE, vm_kmem_size); 16437140716SAndre Oppermann maxmbufmem = realmem / 2; 165e0c00addSAndre Oppermann TUNABLE_QUAD_FETCH("kern.ipc.maxmbufmem", &maxmbufmem); 16637140716SAndre Oppermann if (maxmbufmem > realmem / 4 * 3) 16737140716SAndre Oppermann maxmbufmem = realmem / 4 * 3; 168099a0e58SBosko Milekic 169812302c3SNavdeep Parhar TUNABLE_INT_FETCH("kern.ipc.nmbclusters", &nmbclusters); 170416a434cSAndre Oppermann if (nmbclusters == 0) 171416a434cSAndre Oppermann nmbclusters = maxmbufmem / MCLBYTES / 4; 172812302c3SNavdeep Parhar 173812302c3SNavdeep Parhar TUNABLE_INT_FETCH("kern.ipc.nmbjumbop", &nmbjumbop); 174812302c3SNavdeep Parhar if (nmbjumbop == 0) 175416a434cSAndre Oppermann nmbjumbop = maxmbufmem / MJUMPAGESIZE / 4; 176812302c3SNavdeep Parhar 177812302c3SNavdeep Parhar TUNABLE_INT_FETCH("kern.ipc.nmbjumbo9", &nmbjumbo9); 178812302c3SNavdeep Parhar if (nmbjumbo9 == 0) 179416a434cSAndre Oppermann nmbjumbo9 = maxmbufmem / MJUM9BYTES / 6; 180812302c3SNavdeep Parhar 181812302c3SNavdeep Parhar TUNABLE_INT_FETCH("kern.ipc.nmbjumbo16", &nmbjumbo16); 182812302c3SNavdeep Parhar if (nmbjumbo16 == 0) 183416a434cSAndre Oppermann nmbjumbo16 = maxmbufmem / MJUM16BYTES / 6; 184416a434cSAndre Oppermann 185416a434cSAndre Oppermann /* 186416a434cSAndre Oppermann * We need at least as many mbufs as we have clusters of 187416a434cSAndre Oppermann * the various types added together. 188416a434cSAndre Oppermann */ 189416a434cSAndre Oppermann TUNABLE_INT_FETCH("kern.ipc.nmbufs", &nmbufs); 190416a434cSAndre Oppermann if (nmbufs < nmbclusters + nmbjumbop + nmbjumbo9 + nmbjumbo16) 191416a434cSAndre Oppermann nmbufs = lmax(maxmbufmem / MSIZE / 5, 192416a434cSAndre Oppermann nmbclusters + nmbjumbop + nmbjumbo9 + nmbjumbo16); 193fcaa890cSMark Johnston 194fcaa890cSMark Johnston /* 195fcaa890cSMark Johnston * Unmapped mbufs can only safely be used on platforms with a direct 196fcaa890cSMark Johnston * map. 197fcaa890cSMark Johnston */ 198fcaa890cSMark Johnston if (PMAP_HAS_DMAP) { 19932854e52SMark Johnston extpg = 1; 200fcaa890cSMark Johnston TUNABLE_INT_FETCH("kern.ipc.mb_use_ext_pgs", &extpg); 201fcaa890cSMark Johnston mb_use_ext_pgs = extpg != 0; 202fcaa890cSMark Johnston } 203099a0e58SBosko Milekic } 20437140716SAndre Oppermann SYSINIT(tunable_mbinit, SI_SUB_KMEM, SI_ORDER_MIDDLE, tunable_mbinit, NULL); 205099a0e58SBosko Milekic 2064f590175SPaul Saab static int 2074f590175SPaul Saab sysctl_nmbclusters(SYSCTL_HANDLER_ARGS) 2084f590175SPaul Saab { 2094f590175SPaul Saab int error, newnmbclusters; 2104f590175SPaul Saab 2114f590175SPaul Saab newnmbclusters = nmbclusters; 212041b706bSDavid Malone error = sysctl_handle_int(oidp, &newnmbclusters, 0, req); 213d251e700SJohn Baldwin if (error == 0 && req->newptr && newnmbclusters != nmbclusters) { 214ead46972SAndre Oppermann if (newnmbclusters > nmbclusters && 215ead46972SAndre Oppermann nmbufs >= nmbclusters + nmbjumbop + nmbjumbo9 + nmbjumbo16) { 2164f590175SPaul Saab nmbclusters = newnmbclusters; 217bc4a1b8cSAndre Oppermann nmbclusters = uma_zone_set_max(zone_clust, nmbclusters); 2184f590175SPaul Saab EVENTHANDLER_INVOKE(nmbclusters_change); 2194f590175SPaul Saab } else 2204f590175SPaul Saab error = EINVAL; 2214f590175SPaul Saab } 2224f590175SPaul Saab return (error); 2234f590175SPaul Saab } 2247029da5cSPawel Biernacki SYSCTL_PROC(_kern_ipc, OID_AUTO, nmbclusters, 2256a88498eSZhenlei Huang CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE, 2266a88498eSZhenlei Huang &nmbclusters, 0, sysctl_nmbclusters, "IU", 227099a0e58SBosko Milekic "Maximum number of mbuf clusters allowed"); 228cf70a46bSRandall Stewart 229cf70a46bSRandall Stewart static int 230cf70a46bSRandall Stewart sysctl_nmbjumbop(SYSCTL_HANDLER_ARGS) 231cf70a46bSRandall Stewart { 232cf70a46bSRandall Stewart int error, newnmbjumbop; 233cf70a46bSRandall Stewart 234cf70a46bSRandall Stewart newnmbjumbop = nmbjumbop; 235cf70a46bSRandall Stewart error = sysctl_handle_int(oidp, &newnmbjumbop, 0, req); 236d251e700SJohn Baldwin if (error == 0 && req->newptr && newnmbjumbop != nmbjumbop) { 237ead46972SAndre Oppermann if (newnmbjumbop > nmbjumbop && 238ead46972SAndre Oppermann nmbufs >= nmbclusters + nmbjumbop + nmbjumbo9 + nmbjumbo16) { 239cf70a46bSRandall Stewart nmbjumbop = newnmbjumbop; 240bc4a1b8cSAndre Oppermann nmbjumbop = uma_zone_set_max(zone_jumbop, nmbjumbop); 241cf70a46bSRandall Stewart } else 242cf70a46bSRandall Stewart error = EINVAL; 243cf70a46bSRandall Stewart } 244cf70a46bSRandall Stewart return (error); 245cf70a46bSRandall Stewart } 2467029da5cSPawel Biernacki SYSCTL_PROC(_kern_ipc, OID_AUTO, nmbjumbop, 2476a88498eSZhenlei Huang CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE, 2486a88498eSZhenlei Huang &nmbjumbop, 0, sysctl_nmbjumbop, "IU", 249ec63cb90SAndre Oppermann "Maximum number of mbuf page size jumbo clusters allowed"); 250cf70a46bSRandall Stewart 251cf70a46bSRandall Stewart static int 252cf70a46bSRandall Stewart sysctl_nmbjumbo9(SYSCTL_HANDLER_ARGS) 253cf70a46bSRandall Stewart { 254cf70a46bSRandall Stewart int error, newnmbjumbo9; 255cf70a46bSRandall Stewart 256cf70a46bSRandall Stewart newnmbjumbo9 = nmbjumbo9; 257cf70a46bSRandall Stewart error = sysctl_handle_int(oidp, &newnmbjumbo9, 0, req); 258d251e700SJohn Baldwin if (error == 0 && req->newptr && newnmbjumbo9 != nmbjumbo9) { 259ead46972SAndre Oppermann if (newnmbjumbo9 > nmbjumbo9 && 260ead46972SAndre Oppermann nmbufs >= nmbclusters + nmbjumbop + nmbjumbo9 + nmbjumbo16) { 261cf70a46bSRandall Stewart nmbjumbo9 = newnmbjumbo9; 262bc4a1b8cSAndre Oppermann nmbjumbo9 = uma_zone_set_max(zone_jumbo9, nmbjumbo9); 263cf70a46bSRandall Stewart } else 264cf70a46bSRandall Stewart error = EINVAL; 265cf70a46bSRandall Stewart } 266cf70a46bSRandall Stewart return (error); 267cf70a46bSRandall Stewart } 2687029da5cSPawel Biernacki SYSCTL_PROC(_kern_ipc, OID_AUTO, nmbjumbo9, 2696a88498eSZhenlei Huang CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE, 2706a88498eSZhenlei Huang &nmbjumbo9, 0, sysctl_nmbjumbo9, "IU", 27156a4e45aSAndre Oppermann "Maximum number of mbuf 9k jumbo clusters allowed"); 272cf70a46bSRandall Stewart 273cf70a46bSRandall Stewart static int 274cf70a46bSRandall Stewart sysctl_nmbjumbo16(SYSCTL_HANDLER_ARGS) 275cf70a46bSRandall Stewart { 276cf70a46bSRandall Stewart int error, newnmbjumbo16; 277cf70a46bSRandall Stewart 278cf70a46bSRandall Stewart newnmbjumbo16 = nmbjumbo16; 279cf70a46bSRandall Stewart error = sysctl_handle_int(oidp, &newnmbjumbo16, 0, req); 280d251e700SJohn Baldwin if (error == 0 && req->newptr && newnmbjumbo16 != nmbjumbo16) { 281ead46972SAndre Oppermann if (newnmbjumbo16 > nmbjumbo16 && 282ead46972SAndre Oppermann nmbufs >= nmbclusters + nmbjumbop + nmbjumbo9 + nmbjumbo16) { 283cf70a46bSRandall Stewart nmbjumbo16 = newnmbjumbo16; 284bc4a1b8cSAndre Oppermann nmbjumbo16 = uma_zone_set_max(zone_jumbo16, nmbjumbo16); 285cf70a46bSRandall Stewart } else 286cf70a46bSRandall Stewart error = EINVAL; 287cf70a46bSRandall Stewart } 288cf70a46bSRandall Stewart return (error); 289cf70a46bSRandall Stewart } 2907029da5cSPawel Biernacki SYSCTL_PROC(_kern_ipc, OID_AUTO, nmbjumbo16, 2916a88498eSZhenlei Huang CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE, 2926a88498eSZhenlei Huang &nmbjumbo16, 0, sysctl_nmbjumbo16, "IU", 29356a4e45aSAndre Oppermann "Maximum number of mbuf 16k jumbo clusters allowed"); 294cf70a46bSRandall Stewart 295ead46972SAndre Oppermann static int 296ead46972SAndre Oppermann sysctl_nmbufs(SYSCTL_HANDLER_ARGS) 297ead46972SAndre Oppermann { 298ead46972SAndre Oppermann int error, newnmbufs; 299ead46972SAndre Oppermann 300ead46972SAndre Oppermann newnmbufs = nmbufs; 301ead46972SAndre Oppermann error = sysctl_handle_int(oidp, &newnmbufs, 0, req); 302d251e700SJohn Baldwin if (error == 0 && req->newptr && newnmbufs != nmbufs) { 303ead46972SAndre Oppermann if (newnmbufs > nmbufs) { 304ead46972SAndre Oppermann nmbufs = newnmbufs; 305bc4a1b8cSAndre Oppermann nmbufs = uma_zone_set_max(zone_mbuf, nmbufs); 306ead46972SAndre Oppermann EVENTHANDLER_INVOKE(nmbufs_change); 307ead46972SAndre Oppermann } else 308ead46972SAndre Oppermann error = EINVAL; 309ead46972SAndre Oppermann } 310ead46972SAndre Oppermann return (error); 311ead46972SAndre Oppermann } 3127029da5cSPawel Biernacki SYSCTL_PROC(_kern_ipc, OID_AUTO, nmbufs, 3136a88498eSZhenlei Huang CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE, 314ead46972SAndre Oppermann &nmbufs, 0, sysctl_nmbufs, "IU", 315ead46972SAndre Oppermann "Maximum number of mbufs allowed"); 316cf70a46bSRandall Stewart 317099a0e58SBosko Milekic /* 318099a0e58SBosko Milekic * Zones from which we allocate. 319099a0e58SBosko Milekic */ 320099a0e58SBosko Milekic uma_zone_t zone_mbuf; 321099a0e58SBosko Milekic uma_zone_t zone_clust; 322099a0e58SBosko Milekic uma_zone_t zone_pack; 323ec63cb90SAndre Oppermann uma_zone_t zone_jumbop; 32456a4e45aSAndre Oppermann uma_zone_t zone_jumbo9; 32556a4e45aSAndre Oppermann uma_zone_t zone_jumbo16; 326099a0e58SBosko Milekic 327099a0e58SBosko Milekic /* 328099a0e58SBosko Milekic * Local prototypes. 329099a0e58SBosko Milekic */ 330b23f72e9SBrian Feldman static int mb_ctor_mbuf(void *, int, void *, int); 331b23f72e9SBrian Feldman static int mb_ctor_clust(void *, int, void *, int); 332b23f72e9SBrian Feldman static int mb_ctor_pack(void *, int, void *, int); 333099a0e58SBosko Milekic static void mb_dtor_mbuf(void *, int, void *); 33456a4e45aSAndre Oppermann static void mb_dtor_pack(void *, int, void *); 33556a4e45aSAndre Oppermann static int mb_zinit_pack(void *, int, int); 33656a4e45aSAndre Oppermann static void mb_zfini_pack(void *, int); 337e60b2fcbSGleb Smirnoff static void mb_reclaim(uma_zone_t, int); 338099a0e58SBosko Milekic 33937140716SAndre Oppermann /* Ensure that MSIZE is a power of 2. */ 340a04946cfSBrian Somers CTASSERT((((MSIZE - 1) ^ MSIZE) + 1) >> 1 == MSIZE); 341a04946cfSBrian Somers 34223feb563SAndrew Gallatin _Static_assert(sizeof(struct mbuf) <= MSIZE, 34323feb563SAndrew Gallatin "size of mbuf exceeds MSIZE"); 344099a0e58SBosko Milekic /* 345099a0e58SBosko Milekic * Initialize FreeBSD Network buffer allocation. 346099a0e58SBosko Milekic */ 347099a0e58SBosko Milekic static void 348099a0e58SBosko Milekic mbuf_init(void *dummy) 349099a0e58SBosko Milekic { 350099a0e58SBosko Milekic 351099a0e58SBosko Milekic /* 352099a0e58SBosko Milekic * Configure UMA zones for Mbufs, Clusters, and Packets. 353099a0e58SBosko Milekic */ 35456a4e45aSAndre Oppermann zone_mbuf = uma_zcreate(MBUF_MEM_NAME, MSIZE, 35530be9685SRyan Libby mb_ctor_mbuf, mb_dtor_mbuf, NULL, NULL, 35610c8fb47SRyan Libby MSIZE - 1, UMA_ZONE_CONTIG | UMA_ZONE_MAXBUCKET); 35745fe0bf7SPawel Jakub Dawidek if (nmbufs > 0) 35845fe0bf7SPawel Jakub Dawidek nmbufs = uma_zone_set_max(zone_mbuf, nmbufs); 3596e0b6746SPawel Jakub Dawidek uma_zone_set_warning(zone_mbuf, "kern.ipc.nmbufs limit reached"); 360e60b2fcbSGleb Smirnoff uma_zone_set_maxaction(zone_mbuf, mb_reclaim); 36156a4e45aSAndre Oppermann 36268352adfSRobert Watson zone_clust = uma_zcreate(MBUF_CLUSTER_MEM_NAME, MCLBYTES, 36330be9685SRyan Libby mb_ctor_clust, NULL, NULL, NULL, 36410c8fb47SRyan Libby UMA_ALIGN_PTR, UMA_ZONE_CONTIG); 36545fe0bf7SPawel Jakub Dawidek if (nmbclusters > 0) 36645fe0bf7SPawel Jakub Dawidek nmbclusters = uma_zone_set_max(zone_clust, nmbclusters); 3676e0b6746SPawel Jakub Dawidek uma_zone_set_warning(zone_clust, "kern.ipc.nmbclusters limit reached"); 368e60b2fcbSGleb Smirnoff uma_zone_set_maxaction(zone_clust, mb_reclaim); 369099a0e58SBosko Milekic 37056a4e45aSAndre Oppermann zone_pack = uma_zsecond_create(MBUF_PACKET_MEM_NAME, mb_ctor_pack, 37156a4e45aSAndre Oppermann mb_dtor_pack, mb_zinit_pack, mb_zfini_pack, zone_mbuf); 37256a4e45aSAndre Oppermann 373fcf90618SGleb Smirnoff /* Make jumbo frame zone too. Page size, 9k and 16k. */ 374ec63cb90SAndre Oppermann zone_jumbop = uma_zcreate(MBUF_JUMBOP_MEM_NAME, MJUMPAGESIZE, 37530be9685SRyan Libby mb_ctor_clust, NULL, NULL, NULL, 37610c8fb47SRyan Libby UMA_ALIGN_PTR, UMA_ZONE_CONTIG); 37745fe0bf7SPawel Jakub Dawidek if (nmbjumbop > 0) 37845fe0bf7SPawel Jakub Dawidek nmbjumbop = uma_zone_set_max(zone_jumbop, nmbjumbop); 3796e0b6746SPawel Jakub Dawidek uma_zone_set_warning(zone_jumbop, "kern.ipc.nmbjumbop limit reached"); 380e60b2fcbSGleb Smirnoff uma_zone_set_maxaction(zone_jumbop, mb_reclaim); 381d5269a63SAndre Oppermann 38256a4e45aSAndre Oppermann zone_jumbo9 = uma_zcreate(MBUF_JUMBO9_MEM_NAME, MJUM9BYTES, 38330be9685SRyan Libby mb_ctor_clust, NULL, NULL, NULL, 38410c8fb47SRyan Libby UMA_ALIGN_PTR, UMA_ZONE_CONTIG); 38545fe0bf7SPawel Jakub Dawidek if (nmbjumbo9 > 0) 38645fe0bf7SPawel Jakub Dawidek nmbjumbo9 = uma_zone_set_max(zone_jumbo9, nmbjumbo9); 3876e0b6746SPawel Jakub Dawidek uma_zone_set_warning(zone_jumbo9, "kern.ipc.nmbjumbo9 limit reached"); 388e60b2fcbSGleb Smirnoff uma_zone_set_maxaction(zone_jumbo9, mb_reclaim); 38956a4e45aSAndre Oppermann 39056a4e45aSAndre Oppermann zone_jumbo16 = uma_zcreate(MBUF_JUMBO16_MEM_NAME, MJUM16BYTES, 39130be9685SRyan Libby mb_ctor_clust, NULL, NULL, NULL, 39210c8fb47SRyan Libby UMA_ALIGN_PTR, UMA_ZONE_CONTIG); 39345fe0bf7SPawel Jakub Dawidek if (nmbjumbo16 > 0) 39445fe0bf7SPawel Jakub Dawidek nmbjumbo16 = uma_zone_set_max(zone_jumbo16, nmbjumbo16); 3956e0b6746SPawel Jakub Dawidek uma_zone_set_warning(zone_jumbo16, "kern.ipc.nmbjumbo16 limit reached"); 396e60b2fcbSGleb Smirnoff uma_zone_set_maxaction(zone_jumbo16, mb_reclaim); 39756a4e45aSAndre Oppermann 398fb3bc596SJohn Baldwin snd_tag_count = counter_u64_alloc(M_WAITOK); 399099a0e58SBosko Milekic } 40037140716SAndre Oppermann SYSINIT(mbuf, SI_SUB_MBUF, SI_ORDER_FIRST, mbuf_init, NULL); 401099a0e58SBosko Milekic 4027790c8c1SConrad Meyer #ifdef DEBUGNET 4035475ca5aSMark Johnston /* 4047790c8c1SConrad Meyer * debugnet makes use of a pre-allocated pool of mbufs and clusters. When 4057790c8c1SConrad Meyer * debugnet is configured, we initialize a set of UMA cache zones which return 4065475ca5aSMark Johnston * items from this pool. At panic-time, the regular UMA zone pointers are 4075475ca5aSMark Johnston * overwritten with those of the cache zones so that drivers may allocate and 4085475ca5aSMark Johnston * free mbufs and clusters without attempting to allocate physical memory. 4095475ca5aSMark Johnston * 4105475ca5aSMark Johnston * We keep mbufs and clusters in a pair of mbuf queues. In particular, for 4115475ca5aSMark Johnston * the purpose of caching clusters, we treat them as mbufs. 4125475ca5aSMark Johnston */ 4137790c8c1SConrad Meyer static struct mbufq dn_mbufq = 4147790c8c1SConrad Meyer { STAILQ_HEAD_INITIALIZER(dn_mbufq.mq_head), 0, INT_MAX }; 4157790c8c1SConrad Meyer static struct mbufq dn_clustq = 4167790c8c1SConrad Meyer { STAILQ_HEAD_INITIALIZER(dn_clustq.mq_head), 0, INT_MAX }; 4175475ca5aSMark Johnston 4187790c8c1SConrad Meyer static int dn_clsize; 4197790c8c1SConrad Meyer static uma_zone_t dn_zone_mbuf; 4207790c8c1SConrad Meyer static uma_zone_t dn_zone_clust; 4217790c8c1SConrad Meyer static uma_zone_t dn_zone_pack; 4227790c8c1SConrad Meyer 4237790c8c1SConrad Meyer static struct debugnet_saved_zones { 4247790c8c1SConrad Meyer uma_zone_t dsz_mbuf; 4257790c8c1SConrad Meyer uma_zone_t dsz_clust; 4267790c8c1SConrad Meyer uma_zone_t dsz_pack; 4277790c8c1SConrad Meyer uma_zone_t dsz_jumbop; 4287790c8c1SConrad Meyer uma_zone_t dsz_jumbo9; 4297790c8c1SConrad Meyer uma_zone_t dsz_jumbo16; 4307790c8c1SConrad Meyer bool dsz_debugnet_zones_enabled; 4317790c8c1SConrad Meyer } dn_saved_zones; 4325475ca5aSMark Johnston 4335475ca5aSMark Johnston static int 4347790c8c1SConrad Meyer dn_buf_import(void *arg, void **store, int count, int domain __unused, 4355475ca5aSMark Johnston int flags) 4365475ca5aSMark Johnston { 4375475ca5aSMark Johnston struct mbufq *q; 4385475ca5aSMark Johnston struct mbuf *m; 4395475ca5aSMark Johnston int i; 4405475ca5aSMark Johnston 4415475ca5aSMark Johnston q = arg; 4425475ca5aSMark Johnston 4435475ca5aSMark Johnston for (i = 0; i < count; i++) { 4445475ca5aSMark Johnston m = mbufq_dequeue(q); 4455475ca5aSMark Johnston if (m == NULL) 4465475ca5aSMark Johnston break; 4477790c8c1SConrad Meyer trash_init(m, q == &dn_mbufq ? MSIZE : dn_clsize, flags); 4485475ca5aSMark Johnston store[i] = m; 4495475ca5aSMark Johnston } 4500d1467b1SConrad Meyer KASSERT((flags & M_WAITOK) == 0 || i == count, 4510d1467b1SConrad Meyer ("%s: ran out of pre-allocated mbufs", __func__)); 4525475ca5aSMark Johnston return (i); 4535475ca5aSMark Johnston } 4545475ca5aSMark Johnston 4555475ca5aSMark Johnston static void 4567790c8c1SConrad Meyer dn_buf_release(void *arg, void **store, int count) 4575475ca5aSMark Johnston { 4585475ca5aSMark Johnston struct mbufq *q; 4595475ca5aSMark Johnston struct mbuf *m; 4605475ca5aSMark Johnston int i; 4615475ca5aSMark Johnston 4625475ca5aSMark Johnston q = arg; 4635475ca5aSMark Johnston 4645475ca5aSMark Johnston for (i = 0; i < count; i++) { 4655475ca5aSMark Johnston m = store[i]; 4665475ca5aSMark Johnston (void)mbufq_enqueue(q, m); 4675475ca5aSMark Johnston } 4685475ca5aSMark Johnston } 4695475ca5aSMark Johnston 4705475ca5aSMark Johnston static int 4717790c8c1SConrad Meyer dn_pack_import(void *arg __unused, void **store, int count, int domain __unused, 4725475ca5aSMark Johnston int flags __unused) 4735475ca5aSMark Johnston { 4745475ca5aSMark Johnston struct mbuf *m; 4755475ca5aSMark Johnston void *clust; 4765475ca5aSMark Johnston int i; 4775475ca5aSMark Johnston 4785475ca5aSMark Johnston for (i = 0; i < count; i++) { 479440217b0SZhenlei Huang m = m_get(M_NOWAIT, MT_DATA); 4805475ca5aSMark Johnston if (m == NULL) 4815475ca5aSMark Johnston break; 4827790c8c1SConrad Meyer clust = uma_zalloc(dn_zone_clust, M_NOWAIT); 4835475ca5aSMark Johnston if (clust == NULL) { 4845475ca5aSMark Johnston m_free(m); 4855475ca5aSMark Johnston break; 4865475ca5aSMark Johnston } 4877790c8c1SConrad Meyer mb_ctor_clust(clust, dn_clsize, m, 0); 4885475ca5aSMark Johnston store[i] = m; 4895475ca5aSMark Johnston } 4900d1467b1SConrad Meyer KASSERT((flags & M_WAITOK) == 0 || i == count, 4910d1467b1SConrad Meyer ("%s: ran out of pre-allocated mbufs", __func__)); 4925475ca5aSMark Johnston return (i); 4935475ca5aSMark Johnston } 4945475ca5aSMark Johnston 4955475ca5aSMark Johnston static void 4967790c8c1SConrad Meyer dn_pack_release(void *arg __unused, void **store, int count) 4975475ca5aSMark Johnston { 4985475ca5aSMark Johnston struct mbuf *m; 4995475ca5aSMark Johnston void *clust; 5005475ca5aSMark Johnston int i; 5015475ca5aSMark Johnston 5025475ca5aSMark Johnston for (i = 0; i < count; i++) { 5035475ca5aSMark Johnston m = store[i]; 5045475ca5aSMark Johnston clust = m->m_ext.ext_buf; 5057790c8c1SConrad Meyer uma_zfree(dn_zone_clust, clust); 5067790c8c1SConrad Meyer uma_zfree(dn_zone_mbuf, m); 5075475ca5aSMark Johnston } 5085475ca5aSMark Johnston } 5095475ca5aSMark Johnston 5105475ca5aSMark Johnston /* 5117790c8c1SConrad Meyer * Free the pre-allocated mbufs and clusters reserved for debugnet, and destroy 5125475ca5aSMark Johnston * the corresponding UMA cache zones. 5135475ca5aSMark Johnston */ 5145475ca5aSMark Johnston void 5157790c8c1SConrad Meyer debugnet_mbuf_drain(void) 5165475ca5aSMark Johnston { 5175475ca5aSMark Johnston struct mbuf *m; 5185475ca5aSMark Johnston void *item; 5195475ca5aSMark Johnston 5207790c8c1SConrad Meyer if (dn_zone_mbuf != NULL) { 5217790c8c1SConrad Meyer uma_zdestroy(dn_zone_mbuf); 5227790c8c1SConrad Meyer dn_zone_mbuf = NULL; 5235475ca5aSMark Johnston } 5247790c8c1SConrad Meyer if (dn_zone_clust != NULL) { 5257790c8c1SConrad Meyer uma_zdestroy(dn_zone_clust); 5267790c8c1SConrad Meyer dn_zone_clust = NULL; 5275475ca5aSMark Johnston } 5287790c8c1SConrad Meyer if (dn_zone_pack != NULL) { 5297790c8c1SConrad Meyer uma_zdestroy(dn_zone_pack); 5307790c8c1SConrad Meyer dn_zone_pack = NULL; 5315475ca5aSMark Johnston } 5325475ca5aSMark Johnston 5337790c8c1SConrad Meyer while ((m = mbufq_dequeue(&dn_mbufq)) != NULL) 5345475ca5aSMark Johnston m_free(m); 5357790c8c1SConrad Meyer while ((item = mbufq_dequeue(&dn_clustq)) != NULL) 5367790c8c1SConrad Meyer uma_zfree(m_getzone(dn_clsize), item); 5375475ca5aSMark Johnston } 5385475ca5aSMark Johnston 5395475ca5aSMark Johnston /* 5407790c8c1SConrad Meyer * Callback invoked immediately prior to starting a debugnet connection. 5415475ca5aSMark Johnston */ 5425475ca5aSMark Johnston void 5437790c8c1SConrad Meyer debugnet_mbuf_start(void) 5445475ca5aSMark Johnston { 5455475ca5aSMark Johnston 5467790c8c1SConrad Meyer MPASS(!dn_saved_zones.dsz_debugnet_zones_enabled); 5477790c8c1SConrad Meyer 5487790c8c1SConrad Meyer /* Save the old zone pointers to restore when debugnet is closed. */ 5497790c8c1SConrad Meyer dn_saved_zones = (struct debugnet_saved_zones) { 5507790c8c1SConrad Meyer .dsz_debugnet_zones_enabled = true, 5517790c8c1SConrad Meyer .dsz_mbuf = zone_mbuf, 5527790c8c1SConrad Meyer .dsz_clust = zone_clust, 5537790c8c1SConrad Meyer .dsz_pack = zone_pack, 5547790c8c1SConrad Meyer .dsz_jumbop = zone_jumbop, 5557790c8c1SConrad Meyer .dsz_jumbo9 = zone_jumbo9, 5567790c8c1SConrad Meyer .dsz_jumbo16 = zone_jumbo16, 5577790c8c1SConrad Meyer }; 5587790c8c1SConrad Meyer 5595475ca5aSMark Johnston /* 5605475ca5aSMark Johnston * All cluster zones return buffers of the size requested by the 5615475ca5aSMark Johnston * drivers. It's up to the driver to reinitialize the zones if the 5627790c8c1SConrad Meyer * MTU of a debugnet-enabled interface changes. 5635475ca5aSMark Johnston */ 5647790c8c1SConrad Meyer printf("debugnet: overwriting mbuf zone pointers\n"); 5657790c8c1SConrad Meyer zone_mbuf = dn_zone_mbuf; 5667790c8c1SConrad Meyer zone_clust = dn_zone_clust; 5677790c8c1SConrad Meyer zone_pack = dn_zone_pack; 5687790c8c1SConrad Meyer zone_jumbop = dn_zone_clust; 5697790c8c1SConrad Meyer zone_jumbo9 = dn_zone_clust; 5707790c8c1SConrad Meyer zone_jumbo16 = dn_zone_clust; 5715475ca5aSMark Johnston } 5725475ca5aSMark Johnston 5735475ca5aSMark Johnston /* 5747790c8c1SConrad Meyer * Callback invoked when a debugnet connection is closed/finished. 5755475ca5aSMark Johnston */ 5765475ca5aSMark Johnston void 5777790c8c1SConrad Meyer debugnet_mbuf_finish(void) 5787790c8c1SConrad Meyer { 5797790c8c1SConrad Meyer 5807790c8c1SConrad Meyer MPASS(dn_saved_zones.dsz_debugnet_zones_enabled); 5817790c8c1SConrad Meyer 5827790c8c1SConrad Meyer printf("debugnet: restoring mbuf zone pointers\n"); 5837790c8c1SConrad Meyer zone_mbuf = dn_saved_zones.dsz_mbuf; 5847790c8c1SConrad Meyer zone_clust = dn_saved_zones.dsz_clust; 5857790c8c1SConrad Meyer zone_pack = dn_saved_zones.dsz_pack; 5867790c8c1SConrad Meyer zone_jumbop = dn_saved_zones.dsz_jumbop; 5877790c8c1SConrad Meyer zone_jumbo9 = dn_saved_zones.dsz_jumbo9; 5887790c8c1SConrad Meyer zone_jumbo16 = dn_saved_zones.dsz_jumbo16; 5897790c8c1SConrad Meyer 5907790c8c1SConrad Meyer memset(&dn_saved_zones, 0, sizeof(dn_saved_zones)); 5917790c8c1SConrad Meyer } 5927790c8c1SConrad Meyer 5937790c8c1SConrad Meyer /* 5947790c8c1SConrad Meyer * Reinitialize the debugnet mbuf+cluster pool and cache zones. 5957790c8c1SConrad Meyer */ 5967790c8c1SConrad Meyer void 5977790c8c1SConrad Meyer debugnet_mbuf_reinit(int nmbuf, int nclust, int clsize) 5985475ca5aSMark Johnston { 5995475ca5aSMark Johnston struct mbuf *m; 6005475ca5aSMark Johnston void *item; 6015475ca5aSMark Johnston 6027790c8c1SConrad Meyer debugnet_mbuf_drain(); 6035475ca5aSMark Johnston 6047790c8c1SConrad Meyer dn_clsize = clsize; 6055475ca5aSMark Johnston 6067790c8c1SConrad Meyer dn_zone_mbuf = uma_zcache_create("debugnet_" MBUF_MEM_NAME, 60730be9685SRyan Libby MSIZE, mb_ctor_mbuf, mb_dtor_mbuf, NULL, NULL, 6087790c8c1SConrad Meyer dn_buf_import, dn_buf_release, 6097790c8c1SConrad Meyer &dn_mbufq, UMA_ZONE_NOBUCKET); 6105475ca5aSMark Johnston 6117790c8c1SConrad Meyer dn_zone_clust = uma_zcache_create("debugnet_" MBUF_CLUSTER_MEM_NAME, 61230be9685SRyan Libby clsize, mb_ctor_clust, NULL, NULL, NULL, 6137790c8c1SConrad Meyer dn_buf_import, dn_buf_release, 6147790c8c1SConrad Meyer &dn_clustq, UMA_ZONE_NOBUCKET); 6155475ca5aSMark Johnston 6167790c8c1SConrad Meyer dn_zone_pack = uma_zcache_create("debugnet_" MBUF_PACKET_MEM_NAME, 6175475ca5aSMark Johnston MCLBYTES, mb_ctor_pack, mb_dtor_pack, NULL, NULL, 6187790c8c1SConrad Meyer dn_pack_import, dn_pack_release, 6195475ca5aSMark Johnston NULL, UMA_ZONE_NOBUCKET); 6205475ca5aSMark Johnston 6215475ca5aSMark Johnston while (nmbuf-- > 0) { 622440217b0SZhenlei Huang m = m_get(M_WAITOK, MT_DATA); 6237790c8c1SConrad Meyer uma_zfree(dn_zone_mbuf, m); 6245475ca5aSMark Johnston } 6255475ca5aSMark Johnston while (nclust-- > 0) { 6267790c8c1SConrad Meyer item = uma_zalloc(m_getzone(dn_clsize), M_WAITOK); 6277790c8c1SConrad Meyer uma_zfree(dn_zone_clust, item); 6285475ca5aSMark Johnston } 6295475ca5aSMark Johnston } 6307790c8c1SConrad Meyer #endif /* DEBUGNET */ 6315475ca5aSMark Johnston 632099a0e58SBosko Milekic /* 63303270b59SJeff Roberson * Constructor for Mbuf primary zone. 634099a0e58SBosko Milekic * 635099a0e58SBosko Milekic * The 'arg' pointer points to a mb_args structure which 636099a0e58SBosko Milekic * contains call-specific information required to support the 63756a4e45aSAndre Oppermann * mbuf allocation API. See mbuf.h. 638099a0e58SBosko Milekic */ 639b23f72e9SBrian Feldman static int 640b23f72e9SBrian Feldman mb_ctor_mbuf(void *mem, int size, void *arg, int how) 641099a0e58SBosko Milekic { 642099a0e58SBosko Milekic struct mbuf *m; 643099a0e58SBosko Milekic struct mb_args *args; 644b23f72e9SBrian Feldman int error; 645099a0e58SBosko Milekic int flags; 646099a0e58SBosko Milekic short type; 647099a0e58SBosko Milekic 648099a0e58SBosko Milekic args = (struct mb_args *)arg; 649099a0e58SBosko Milekic type = args->type; 650099a0e58SBosko Milekic 65156a4e45aSAndre Oppermann /* 65256a4e45aSAndre Oppermann * The mbuf is initialized later. The caller has the 653fcf90618SGleb Smirnoff * responsibility to set up any MAC labels too. 65456a4e45aSAndre Oppermann */ 65556a4e45aSAndre Oppermann if (type == MT_NOINIT) 65656a4e45aSAndre Oppermann return (0); 65756a4e45aSAndre Oppermann 658afb295ccSAndre Oppermann m = (struct mbuf *)mem; 659afb295ccSAndre Oppermann flags = args->flags; 660fddd4f62SNavdeep Parhar MPASS((flags & M_NOFREE) == 0); 661afb295ccSAndre Oppermann 662b4b12e52SGleb Smirnoff error = m_init(m, how, type, flags); 663afb295ccSAndre Oppermann 664b23f72e9SBrian Feldman return (error); 665099a0e58SBosko Milekic } 666099a0e58SBosko Milekic 667099a0e58SBosko Milekic /* 66803270b59SJeff Roberson * The Mbuf primary zone destructor. 669099a0e58SBosko Milekic */ 670099a0e58SBosko Milekic static void 671099a0e58SBosko Milekic mb_dtor_mbuf(void *mem, int size, void *arg) 672099a0e58SBosko Milekic { 673099a0e58SBosko Milekic struct mbuf *m; 6740a048d4aSMateusz Guzik unsigned long flags __diagused; 675099a0e58SBosko Milekic 676099a0e58SBosko Milekic m = (struct mbuf *)mem; 677629b9e08SKip Macy flags = (unsigned long)arg; 678629b9e08SKip Macy 679a9fa76f2SNavdeep Parhar KASSERT((m->m_flags & M_NOFREE) == 0, ("%s: M_NOFREE set", __func__)); 680fb32c8dbSMateusz Guzik KASSERT((flags & 0x1) == 0, ("%s: obsolete MB_DTOR_SKIP passed", __func__)); 681fb32c8dbSMateusz Guzik if ((m->m_flags & M_PKTHDR) && !SLIST_EMPTY(&m->m_pkthdr.tags)) 682099a0e58SBosko Milekic m_tag_delete_chain(m, NULL); 683099a0e58SBosko Milekic } 684099a0e58SBosko Milekic 68556a4e45aSAndre Oppermann /* 68656a4e45aSAndre Oppermann * The Mbuf Packet zone destructor. 68756a4e45aSAndre Oppermann */ 688099a0e58SBosko Milekic static void 689099a0e58SBosko Milekic mb_dtor_pack(void *mem, int size, void *arg) 690099a0e58SBosko Milekic { 691099a0e58SBosko Milekic struct mbuf *m; 692099a0e58SBosko Milekic 693099a0e58SBosko Milekic m = (struct mbuf *)mem; 694099a0e58SBosko Milekic if ((m->m_flags & M_PKTHDR) != 0) 695099a0e58SBosko Milekic m_tag_delete_chain(m, NULL); 69656a4e45aSAndre Oppermann 69756a4e45aSAndre Oppermann /* Make sure we've got a clean cluster back. */ 69856a4e45aSAndre Oppermann KASSERT((m->m_flags & M_EXT) == M_EXT, ("%s: M_EXT not set", __func__)); 69956a4e45aSAndre Oppermann KASSERT(m->m_ext.ext_buf != NULL, ("%s: ext_buf == NULL", __func__)); 70056a4e45aSAndre Oppermann KASSERT(m->m_ext.ext_free == NULL, ("%s: ext_free != NULL", __func__)); 701cf827063SPoul-Henning Kamp KASSERT(m->m_ext.ext_arg1 == NULL, ("%s: ext_arg1 != NULL", __func__)); 702cf827063SPoul-Henning Kamp KASSERT(m->m_ext.ext_arg2 == NULL, ("%s: ext_arg2 != NULL", __func__)); 70356a4e45aSAndre Oppermann KASSERT(m->m_ext.ext_size == MCLBYTES, ("%s: ext_size != MCLBYTES", __func__)); 70449d46b61SGleb Smirnoff KASSERT(m->m_ext.ext_type == EXT_PACKET, ("%s: ext_type != EXT_PACKET", __func__)); 70510094910SMark Johnston #if defined(INVARIANTS) && !defined(KMSAN) 706a03c2393SAlexander Motin trash_dtor(m->m_ext.ext_buf, MCLBYTES, zone_clust); 707121f0509SMike Silbersack #endif 7086c125b8dSMohan Srinivasan /* 709ef44c8d2SDavid E. O'Brien * If there are processes blocked on zone_clust, waiting for pages 71008cfa56eSMark Johnston * to be freed up, cause them to be woken up by draining the 71108cfa56eSMark Johnston * packet zone. We are exposed to a race here (in the check for 712ef44c8d2SDavid E. O'Brien * the UMA_ZFLAG_FULL) where we might miss the flag set, but that 713ef44c8d2SDavid E. O'Brien * is deliberate. We don't want to acquire the zone lock for every 714ef44c8d2SDavid E. O'Brien * mbuf free. 7156c125b8dSMohan Srinivasan */ 716727c6918SJeff Roberson if (uma_zone_exhausted(zone_clust)) 71708cfa56eSMark Johnston uma_zone_reclaim(zone_pack, UMA_RECLAIM_DRAIN); 718099a0e58SBosko Milekic } 719099a0e58SBosko Milekic 720099a0e58SBosko Milekic /* 721ec63cb90SAndre Oppermann * The Cluster and Jumbo[PAGESIZE|9|16] zone constructor. 722099a0e58SBosko Milekic * 723099a0e58SBosko Milekic * Here the 'arg' pointer points to the Mbuf which we 72456a4e45aSAndre Oppermann * are configuring cluster storage for. If 'arg' is 72556a4e45aSAndre Oppermann * empty we allocate just the cluster without setting 72656a4e45aSAndre Oppermann * the mbuf to it. See mbuf.h. 727099a0e58SBosko Milekic */ 728b23f72e9SBrian Feldman static int 729b23f72e9SBrian Feldman mb_ctor_clust(void *mem, int size, void *arg, int how) 730099a0e58SBosko Milekic { 731099a0e58SBosko Milekic struct mbuf *m; 732099a0e58SBosko Milekic 7330f4d9d04SKip Macy m = (struct mbuf *)arg; 7340f4d9d04SKip Macy if (m != NULL) { 735e8fd18f3SGleb Smirnoff m->m_ext.ext_buf = (char *)mem; 736099a0e58SBosko Milekic m->m_data = m->m_ext.ext_buf; 737099a0e58SBosko Milekic m->m_flags |= M_EXT; 738099a0e58SBosko Milekic m->m_ext.ext_free = NULL; 739cf827063SPoul-Henning Kamp m->m_ext.ext_arg1 = NULL; 740cf827063SPoul-Henning Kamp m->m_ext.ext_arg2 = NULL; 74156a4e45aSAndre Oppermann m->m_ext.ext_size = size; 74256a5f52eSGleb Smirnoff m->m_ext.ext_type = m_gettype(size); 74356a5f52eSGleb Smirnoff m->m_ext.ext_flags = EXT_FLAG_EMBREF; 74456a5f52eSGleb Smirnoff m->m_ext.ext_count = 1; 74556a4e45aSAndre Oppermann } 7460f4d9d04SKip Macy 747b23f72e9SBrian Feldman return (0); 748099a0e58SBosko Milekic } 749099a0e58SBosko Milekic 75056a4e45aSAndre Oppermann /* 751099a0e58SBosko Milekic * The Packet secondary zone's init routine, executed on the 75256a4e45aSAndre Oppermann * object's transition from mbuf keg slab to zone cache. 753099a0e58SBosko Milekic */ 754b23f72e9SBrian Feldman static int 75556a4e45aSAndre Oppermann mb_zinit_pack(void *mem, int size, int how) 756099a0e58SBosko Milekic { 757099a0e58SBosko Milekic struct mbuf *m; 758099a0e58SBosko Milekic 75956a4e45aSAndre Oppermann m = (struct mbuf *)mem; /* m is virgin. */ 760a7bd90efSAndre Oppermann if (uma_zalloc_arg(zone_clust, m, how) == NULL || 761a7bd90efSAndre Oppermann m->m_ext.ext_buf == NULL) 762b23f72e9SBrian Feldman return (ENOMEM); 763cd5bb63bSAndre Oppermann m->m_ext.ext_type = EXT_PACKET; /* Override. */ 76410094910SMark Johnston #if defined(INVARIANTS) && !defined(KMSAN) 765121f0509SMike Silbersack trash_init(m->m_ext.ext_buf, MCLBYTES, how); 766121f0509SMike Silbersack #endif 767b23f72e9SBrian Feldman return (0); 768099a0e58SBosko Milekic } 769099a0e58SBosko Milekic 770099a0e58SBosko Milekic /* 771099a0e58SBosko Milekic * The Packet secondary zone's fini routine, executed on the 772099a0e58SBosko Milekic * object's transition from zone cache to keg slab. 773099a0e58SBosko Milekic */ 774099a0e58SBosko Milekic static void 77556a4e45aSAndre Oppermann mb_zfini_pack(void *mem, int size) 776099a0e58SBosko Milekic { 777099a0e58SBosko Milekic struct mbuf *m; 778099a0e58SBosko Milekic 779099a0e58SBosko Milekic m = (struct mbuf *)mem; 78010094910SMark Johnston #if defined(INVARIANTS) && !defined(KMSAN) 781121f0509SMike Silbersack trash_fini(m->m_ext.ext_buf, MCLBYTES); 782121f0509SMike Silbersack #endif 783099a0e58SBosko Milekic uma_zfree_arg(zone_clust, m->m_ext.ext_buf, NULL); 78410094910SMark Johnston #if defined(INVARIANTS) && !defined(KMSAN) 785a03c2393SAlexander Motin trash_dtor(mem, size, zone_clust); 786a7b844d2SMike Silbersack #endif 787099a0e58SBosko Milekic } 788099a0e58SBosko Milekic 789099a0e58SBosko Milekic /* 790099a0e58SBosko Milekic * The "packet" keg constructor. 791099a0e58SBosko Milekic */ 792b23f72e9SBrian Feldman static int 793b23f72e9SBrian Feldman mb_ctor_pack(void *mem, int size, void *arg, int how) 794099a0e58SBosko Milekic { 795099a0e58SBosko Milekic struct mbuf *m; 796099a0e58SBosko Milekic struct mb_args *args; 797ce28636bSAndre Oppermann int error, flags; 798099a0e58SBosko Milekic short type; 799099a0e58SBosko Milekic 800099a0e58SBosko Milekic m = (struct mbuf *)mem; 801099a0e58SBosko Milekic args = (struct mb_args *)arg; 802099a0e58SBosko Milekic flags = args->flags; 803099a0e58SBosko Milekic type = args->type; 804fddd4f62SNavdeep Parhar MPASS((flags & M_NOFREE) == 0); 805099a0e58SBosko Milekic 80610094910SMark Johnston #if defined(INVARIANTS) && !defined(KMSAN) 807a03c2393SAlexander Motin trash_ctor(m->m_ext.ext_buf, MCLBYTES, zone_clust, how); 808121f0509SMike Silbersack #endif 809099a0e58SBosko Milekic 810b4b12e52SGleb Smirnoff error = m_init(m, how, type, flags); 811afb295ccSAndre Oppermann 81256a4e45aSAndre Oppermann /* m_ext is already initialized. */ 813afb295ccSAndre Oppermann m->m_data = m->m_ext.ext_buf; 814afb295ccSAndre Oppermann m->m_flags = (flags | M_EXT); 81556a4e45aSAndre Oppermann 816afb295ccSAndre Oppermann return (error); 817099a0e58SBosko Milekic } 818099a0e58SBosko Milekic 819099a0e58SBosko Milekic /* 820e60b2fcbSGleb Smirnoff * This is the protocol drain routine. Called by UMA whenever any of the 821e60b2fcbSGleb Smirnoff * mbuf zones is closed to its limit. 822099a0e58SBosko Milekic */ 823099a0e58SBosko Milekic static void 824e60b2fcbSGleb Smirnoff mb_reclaim(uma_zone_t zone __unused, int pending __unused) 825099a0e58SBosko Milekic { 826099a0e58SBosko Milekic 82781a34d37SGleb Smirnoff EVENTHANDLER_INVOKE(mbuf_lowmem, VM_LOW_MBUFS); 828099a0e58SBosko Milekic } 8295e4bc63bSGleb Smirnoff 8305e4bc63bSGleb Smirnoff /* 83182334850SJohn Baldwin * Free "count" units of I/O from an mbuf chain. They could be held 83261664ee7SGleb Smirnoff * in M_EXTPG or just as a normal mbuf. This code is intended to be 83382334850SJohn Baldwin * called in an error path (I/O error, closed connection, etc). 83482334850SJohn Baldwin */ 83582334850SJohn Baldwin void 83682334850SJohn Baldwin mb_free_notready(struct mbuf *m, int count) 83782334850SJohn Baldwin { 83882334850SJohn Baldwin int i; 83982334850SJohn Baldwin 84082334850SJohn Baldwin for (i = 0; i < count && m != NULL; i++) { 84161664ee7SGleb Smirnoff if ((m->m_flags & M_EXTPG) != 0) { 8427b6c99d0SGleb Smirnoff m->m_epg_nrdy--; 8437b6c99d0SGleb Smirnoff if (m->m_epg_nrdy != 0) 84482334850SJohn Baldwin continue; 84582334850SJohn Baldwin } 84682334850SJohn Baldwin m = m_free(m); 84782334850SJohn Baldwin } 84882334850SJohn Baldwin KASSERT(i == count, ("Removed only %d items from %p", i, m)); 84982334850SJohn Baldwin } 85082334850SJohn Baldwin 85182334850SJohn Baldwin /* 85282334850SJohn Baldwin * Compress an unmapped mbuf into a simple mbuf when it holds a small 85382334850SJohn Baldwin * amount of data. This is used as a DOS defense to avoid having 85482334850SJohn Baldwin * small packets tie up wired pages, an ext_pgs structure, and an 85582334850SJohn Baldwin * mbuf. Since this converts the existing mbuf in place, it can only 85682334850SJohn Baldwin * be used if there are no other references to 'm'. 85782334850SJohn Baldwin */ 85882334850SJohn Baldwin int 85982334850SJohn Baldwin mb_unmapped_compress(struct mbuf *m) 86082334850SJohn Baldwin { 86182334850SJohn Baldwin volatile u_int *refcnt; 8624c9f0f98SGleb Smirnoff char buf[MLEN]; 86382334850SJohn Baldwin 86482334850SJohn Baldwin /* 86582334850SJohn Baldwin * Assert that 'm' does not have a packet header. If 'm' had 86682334850SJohn Baldwin * a packet header, it would only be able to hold MHLEN bytes 86782334850SJohn Baldwin * and m_data would have to be initialized differently. 86882334850SJohn Baldwin */ 86961664ee7SGleb Smirnoff KASSERT((m->m_flags & M_PKTHDR) == 0 && (m->m_flags & M_EXTPG), 87061664ee7SGleb Smirnoff ("%s: m %p !M_EXTPG or M_PKTHDR", __func__, m)); 87182334850SJohn Baldwin KASSERT(m->m_len <= MLEN, ("m_len too large %p", m)); 87282334850SJohn Baldwin 87382334850SJohn Baldwin if (m->m_ext.ext_flags & EXT_FLAG_EMBREF) { 87482334850SJohn Baldwin refcnt = &m->m_ext.ext_count; 87582334850SJohn Baldwin } else { 87682334850SJohn Baldwin KASSERT(m->m_ext.ext_cnt != NULL, 87782334850SJohn Baldwin ("%s: no refcounting pointer on %p", __func__, m)); 87882334850SJohn Baldwin refcnt = m->m_ext.ext_cnt; 87982334850SJohn Baldwin } 88082334850SJohn Baldwin 88182334850SJohn Baldwin if (*refcnt != 1) 88282334850SJohn Baldwin return (EBUSY); 88382334850SJohn Baldwin 8844c9f0f98SGleb Smirnoff m_copydata(m, 0, m->m_len, buf); 88523feb563SAndrew Gallatin 88623feb563SAndrew Gallatin /* Free the backing pages. */ 88723feb563SAndrew Gallatin m->m_ext.ext_free(m); 88882334850SJohn Baldwin 88982334850SJohn Baldwin /* Turn 'm' into a "normal" mbuf. */ 8906edfd179SGleb Smirnoff m->m_flags &= ~(M_EXT | M_RDONLY | M_EXTPG); 89182334850SJohn Baldwin m->m_data = m->m_dat; 89282334850SJohn Baldwin 8934c9f0f98SGleb Smirnoff /* Copy data back into m. */ 8944c9f0f98SGleb Smirnoff bcopy(buf, mtod(m, char *), m->m_len); 89582334850SJohn Baldwin 89682334850SJohn Baldwin return (0); 89782334850SJohn Baldwin } 89882334850SJohn Baldwin 89982334850SJohn Baldwin /* 90082334850SJohn Baldwin * These next few routines are used to permit downgrading an unmapped 90182334850SJohn Baldwin * mbuf to a chain of mapped mbufs. This is used when an interface 90282334850SJohn Baldwin * doesn't supported unmapped mbufs or if checksums need to be 90382334850SJohn Baldwin * computed in software. 90482334850SJohn Baldwin * 90582334850SJohn Baldwin * Each unmapped mbuf is converted to a chain of mbufs. First, any 90682334850SJohn Baldwin * TLS header data is stored in a regular mbuf. Second, each page of 90782334850SJohn Baldwin * unmapped data is stored in an mbuf with an EXT_SFBUF external 90882334850SJohn Baldwin * cluster. These mbufs use an sf_buf to provide a valid KVA for the 90982334850SJohn Baldwin * associated physical page. They also hold a reference on the 91061664ee7SGleb Smirnoff * original M_EXTPG mbuf to ensure the physical page doesn't go away. 91182334850SJohn Baldwin * Finally, any TLS trailer data is stored in a regular mbuf. 91282334850SJohn Baldwin * 91382334850SJohn Baldwin * mb_unmapped_free_mext() is the ext_free handler for the EXT_SFBUF 91482334850SJohn Baldwin * mbufs. It frees the associated sf_buf and releases its reference 91561664ee7SGleb Smirnoff * on the original M_EXTPG mbuf. 91682334850SJohn Baldwin * 91782334850SJohn Baldwin * _mb_unmapped_to_ext() is a helper function that converts a single 91882334850SJohn Baldwin * unmapped mbuf into a chain of mbufs. 91982334850SJohn Baldwin * 92082334850SJohn Baldwin * mb_unmapped_to_ext() is the public function that walks an mbuf 92182334850SJohn Baldwin * chain converting any unmapped mbufs to mapped mbufs. It returns 92282334850SJohn Baldwin * the new chain of unmapped mbufs on success. On failure it frees 92382334850SJohn Baldwin * the original mbuf chain and returns NULL. 92482334850SJohn Baldwin */ 92582334850SJohn Baldwin static void 92682334850SJohn Baldwin mb_unmapped_free_mext(struct mbuf *m) 92782334850SJohn Baldwin { 92882334850SJohn Baldwin struct sf_buf *sf; 92982334850SJohn Baldwin struct mbuf *old_m; 93082334850SJohn Baldwin 93182334850SJohn Baldwin sf = m->m_ext.ext_arg1; 93282334850SJohn Baldwin sf_buf_free(sf); 93382334850SJohn Baldwin 93461664ee7SGleb Smirnoff /* Drop the reference on the backing M_EXTPG mbuf. */ 93582334850SJohn Baldwin old_m = m->m_ext.ext_arg2; 93661664ee7SGleb Smirnoff mb_free_extpg(old_m); 93782334850SJohn Baldwin } 93882334850SJohn Baldwin 939*cf322978SKonstantin Belousov static int 940*cf322978SKonstantin Belousov _mb_unmapped_to_ext(struct mbuf *m, struct mbuf **mres) 94182334850SJohn Baldwin { 94282334850SJohn Baldwin struct mbuf *m_new, *top, *prev, *mref; 94382334850SJohn Baldwin struct sf_buf *sf; 94482334850SJohn Baldwin vm_page_t pg; 94582334850SJohn Baldwin int i, len, off, pglen, pgoff, seglen, segoff; 94682334850SJohn Baldwin volatile u_int *refcnt; 94782334850SJohn Baldwin u_int ref_inc = 0; 94882334850SJohn Baldwin 949365e8da4SGleb Smirnoff M_ASSERTEXTPG(m); 950*cf322978SKonstantin Belousov 951*cf322978SKonstantin Belousov if (m->m_epg_tls != NULL) { 952*cf322978SKonstantin Belousov /* can't convert TLS mbuf */ 953*cf322978SKonstantin Belousov m_freem(m); 954*cf322978SKonstantin Belousov *mres = NULL; 955*cf322978SKonstantin Belousov return (EINVAL); 956*cf322978SKonstantin Belousov } 957*cf322978SKonstantin Belousov 95882334850SJohn Baldwin len = m->m_len; 95982334850SJohn Baldwin 96082334850SJohn Baldwin /* See if this is the mbuf that holds the embedded refcount. */ 96182334850SJohn Baldwin if (m->m_ext.ext_flags & EXT_FLAG_EMBREF) { 96282334850SJohn Baldwin refcnt = &m->m_ext.ext_count; 96382334850SJohn Baldwin mref = m; 96482334850SJohn Baldwin } else { 96582334850SJohn Baldwin KASSERT(m->m_ext.ext_cnt != NULL, 96682334850SJohn Baldwin ("%s: no refcounting pointer on %p", __func__, m)); 96782334850SJohn Baldwin refcnt = m->m_ext.ext_cnt; 96882334850SJohn Baldwin mref = __containerof(refcnt, struct mbuf, m_ext.ext_count); 96982334850SJohn Baldwin } 97082334850SJohn Baldwin 97182334850SJohn Baldwin /* Skip over any data removed from the front. */ 97282334850SJohn Baldwin off = mtod(m, vm_offset_t); 97382334850SJohn Baldwin 97482334850SJohn Baldwin top = NULL; 9757b6c99d0SGleb Smirnoff if (m->m_epg_hdrlen != 0) { 9767b6c99d0SGleb Smirnoff if (off >= m->m_epg_hdrlen) { 9777b6c99d0SGleb Smirnoff off -= m->m_epg_hdrlen; 97882334850SJohn Baldwin } else { 9797b6c99d0SGleb Smirnoff seglen = m->m_epg_hdrlen - off; 98082334850SJohn Baldwin segoff = off; 98182334850SJohn Baldwin seglen = min(seglen, len); 98282334850SJohn Baldwin off = 0; 98382334850SJohn Baldwin len -= seglen; 98482334850SJohn Baldwin m_new = m_get(M_NOWAIT, MT_DATA); 98582334850SJohn Baldwin if (m_new == NULL) 98682334850SJohn Baldwin goto fail; 98782334850SJohn Baldwin m_new->m_len = seglen; 98882334850SJohn Baldwin prev = top = m_new; 9890c103266SGleb Smirnoff memcpy(mtod(m_new, void *), &m->m_epg_hdr[segoff], 99082334850SJohn Baldwin seglen); 99182334850SJohn Baldwin } 99282334850SJohn Baldwin } 9937b6c99d0SGleb Smirnoff pgoff = m->m_epg_1st_off; 9947b6c99d0SGleb Smirnoff for (i = 0; i < m->m_epg_npgs && len > 0; i++) { 995c4ee38f8SGleb Smirnoff pglen = m_epg_pagelen(m, i, pgoff); 99682334850SJohn Baldwin if (off >= pglen) { 99782334850SJohn Baldwin off -= pglen; 99882334850SJohn Baldwin pgoff = 0; 99982334850SJohn Baldwin continue; 100082334850SJohn Baldwin } 100182334850SJohn Baldwin seglen = pglen - off; 100282334850SJohn Baldwin segoff = pgoff + off; 100382334850SJohn Baldwin off = 0; 100482334850SJohn Baldwin seglen = min(seglen, len); 100582334850SJohn Baldwin len -= seglen; 100682334850SJohn Baldwin 10070c103266SGleb Smirnoff pg = PHYS_TO_VM_PAGE(m->m_epg_pa[i]); 100882334850SJohn Baldwin m_new = m_get(M_NOWAIT, MT_DATA); 100982334850SJohn Baldwin if (m_new == NULL) 101082334850SJohn Baldwin goto fail; 101182334850SJohn Baldwin if (top == NULL) { 101282334850SJohn Baldwin top = prev = m_new; 101382334850SJohn Baldwin } else { 101482334850SJohn Baldwin prev->m_next = m_new; 101582334850SJohn Baldwin prev = m_new; 101682334850SJohn Baldwin } 101782334850SJohn Baldwin sf = sf_buf_alloc(pg, SFB_NOWAIT); 101882334850SJohn Baldwin if (sf == NULL) 101982334850SJohn Baldwin goto fail; 102082334850SJohn Baldwin 102182334850SJohn Baldwin ref_inc++; 102282334850SJohn Baldwin m_extadd(m_new, (char *)sf_buf_kva(sf), PAGE_SIZE, 1023314cb279SJohn Baldwin mb_unmapped_free_mext, sf, mref, m->m_flags & M_RDONLY, 1024314cb279SJohn Baldwin EXT_SFBUF); 102582334850SJohn Baldwin m_new->m_data += segoff; 102682334850SJohn Baldwin m_new->m_len = seglen; 102782334850SJohn Baldwin 102882334850SJohn Baldwin pgoff = 0; 102982334850SJohn Baldwin }; 103082334850SJohn Baldwin if (len != 0) { 10317b6c99d0SGleb Smirnoff KASSERT((off + len) <= m->m_epg_trllen, 103282334850SJohn Baldwin ("off + len > trail (%d + %d > %d)", off, len, 10337b6c99d0SGleb Smirnoff m->m_epg_trllen)); 103482334850SJohn Baldwin m_new = m_get(M_NOWAIT, MT_DATA); 103582334850SJohn Baldwin if (m_new == NULL) 103682334850SJohn Baldwin goto fail; 103782334850SJohn Baldwin if (top == NULL) 103882334850SJohn Baldwin top = m_new; 103982334850SJohn Baldwin else 104082334850SJohn Baldwin prev->m_next = m_new; 104182334850SJohn Baldwin m_new->m_len = len; 10420c103266SGleb Smirnoff memcpy(mtod(m_new, void *), &m->m_epg_trail[off], len); 104382334850SJohn Baldwin } 104482334850SJohn Baldwin 104582334850SJohn Baldwin if (ref_inc != 0) { 104682334850SJohn Baldwin /* 104782334850SJohn Baldwin * Obtain an additional reference on the old mbuf for 104882334850SJohn Baldwin * each created EXT_SFBUF mbuf. They will be dropped 104982334850SJohn Baldwin * in mb_unmapped_free_mext(). 105082334850SJohn Baldwin */ 105182334850SJohn Baldwin if (*refcnt == 1) 105282334850SJohn Baldwin *refcnt += ref_inc; 105382334850SJohn Baldwin else 105482334850SJohn Baldwin atomic_add_int(refcnt, ref_inc); 105582334850SJohn Baldwin } 105682334850SJohn Baldwin m_free(m); 1057*cf322978SKonstantin Belousov *mres = top; 1058*cf322978SKonstantin Belousov return (0); 105982334850SJohn Baldwin 106082334850SJohn Baldwin fail: 106182334850SJohn Baldwin if (ref_inc != 0) { 106282334850SJohn Baldwin /* 106382334850SJohn Baldwin * Obtain an additional reference on the old mbuf for 106482334850SJohn Baldwin * each created EXT_SFBUF mbuf. They will be 106582334850SJohn Baldwin * immediately dropped when these mbufs are freed 106682334850SJohn Baldwin * below. 106782334850SJohn Baldwin */ 106882334850SJohn Baldwin if (*refcnt == 1) 106982334850SJohn Baldwin *refcnt += ref_inc; 107082334850SJohn Baldwin else 107182334850SJohn Baldwin atomic_add_int(refcnt, ref_inc); 107282334850SJohn Baldwin } 107382334850SJohn Baldwin m_free(m); 107482334850SJohn Baldwin m_freem(top); 1075*cf322978SKonstantin Belousov *mres = NULL; 1076*cf322978SKonstantin Belousov return (ENOMEM); 107782334850SJohn Baldwin } 107882334850SJohn Baldwin 1079*cf322978SKonstantin Belousov int 1080*cf322978SKonstantin Belousov mb_unmapped_to_ext(struct mbuf *top, struct mbuf **mres) 108182334850SJohn Baldwin { 1082*cf322978SKonstantin Belousov struct mbuf *m, *m1, *next, *prev = NULL; 1083*cf322978SKonstantin Belousov int error; 108482334850SJohn Baldwin 108582334850SJohn Baldwin prev = NULL; 108682334850SJohn Baldwin for (m = top; m != NULL; m = next) { 108782334850SJohn Baldwin /* m might be freed, so cache the next pointer. */ 108882334850SJohn Baldwin next = m->m_next; 10896edfd179SGleb Smirnoff if (m->m_flags & M_EXTPG) { 109082334850SJohn Baldwin if (prev != NULL) { 109182334850SJohn Baldwin /* 109282334850SJohn Baldwin * Remove 'm' from the new chain so 109382334850SJohn Baldwin * that the 'top' chain terminates 109482334850SJohn Baldwin * before 'm' in case 'top' is freed 109582334850SJohn Baldwin * due to an error. 109682334850SJohn Baldwin */ 109782334850SJohn Baldwin prev->m_next = NULL; 109882334850SJohn Baldwin } 1099*cf322978SKonstantin Belousov error = _mb_unmapped_to_ext(m, &m1); 1100*cf322978SKonstantin Belousov if (error != 0) { 1101*cf322978SKonstantin Belousov if (top != m) 1102*cf322978SKonstantin Belousov m_free(top); 110382334850SJohn Baldwin m_freem(next); 1104*cf322978SKonstantin Belousov *mres = NULL; 1105*cf322978SKonstantin Belousov return (error); 110682334850SJohn Baldwin } 1107*cf322978SKonstantin Belousov m = m1; 110882334850SJohn Baldwin if (prev == NULL) { 110982334850SJohn Baldwin top = m; 111082334850SJohn Baldwin } else { 111182334850SJohn Baldwin prev->m_next = m; 111282334850SJohn Baldwin } 111382334850SJohn Baldwin 111482334850SJohn Baldwin /* 111582334850SJohn Baldwin * Replaced one mbuf with a chain, so we must 111682334850SJohn Baldwin * find the end of chain. 111782334850SJohn Baldwin */ 111882334850SJohn Baldwin prev = m_last(m); 111982334850SJohn Baldwin } else { 112082334850SJohn Baldwin if (prev != NULL) { 112182334850SJohn Baldwin prev->m_next = m; 112282334850SJohn Baldwin } 112382334850SJohn Baldwin prev = m; 112482334850SJohn Baldwin } 112582334850SJohn Baldwin } 1126*cf322978SKonstantin Belousov *mres = top; 1127*cf322978SKonstantin Belousov return (0); 112882334850SJohn Baldwin } 112982334850SJohn Baldwin 113082334850SJohn Baldwin /* 113161664ee7SGleb Smirnoff * Allocate an empty M_EXTPG mbuf. The ext_free routine is 113282334850SJohn Baldwin * responsible for freeing any pages backing this mbuf when it is 113382334850SJohn Baldwin * freed. 113482334850SJohn Baldwin */ 113582334850SJohn Baldwin struct mbuf * 1136314cb279SJohn Baldwin mb_alloc_ext_pgs(int how, m_ext_free_t ext_free, int flags) 113782334850SJohn Baldwin { 113882334850SJohn Baldwin struct mbuf *m; 113982334850SJohn Baldwin 114082334850SJohn Baldwin m = m_get(how, MT_DATA); 114182334850SJohn Baldwin if (m == NULL) 114282334850SJohn Baldwin return (NULL); 114382334850SJohn Baldwin 11447b6c99d0SGleb Smirnoff m->m_epg_npgs = 0; 11457b6c99d0SGleb Smirnoff m->m_epg_nrdy = 0; 11467b6c99d0SGleb Smirnoff m->m_epg_1st_off = 0; 11477b6c99d0SGleb Smirnoff m->m_epg_last_len = 0; 11487b6c99d0SGleb Smirnoff m->m_epg_flags = 0; 11497b6c99d0SGleb Smirnoff m->m_epg_hdrlen = 0; 11507b6c99d0SGleb Smirnoff m->m_epg_trllen = 0; 11517b6c99d0SGleb Smirnoff m->m_epg_tls = NULL; 11527b6c99d0SGleb Smirnoff m->m_epg_so = NULL; 115382334850SJohn Baldwin m->m_data = NULL; 1154314cb279SJohn Baldwin m->m_flags |= M_EXT | M_EXTPG | flags; 115582334850SJohn Baldwin m->m_ext.ext_flags = EXT_FLAG_EMBREF; 115682334850SJohn Baldwin m->m_ext.ext_count = 1; 115782334850SJohn Baldwin m->m_ext.ext_size = 0; 115882334850SJohn Baldwin m->m_ext.ext_free = ext_free; 115982334850SJohn Baldwin return (m); 116082334850SJohn Baldwin } 116182334850SJohn Baldwin 116282334850SJohn Baldwin /* 11635e4bc63bSGleb Smirnoff * Clean up after mbufs with M_EXT storage attached to them if the 11645e4bc63bSGleb Smirnoff * reference count hits 1. 11655e4bc63bSGleb Smirnoff */ 11665e4bc63bSGleb Smirnoff void 11675e4bc63bSGleb Smirnoff mb_free_ext(struct mbuf *m) 11685e4bc63bSGleb Smirnoff { 116956a5f52eSGleb Smirnoff volatile u_int *refcnt; 117056a5f52eSGleb Smirnoff struct mbuf *mref; 11715e4bc63bSGleb Smirnoff int freembuf; 11725e4bc63bSGleb Smirnoff 11735e4bc63bSGleb Smirnoff KASSERT(m->m_flags & M_EXT, ("%s: M_EXT not set on %p", __func__, m)); 11745e4bc63bSGleb Smirnoff 117556a5f52eSGleb Smirnoff /* See if this is the mbuf that holds the embedded refcount. */ 117656a5f52eSGleb Smirnoff if (m->m_ext.ext_flags & EXT_FLAG_EMBREF) { 117756a5f52eSGleb Smirnoff refcnt = &m->m_ext.ext_count; 117856a5f52eSGleb Smirnoff mref = m; 117956a5f52eSGleb Smirnoff } else { 118056a5f52eSGleb Smirnoff KASSERT(m->m_ext.ext_cnt != NULL, 118156a5f52eSGleb Smirnoff ("%s: no refcounting pointer on %p", __func__, m)); 118256a5f52eSGleb Smirnoff refcnt = m->m_ext.ext_cnt; 118356a5f52eSGleb Smirnoff mref = __containerof(refcnt, struct mbuf, m_ext.ext_count); 118456a5f52eSGleb Smirnoff } 118556a5f52eSGleb Smirnoff 11865e4bc63bSGleb Smirnoff /* 118756a5f52eSGleb Smirnoff * Check if the header is embedded in the cluster. It is 118856a5f52eSGleb Smirnoff * important that we can't touch any of the mbuf fields 118956a5f52eSGleb Smirnoff * after we have freed the external storage, since mbuf 119017cd649fSGleb Smirnoff * could have been embedded in it. For now, the mbufs 119117cd649fSGleb Smirnoff * embedded into the cluster are always of type EXT_EXTREF, 119217cd649fSGleb Smirnoff * and for this type we won't free the mref. 11935e4bc63bSGleb Smirnoff */ 119417cd649fSGleb Smirnoff if (m->m_flags & M_NOFREE) { 119517cd649fSGleb Smirnoff freembuf = 0; 1196eec189c7SGleb Smirnoff KASSERT(m->m_ext.ext_type == EXT_EXTREF || 1197eec189c7SGleb Smirnoff m->m_ext.ext_type == EXT_RXRING, 119817cd649fSGleb Smirnoff ("%s: no-free mbuf %p has wrong type", __func__, m)); 119917cd649fSGleb Smirnoff } else 120017cd649fSGleb Smirnoff freembuf = 1; 12015e4bc63bSGleb Smirnoff 120256a5f52eSGleb Smirnoff /* Free attached storage if this mbuf is the only reference to it. */ 120356a5f52eSGleb Smirnoff if (*refcnt == 1 || atomic_fetchadd_int(refcnt, -1) == 1) { 12045e4bc63bSGleb Smirnoff switch (m->m_ext.ext_type) { 120556a5f52eSGleb Smirnoff case EXT_PACKET: 120656a5f52eSGleb Smirnoff /* The packet zone is special. */ 120756a5f52eSGleb Smirnoff if (*refcnt == 0) 120856a5f52eSGleb Smirnoff *refcnt = 1; 120956a5f52eSGleb Smirnoff uma_zfree(zone_pack, mref); 12105e4bc63bSGleb Smirnoff break; 12115e4bc63bSGleb Smirnoff case EXT_CLUSTER: 12125e4bc63bSGleb Smirnoff uma_zfree(zone_clust, m->m_ext.ext_buf); 12130a718a6eSMateusz Guzik m_free_raw(mref); 12145e4bc63bSGleb Smirnoff break; 12155e4bc63bSGleb Smirnoff case EXT_JUMBOP: 12165e4bc63bSGleb Smirnoff uma_zfree(zone_jumbop, m->m_ext.ext_buf); 12170a718a6eSMateusz Guzik m_free_raw(mref); 12185e4bc63bSGleb Smirnoff break; 12195e4bc63bSGleb Smirnoff case EXT_JUMBO9: 12205e4bc63bSGleb Smirnoff uma_zfree(zone_jumbo9, m->m_ext.ext_buf); 12210a718a6eSMateusz Guzik m_free_raw(mref); 12225e4bc63bSGleb Smirnoff break; 12235e4bc63bSGleb Smirnoff case EXT_JUMBO16: 12245e4bc63bSGleb Smirnoff uma_zfree(zone_jumbo16, m->m_ext.ext_buf); 12250a718a6eSMateusz Guzik m_free_raw(mref); 122656a5f52eSGleb Smirnoff break; 122756a5f52eSGleb Smirnoff case EXT_SFBUF: 12285e4bc63bSGleb Smirnoff case EXT_NET_DRV: 122951346bd5SJohn Baldwin case EXT_CTL: 12305e4bc63bSGleb Smirnoff case EXT_MOD_TYPE: 12315e4bc63bSGleb Smirnoff case EXT_DISPOSABLE: 123207e87a1dSGleb Smirnoff KASSERT(mref->m_ext.ext_free != NULL, 12330ea37a86SGleb Smirnoff ("%s: ext_free not set", __func__)); 123407e87a1dSGleb Smirnoff mref->m_ext.ext_free(mref); 12350a718a6eSMateusz Guzik m_free_raw(mref); 12360ea37a86SGleb Smirnoff break; 12375e4bc63bSGleb Smirnoff case EXT_EXTREF: 12385e4bc63bSGleb Smirnoff KASSERT(m->m_ext.ext_free != NULL, 12395e4bc63bSGleb Smirnoff ("%s: ext_free not set", __func__)); 1240e8fd18f3SGleb Smirnoff m->m_ext.ext_free(m); 12415e4bc63bSGleb Smirnoff break; 1242eec189c7SGleb Smirnoff case EXT_RXRING: 1243eec189c7SGleb Smirnoff KASSERT(m->m_ext.ext_free == NULL, 1244eec189c7SGleb Smirnoff ("%s: ext_free is set", __func__)); 1245eec189c7SGleb Smirnoff break; 12465e4bc63bSGleb Smirnoff default: 12475e4bc63bSGleb Smirnoff KASSERT(m->m_ext.ext_type == 0, 12485e4bc63bSGleb Smirnoff ("%s: unknown ext_type", __func__)); 12495e4bc63bSGleb Smirnoff } 12505e4bc63bSGleb Smirnoff } 12515e4bc63bSGleb Smirnoff 125256a5f52eSGleb Smirnoff if (freembuf && m != mref) 12530a718a6eSMateusz Guzik m_free_raw(m); 12545e4bc63bSGleb Smirnoff } 12555e4bc63bSGleb Smirnoff 12565e4bc63bSGleb Smirnoff /* 125761664ee7SGleb Smirnoff * Clean up after mbufs with M_EXTPG storage attached to them if the 125861664ee7SGleb Smirnoff * reference count hits 1. 125961664ee7SGleb Smirnoff */ 126061664ee7SGleb Smirnoff void 126161664ee7SGleb Smirnoff mb_free_extpg(struct mbuf *m) 126261664ee7SGleb Smirnoff { 126361664ee7SGleb Smirnoff volatile u_int *refcnt; 126461664ee7SGleb Smirnoff struct mbuf *mref; 126561664ee7SGleb Smirnoff 126661664ee7SGleb Smirnoff M_ASSERTEXTPG(m); 126761664ee7SGleb Smirnoff 126861664ee7SGleb Smirnoff /* See if this is the mbuf that holds the embedded refcount. */ 126961664ee7SGleb Smirnoff if (m->m_ext.ext_flags & EXT_FLAG_EMBREF) { 127061664ee7SGleb Smirnoff refcnt = &m->m_ext.ext_count; 127161664ee7SGleb Smirnoff mref = m; 127261664ee7SGleb Smirnoff } else { 127361664ee7SGleb Smirnoff KASSERT(m->m_ext.ext_cnt != NULL, 127461664ee7SGleb Smirnoff ("%s: no refcounting pointer on %p", __func__, m)); 127561664ee7SGleb Smirnoff refcnt = m->m_ext.ext_cnt; 127661664ee7SGleb Smirnoff mref = __containerof(refcnt, struct mbuf, m_ext.ext_count); 127761664ee7SGleb Smirnoff } 127861664ee7SGleb Smirnoff 127961664ee7SGleb Smirnoff /* Free attached storage if this mbuf is the only reference to it. */ 128061664ee7SGleb Smirnoff if (*refcnt == 1 || atomic_fetchadd_int(refcnt, -1) == 1) { 128161664ee7SGleb Smirnoff KASSERT(mref->m_ext.ext_free != NULL, 128261664ee7SGleb Smirnoff ("%s: ext_free not set", __func__)); 128361664ee7SGleb Smirnoff 128461664ee7SGleb Smirnoff mref->m_ext.ext_free(mref); 128561664ee7SGleb Smirnoff #ifdef KERN_TLS 128661664ee7SGleb Smirnoff if (mref->m_epg_tls != NULL && 128761664ee7SGleb Smirnoff !refcount_release_if_not_last(&mref->m_epg_tls->refcount)) 128861664ee7SGleb Smirnoff ktls_enqueue_to_free(mref); 128961664ee7SGleb Smirnoff else 129061664ee7SGleb Smirnoff #endif 12910a718a6eSMateusz Guzik m_free_raw(mref); 129261664ee7SGleb Smirnoff } 129361664ee7SGleb Smirnoff 129461664ee7SGleb Smirnoff if (m != mref) 12950a718a6eSMateusz Guzik m_free_raw(m); 129661664ee7SGleb Smirnoff } 129761664ee7SGleb Smirnoff 129861664ee7SGleb Smirnoff /* 12995e4bc63bSGleb Smirnoff * Official mbuf(9) allocation KPI for stack and drivers: 13005e4bc63bSGleb Smirnoff * 13015e4bc63bSGleb Smirnoff * m_get() - a single mbuf without any attachments, sys/mbuf.h. 13025e4bc63bSGleb Smirnoff * m_gethdr() - a single mbuf initialized as M_PKTHDR, sys/mbuf.h. 13035e4bc63bSGleb Smirnoff * m_getcl() - an mbuf + 2k cluster, sys/mbuf.h. 13045e4bc63bSGleb Smirnoff * m_clget() - attach cluster to already allocated mbuf. 13055e4bc63bSGleb Smirnoff * m_cljget() - attach jumbo cluster to already allocated mbuf. 13065e4bc63bSGleb Smirnoff * m_get2() - allocate minimum mbuf that would fit size argument. 13075e4bc63bSGleb Smirnoff * m_getm2() - allocate a chain of mbufs/clusters. 13085e4bc63bSGleb Smirnoff * m_extadd() - attach external cluster to mbuf. 13095e4bc63bSGleb Smirnoff * 13105e4bc63bSGleb Smirnoff * m_free() - free single mbuf with its tags and ext, sys/mbuf.h. 13115e4bc63bSGleb Smirnoff * m_freem() - free chain of mbufs. 13125e4bc63bSGleb Smirnoff */ 13135e4bc63bSGleb Smirnoff 13145e4bc63bSGleb Smirnoff int 13155e4bc63bSGleb Smirnoff m_clget(struct mbuf *m, int how) 13165e4bc63bSGleb Smirnoff { 13175e4bc63bSGleb Smirnoff 13185e4bc63bSGleb Smirnoff KASSERT((m->m_flags & M_EXT) == 0, ("%s: mbuf %p has M_EXT", 13195e4bc63bSGleb Smirnoff __func__, m)); 13205e4bc63bSGleb Smirnoff m->m_ext.ext_buf = (char *)NULL; 13215e4bc63bSGleb Smirnoff uma_zalloc_arg(zone_clust, m, how); 13225e4bc63bSGleb Smirnoff /* 13235e4bc63bSGleb Smirnoff * On a cluster allocation failure, drain the packet zone and retry, 13245e4bc63bSGleb Smirnoff * we might be able to loosen a few clusters up on the drain. 13255e4bc63bSGleb Smirnoff */ 13265e4bc63bSGleb Smirnoff if ((how & M_NOWAIT) && (m->m_ext.ext_buf == NULL)) { 132708cfa56eSMark Johnston uma_zone_reclaim(zone_pack, UMA_RECLAIM_DRAIN); 13285e4bc63bSGleb Smirnoff uma_zalloc_arg(zone_clust, m, how); 13295e4bc63bSGleb Smirnoff } 1330480f4e94SGeorge V. Neville-Neil MBUF_PROBE2(m__clget, m, how); 13315e4bc63bSGleb Smirnoff return (m->m_flags & M_EXT); 13325e4bc63bSGleb Smirnoff } 13335e4bc63bSGleb Smirnoff 13345e4bc63bSGleb Smirnoff /* 13355e4bc63bSGleb Smirnoff * m_cljget() is different from m_clget() as it can allocate clusters without 13365e4bc63bSGleb Smirnoff * attaching them to an mbuf. In that case the return value is the pointer 13375e4bc63bSGleb Smirnoff * to the cluster of the requested size. If an mbuf was specified, it gets 13385e4bc63bSGleb Smirnoff * the cluster attached to it and the return value can be safely ignored. 13395e4bc63bSGleb Smirnoff * For size it takes MCLBYTES, MJUMPAGESIZE, MJUM9BYTES, MJUM16BYTES. 13405e4bc63bSGleb Smirnoff */ 13415e4bc63bSGleb Smirnoff void * 13425e4bc63bSGleb Smirnoff m_cljget(struct mbuf *m, int how, int size) 13435e4bc63bSGleb Smirnoff { 13445e4bc63bSGleb Smirnoff uma_zone_t zone; 1345480f4e94SGeorge V. Neville-Neil void *retval; 13465e4bc63bSGleb Smirnoff 13475e4bc63bSGleb Smirnoff if (m != NULL) { 13485e4bc63bSGleb Smirnoff KASSERT((m->m_flags & M_EXT) == 0, ("%s: mbuf %p has M_EXT", 13495e4bc63bSGleb Smirnoff __func__, m)); 13505e4bc63bSGleb Smirnoff m->m_ext.ext_buf = NULL; 13515e4bc63bSGleb Smirnoff } 13525e4bc63bSGleb Smirnoff 13535e4bc63bSGleb Smirnoff zone = m_getzone(size); 1354480f4e94SGeorge V. Neville-Neil retval = uma_zalloc_arg(zone, m, how); 1355480f4e94SGeorge V. Neville-Neil 1356480f4e94SGeorge V. Neville-Neil MBUF_PROBE4(m__cljget, m, how, size, retval); 1357480f4e94SGeorge V. Neville-Neil 1358480f4e94SGeorge V. Neville-Neil return (retval); 13595e4bc63bSGleb Smirnoff } 13605e4bc63bSGleb Smirnoff 13615e4bc63bSGleb Smirnoff /* 13625e4bc63bSGleb Smirnoff * m_get2() allocates minimum mbuf that would fit "size" argument. 13635e4bc63bSGleb Smirnoff */ 13645e4bc63bSGleb Smirnoff struct mbuf * 13655e4bc63bSGleb Smirnoff m_get2(int size, int how, short type, int flags) 13665e4bc63bSGleb Smirnoff { 13675e4bc63bSGleb Smirnoff struct mb_args args; 13685e4bc63bSGleb Smirnoff struct mbuf *m, *n; 13695e4bc63bSGleb Smirnoff 13705e4bc63bSGleb Smirnoff args.flags = flags; 13715e4bc63bSGleb Smirnoff args.type = type; 13725e4bc63bSGleb Smirnoff 13735e4bc63bSGleb Smirnoff if (size <= MHLEN || (size <= MLEN && (flags & M_PKTHDR) == 0)) 13745e4bc63bSGleb Smirnoff return (uma_zalloc_arg(zone_mbuf, &args, how)); 13755e4bc63bSGleb Smirnoff if (size <= MCLBYTES) 13765e4bc63bSGleb Smirnoff return (uma_zalloc_arg(zone_pack, &args, how)); 13775e4bc63bSGleb Smirnoff 13785e4bc63bSGleb Smirnoff if (size > MJUMPAGESIZE) 13795e4bc63bSGleb Smirnoff return (NULL); 13805e4bc63bSGleb Smirnoff 13815e4bc63bSGleb Smirnoff m = uma_zalloc_arg(zone_mbuf, &args, how); 13825e4bc63bSGleb Smirnoff if (m == NULL) 13835e4bc63bSGleb Smirnoff return (NULL); 13845e4bc63bSGleb Smirnoff 13855e4bc63bSGleb Smirnoff n = uma_zalloc_arg(zone_jumbop, m, how); 13865e4bc63bSGleb Smirnoff if (n == NULL) { 13870a718a6eSMateusz Guzik m_free_raw(m); 13885e4bc63bSGleb Smirnoff return (NULL); 13895e4bc63bSGleb Smirnoff } 13905e4bc63bSGleb Smirnoff 13915e4bc63bSGleb Smirnoff return (m); 13925e4bc63bSGleb Smirnoff } 13935e4bc63bSGleb Smirnoff 13945e4bc63bSGleb Smirnoff /* 1395a051ca72SKristof Provost * m_get3() allocates minimum mbuf that would fit "size" argument. 1396a051ca72SKristof Provost * Unlike m_get2() it can allocate clusters up to MJUM16BYTES. 1397a051ca72SKristof Provost */ 1398a051ca72SKristof Provost struct mbuf * 1399a051ca72SKristof Provost m_get3(int size, int how, short type, int flags) 1400a051ca72SKristof Provost { 1401a051ca72SKristof Provost struct mb_args args; 1402a051ca72SKristof Provost struct mbuf *m, *n; 1403a051ca72SKristof Provost uma_zone_t zone; 1404a051ca72SKristof Provost 1405a051ca72SKristof Provost if (size <= MJUMPAGESIZE) 1406a051ca72SKristof Provost return (m_get2(size, how, type, flags)); 1407a051ca72SKristof Provost 1408a051ca72SKristof Provost if (size > MJUM16BYTES) 1409a051ca72SKristof Provost return (NULL); 1410a051ca72SKristof Provost 1411a051ca72SKristof Provost args.flags = flags; 1412a051ca72SKristof Provost args.type = type; 1413a051ca72SKristof Provost 1414a051ca72SKristof Provost m = uma_zalloc_arg(zone_mbuf, &args, how); 1415a051ca72SKristof Provost if (m == NULL) 1416a051ca72SKristof Provost return (NULL); 1417a051ca72SKristof Provost 1418a051ca72SKristof Provost if (size <= MJUM9BYTES) 1419a051ca72SKristof Provost zone = zone_jumbo9; 1420a051ca72SKristof Provost else 1421a051ca72SKristof Provost zone = zone_jumbo16; 1422a051ca72SKristof Provost 1423b6cbbcaeSKristof Provost n = uma_zalloc_arg(zone, m, how); 1424a051ca72SKristof Provost if (n == NULL) { 1425a051ca72SKristof Provost m_free_raw(m); 1426a051ca72SKristof Provost return (NULL); 1427a051ca72SKristof Provost } 1428a051ca72SKristof Provost 1429a051ca72SKristof Provost return (m); 1430a051ca72SKristof Provost } 1431a051ca72SKristof Provost 1432a051ca72SKristof Provost /* 14335e4bc63bSGleb Smirnoff * m_getjcl() returns an mbuf with a cluster of the specified size attached. 14345e4bc63bSGleb Smirnoff * For size it takes MCLBYTES, MJUMPAGESIZE, MJUM9BYTES, MJUM16BYTES. 14355e4bc63bSGleb Smirnoff */ 14365e4bc63bSGleb Smirnoff struct mbuf * 14375e4bc63bSGleb Smirnoff m_getjcl(int how, short type, int flags, int size) 14385e4bc63bSGleb Smirnoff { 14395e4bc63bSGleb Smirnoff struct mb_args args; 14405e4bc63bSGleb Smirnoff struct mbuf *m, *n; 14415e4bc63bSGleb Smirnoff uma_zone_t zone; 14425e4bc63bSGleb Smirnoff 14435e4bc63bSGleb Smirnoff if (size == MCLBYTES) 14445e4bc63bSGleb Smirnoff return m_getcl(how, type, flags); 14455e4bc63bSGleb Smirnoff 14465e4bc63bSGleb Smirnoff args.flags = flags; 14475e4bc63bSGleb Smirnoff args.type = type; 14485e4bc63bSGleb Smirnoff 14495e4bc63bSGleb Smirnoff m = uma_zalloc_arg(zone_mbuf, &args, how); 14505e4bc63bSGleb Smirnoff if (m == NULL) 14515e4bc63bSGleb Smirnoff return (NULL); 14525e4bc63bSGleb Smirnoff 14535e4bc63bSGleb Smirnoff zone = m_getzone(size); 14545e4bc63bSGleb Smirnoff n = uma_zalloc_arg(zone, m, how); 14555e4bc63bSGleb Smirnoff if (n == NULL) { 14560a718a6eSMateusz Guzik m_free_raw(m); 14575e4bc63bSGleb Smirnoff return (NULL); 14585e4bc63bSGleb Smirnoff } 1459edde7a53SAndrey V. Elsukov MBUF_PROBE5(m__getjcl, how, type, flags, size, m); 14605e4bc63bSGleb Smirnoff return (m); 14615e4bc63bSGleb Smirnoff } 14625e4bc63bSGleb Smirnoff 14635e4bc63bSGleb Smirnoff /* 146471f8702fSGleb Smirnoff * Allocate mchain of a given length of mbufs and/or clusters (whatever fits 146571f8702fSGleb Smirnoff * best). May fail due to ENOMEM. In case of failure state of mchain is 146671f8702fSGleb Smirnoff * inconsistent. 14675e4bc63bSGleb Smirnoff */ 146871f8702fSGleb Smirnoff int 146971f8702fSGleb Smirnoff mc_get(struct mchain *mc, u_int length, int how, short type, int flags) 14705e4bc63bSGleb Smirnoff { 147171f8702fSGleb Smirnoff struct mbuf *mb; 147271f8702fSGleb Smirnoff u_int progress; 14735e4bc63bSGleb Smirnoff 147471f8702fSGleb Smirnoff MPASS(length >= 0); 14755e4bc63bSGleb Smirnoff 147671f8702fSGleb Smirnoff *mc = MCHAIN_INITIALIZER(mc); 14775e4bc63bSGleb Smirnoff flags &= (M_PKTHDR | M_EOR); 147871f8702fSGleb Smirnoff progress = 0; 14795e4bc63bSGleb Smirnoff 14805e4bc63bSGleb Smirnoff /* Loop and append maximum sized mbufs to the chain tail. */ 148171f8702fSGleb Smirnoff do { 148271f8702fSGleb Smirnoff if (length - progress > MCLBYTES) { 148371f8702fSGleb Smirnoff /* 148471f8702fSGleb Smirnoff * M_NOWAIT here is intentional, it avoids blocking if 148571f8702fSGleb Smirnoff * the jumbop zone is exhausted. See 796d4eb89e2c and 148671f8702fSGleb Smirnoff * D26150 for more detail. 148771f8702fSGleb Smirnoff */ 1488796d4eb8SAndrew Gallatin mb = m_getjcl(M_NOWAIT, type, (flags & M_PKTHDR), 14895e4bc63bSGleb Smirnoff MJUMPAGESIZE); 149071f8702fSGleb Smirnoff } else 149171f8702fSGleb Smirnoff mb = NULL; 1492796d4eb8SAndrew Gallatin if (mb == NULL) { 149371f8702fSGleb Smirnoff if (length - progress >= MINCLSIZE) 14945e4bc63bSGleb Smirnoff mb = m_getcl(how, type, (flags & M_PKTHDR)); 14955e4bc63bSGleb Smirnoff else if (flags & M_PKTHDR) 14965e4bc63bSGleb Smirnoff mb = m_gethdr(how, type); 14975e4bc63bSGleb Smirnoff else 14985e4bc63bSGleb Smirnoff mb = m_get(how, type); 14995e4bc63bSGleb Smirnoff 1500796d4eb8SAndrew Gallatin /* 1501796d4eb8SAndrew Gallatin * Fail the whole operation if one mbuf can't be 1502796d4eb8SAndrew Gallatin * allocated. 1503796d4eb8SAndrew Gallatin */ 15045e4bc63bSGleb Smirnoff if (mb == NULL) { 150571f8702fSGleb Smirnoff m_freem(mc_first(mc)); 150671f8702fSGleb Smirnoff return (ENOMEM); 15075e4bc63bSGleb Smirnoff } 1508796d4eb8SAndrew Gallatin } 15095e4bc63bSGleb Smirnoff 151071f8702fSGleb Smirnoff progress += M_SIZE(mb); 151171f8702fSGleb Smirnoff mc_append(mc, mb); 151271f8702fSGleb Smirnoff /* Only valid on the first mbuf. */ 151371f8702fSGleb Smirnoff flags &= ~M_PKTHDR; 151471f8702fSGleb Smirnoff } while (progress < length); 15155e4bc63bSGleb Smirnoff if (flags & M_EOR) 151671f8702fSGleb Smirnoff /* Only valid on the last mbuf. */ 151771f8702fSGleb Smirnoff mc_last(mc)->m_flags |= M_EOR; 151871f8702fSGleb Smirnoff 151971f8702fSGleb Smirnoff return (0); 152071f8702fSGleb Smirnoff } 152171f8702fSGleb Smirnoff 152271f8702fSGleb Smirnoff /* 152371f8702fSGleb Smirnoff * Allocate a given length worth of mbufs and/or clusters (whatever fits 152471f8702fSGleb Smirnoff * best) and return a pointer to the top of the allocated chain. If an 152571f8702fSGleb Smirnoff * existing mbuf chain is provided, then we will append the new chain 152671f8702fSGleb Smirnoff * to the existing one and return a pointer to the provided mbuf. 152771f8702fSGleb Smirnoff */ 152871f8702fSGleb Smirnoff struct mbuf * 152971f8702fSGleb Smirnoff m_getm2(struct mbuf *m, int len, int how, short type, int flags) 153071f8702fSGleb Smirnoff { 153171f8702fSGleb Smirnoff struct mchain mc; 153271f8702fSGleb Smirnoff 153371f8702fSGleb Smirnoff /* Packet header mbuf must be first in chain. */ 153471f8702fSGleb Smirnoff if (m != NULL && (flags & M_PKTHDR)) 153571f8702fSGleb Smirnoff flags &= ~M_PKTHDR; 153671f8702fSGleb Smirnoff 153771f8702fSGleb Smirnoff if (__predict_false(mc_get(&mc, len, how, type, flags) != 0)) 153871f8702fSGleb Smirnoff return (NULL); 15395e4bc63bSGleb Smirnoff 15405e4bc63bSGleb Smirnoff /* If mbuf was supplied, append new chain to the end of it. */ 15415e4bc63bSGleb Smirnoff if (m != NULL) { 154271f8702fSGleb Smirnoff struct mbuf *mtail; 154371f8702fSGleb Smirnoff 154471f8702fSGleb Smirnoff mtail = m_last(m); 154571f8702fSGleb Smirnoff mtail->m_next = mc_first(&mc); 15465e4bc63bSGleb Smirnoff mtail->m_flags &= ~M_EOR; 15475e4bc63bSGleb Smirnoff } else 154871f8702fSGleb Smirnoff m = mc_first(&mc); 15495e4bc63bSGleb Smirnoff 15505e4bc63bSGleb Smirnoff return (m); 15515e4bc63bSGleb Smirnoff } 15525e4bc63bSGleb Smirnoff 15535e4bc63bSGleb Smirnoff /*- 15545e4bc63bSGleb Smirnoff * Configure a provided mbuf to refer to the provided external storage 155556a5f52eSGleb Smirnoff * buffer and setup a reference count for said buffer. 15565e4bc63bSGleb Smirnoff * 15575e4bc63bSGleb Smirnoff * Arguments: 15585e4bc63bSGleb Smirnoff * mb The existing mbuf to which to attach the provided buffer. 15595e4bc63bSGleb Smirnoff * buf The address of the provided external storage buffer. 15605e4bc63bSGleb Smirnoff * size The size of the provided buffer. 15615e4bc63bSGleb Smirnoff * freef A pointer to a routine that is responsible for freeing the 15625e4bc63bSGleb Smirnoff * provided external storage buffer. 15635e4bc63bSGleb Smirnoff * args A pointer to an argument structure (of any type) to be passed 15645e4bc63bSGleb Smirnoff * to the provided freef routine (may be NULL). 15655e4bc63bSGleb Smirnoff * flags Any other flags to be passed to the provided mbuf. 15665e4bc63bSGleb Smirnoff * type The type that the external storage buffer should be 15675e4bc63bSGleb Smirnoff * labeled with. 15685e4bc63bSGleb Smirnoff * 15695e4bc63bSGleb Smirnoff * Returns: 15705e4bc63bSGleb Smirnoff * Nothing. 15715e4bc63bSGleb Smirnoff */ 157256a5f52eSGleb Smirnoff void 1573e8fd18f3SGleb Smirnoff m_extadd(struct mbuf *mb, char *buf, u_int size, m_ext_free_t freef, 1574e8fd18f3SGleb Smirnoff void *arg1, void *arg2, int flags, int type) 15755e4bc63bSGleb Smirnoff { 157656a5f52eSGleb Smirnoff 15775e4bc63bSGleb Smirnoff KASSERT(type != EXT_CLUSTER, ("%s: EXT_CLUSTER not allowed", __func__)); 15785e4bc63bSGleb Smirnoff 15795e4bc63bSGleb Smirnoff mb->m_flags |= (M_EXT | flags); 15805e4bc63bSGleb Smirnoff mb->m_ext.ext_buf = buf; 15815e4bc63bSGleb Smirnoff mb->m_data = mb->m_ext.ext_buf; 15825e4bc63bSGleb Smirnoff mb->m_ext.ext_size = size; 15835e4bc63bSGleb Smirnoff mb->m_ext.ext_free = freef; 15845e4bc63bSGleb Smirnoff mb->m_ext.ext_arg1 = arg1; 15855e4bc63bSGleb Smirnoff mb->m_ext.ext_arg2 = arg2; 15865e4bc63bSGleb Smirnoff mb->m_ext.ext_type = type; 15875e4bc63bSGleb Smirnoff 158856a5f52eSGleb Smirnoff if (type != EXT_EXTREF) { 158956a5f52eSGleb Smirnoff mb->m_ext.ext_count = 1; 159056a5f52eSGleb Smirnoff mb->m_ext.ext_flags = EXT_FLAG_EMBREF; 159156a5f52eSGleb Smirnoff } else 159256a5f52eSGleb Smirnoff mb->m_ext.ext_flags = 0; 15935e4bc63bSGleb Smirnoff } 15945e4bc63bSGleb Smirnoff 15955e4bc63bSGleb Smirnoff /* 15965e4bc63bSGleb Smirnoff * Free an entire chain of mbufs and associated external buffers, if 15975e4bc63bSGleb Smirnoff * applicable. 15985e4bc63bSGleb Smirnoff */ 15995e4bc63bSGleb Smirnoff void 16005e4bc63bSGleb Smirnoff m_freem(struct mbuf *mb) 16015e4bc63bSGleb Smirnoff { 16025e4bc63bSGleb Smirnoff 1603c8f59118SGleb Smirnoff MBUF_PROBE1(m__freem, mb); 16045e4bc63bSGleb Smirnoff while (mb != NULL) 16055e4bc63bSGleb Smirnoff mb = m_free(mb); 16065e4bc63bSGleb Smirnoff } 1607fb3bc596SJohn Baldwin 160805462babSMateusz Guzik /* 1609badf44ccSGleb Smirnoff * Free an entire chain of mbufs and associated external buffers, following 1610badf44ccSGleb Smirnoff * both m_next and m_nextpkt linkage. 1611badf44ccSGleb Smirnoff * Note: doesn't support NULL argument. 1612badf44ccSGleb Smirnoff */ 1613badf44ccSGleb Smirnoff void 1614badf44ccSGleb Smirnoff m_freemp(struct mbuf *m) 1615badf44ccSGleb Smirnoff { 1616badf44ccSGleb Smirnoff struct mbuf *n; 1617badf44ccSGleb Smirnoff 1618badf44ccSGleb Smirnoff MBUF_PROBE1(m__freemp, m); 1619badf44ccSGleb Smirnoff do { 1620badf44ccSGleb Smirnoff n = m->m_nextpkt; 1621badf44ccSGleb Smirnoff while (m != NULL) 1622badf44ccSGleb Smirnoff m = m_free(m); 1623badf44ccSGleb Smirnoff m = n; 1624badf44ccSGleb Smirnoff } while (m != NULL); 1625badf44ccSGleb Smirnoff } 1626badf44ccSGleb Smirnoff 1627badf44ccSGleb Smirnoff /* 162805462babSMateusz Guzik * Temporary primitive to allow freeing without going through m_free. 162905462babSMateusz Guzik */ 163005462babSMateusz Guzik void 163105462babSMateusz Guzik m_free_raw(struct mbuf *mb) 163205462babSMateusz Guzik { 163305462babSMateusz Guzik 163405462babSMateusz Guzik uma_zfree(zone_mbuf, mb); 163505462babSMateusz Guzik } 163605462babSMateusz Guzik 163736e0a362SJohn Baldwin int 163836e0a362SJohn Baldwin m_snd_tag_alloc(struct ifnet *ifp, union if_snd_tag_alloc_params *params, 163936e0a362SJohn Baldwin struct m_snd_tag **mstp) 164036e0a362SJohn Baldwin { 164136e0a362SJohn Baldwin 16421e6131baSJustin Hibbits return (if_snd_tag_alloc(ifp, params, mstp)); 164336e0a362SJohn Baldwin } 164436e0a362SJohn Baldwin 1645fb3bc596SJohn Baldwin void 1646c782ea8bSJohn Baldwin m_snd_tag_init(struct m_snd_tag *mst, struct ifnet *ifp, 1647c782ea8bSJohn Baldwin const struct if_snd_tag_sw *sw) 1648fb3bc596SJohn Baldwin { 1649fb3bc596SJohn Baldwin 1650fb3bc596SJohn Baldwin if_ref(ifp); 1651fb3bc596SJohn Baldwin mst->ifp = ifp; 1652fb3bc596SJohn Baldwin refcount_init(&mst->refcount, 1); 1653c782ea8bSJohn Baldwin mst->sw = sw; 1654fb3bc596SJohn Baldwin counter_u64_add(snd_tag_count, 1); 1655fb3bc596SJohn Baldwin } 1656fb3bc596SJohn Baldwin 1657fb3bc596SJohn Baldwin void 1658fb3bc596SJohn Baldwin m_snd_tag_destroy(struct m_snd_tag *mst) 1659fb3bc596SJohn Baldwin { 1660fb3bc596SJohn Baldwin struct ifnet *ifp; 1661fb3bc596SJohn Baldwin 1662fb3bc596SJohn Baldwin ifp = mst->ifp; 1663c782ea8bSJohn Baldwin mst->sw->snd_tag_free(mst); 1664fb3bc596SJohn Baldwin if_rele(ifp); 1665fb3bc596SJohn Baldwin counter_u64_add(snd_tag_count, -1); 1666fb3bc596SJohn Baldwin } 166784d746deSRick Macklem 16684d7a1361SGleb Smirnoff void 16694d7a1361SGleb Smirnoff m_rcvif_serialize(struct mbuf *m) 16704d7a1361SGleb Smirnoff { 16714d7a1361SGleb Smirnoff u_short idx, gen; 16724d7a1361SGleb Smirnoff 16734d7a1361SGleb Smirnoff M_ASSERTPKTHDR(m); 16741e6131baSJustin Hibbits idx = if_getindex(m->m_pkthdr.rcvif); 16751e6131baSJustin Hibbits gen = if_getidxgen(m->m_pkthdr.rcvif); 16764d7a1361SGleb Smirnoff m->m_pkthdr.rcvidx = idx; 16774d7a1361SGleb Smirnoff m->m_pkthdr.rcvgen = gen; 16784d88d81cSHans Petter Selasky if (__predict_false(m->m_pkthdr.leaf_rcvif != NULL)) { 16791e6131baSJustin Hibbits idx = if_getindex(m->m_pkthdr.leaf_rcvif); 16801e6131baSJustin Hibbits gen = if_getidxgen(m->m_pkthdr.leaf_rcvif); 16814d88d81cSHans Petter Selasky } else { 16824d88d81cSHans Petter Selasky idx = -1; 16834d88d81cSHans Petter Selasky gen = 0; 16844d88d81cSHans Petter Selasky } 16854d88d81cSHans Petter Selasky m->m_pkthdr.leaf_rcvidx = idx; 16864d88d81cSHans Petter Selasky m->m_pkthdr.leaf_rcvgen = gen; 16874d7a1361SGleb Smirnoff } 16884d7a1361SGleb Smirnoff 16894d7a1361SGleb Smirnoff struct ifnet * 16904d7a1361SGleb Smirnoff m_rcvif_restore(struct mbuf *m) 16914d7a1361SGleb Smirnoff { 16924d88d81cSHans Petter Selasky struct ifnet *ifp, *leaf_ifp; 16934d7a1361SGleb Smirnoff 16944d7a1361SGleb Smirnoff M_ASSERTPKTHDR(m); 1695613acc64SKristof Provost NET_EPOCH_ASSERT(); 16964d7a1361SGleb Smirnoff 1697613acc64SKristof Provost ifp = ifnet_byindexgen(m->m_pkthdr.rcvidx, m->m_pkthdr.rcvgen); 169864727619SJustin Hibbits if (ifp == NULL || (if_getflags(ifp) & IFF_DYING)) 1699613acc64SKristof Provost return (NULL); 1700613acc64SKristof Provost 17014d88d81cSHans Petter Selasky if (__predict_true(m->m_pkthdr.leaf_rcvidx == (u_short)-1)) { 17024d88d81cSHans Petter Selasky leaf_ifp = NULL; 17034d88d81cSHans Petter Selasky } else { 17044d88d81cSHans Petter Selasky leaf_ifp = ifnet_byindexgen(m->m_pkthdr.leaf_rcvidx, 17054d88d81cSHans Petter Selasky m->m_pkthdr.leaf_rcvgen); 170664727619SJustin Hibbits if (__predict_false(leaf_ifp != NULL && (if_getflags(leaf_ifp) & IFF_DYING))) 17074d88d81cSHans Petter Selasky leaf_ifp = NULL; 17084d88d81cSHans Petter Selasky } 17094d88d81cSHans Petter Selasky 17104d88d81cSHans Petter Selasky m->m_pkthdr.leaf_rcvif = leaf_ifp; 17114d88d81cSHans Petter Selasky m->m_pkthdr.rcvif = ifp; 17124d88d81cSHans Petter Selasky 17134d88d81cSHans Petter Selasky return (ifp); 17144d7a1361SGleb Smirnoff } 17154d7a1361SGleb Smirnoff 171684d746deSRick Macklem /* 171784d746deSRick Macklem * Allocate an mbuf with anonymous external pages. 171884d746deSRick Macklem */ 171984d746deSRick Macklem struct mbuf * 172084d746deSRick Macklem mb_alloc_ext_plus_pages(int len, int how) 172184d746deSRick Macklem { 172284d746deSRick Macklem struct mbuf *m; 172384d746deSRick Macklem vm_page_t pg; 172484d746deSRick Macklem int i, npgs; 172584d746deSRick Macklem 1726314cb279SJohn Baldwin m = mb_alloc_ext_pgs(how, mb_free_mext_pgs, 0); 172784d746deSRick Macklem if (m == NULL) 172884d746deSRick Macklem return (NULL); 172984d746deSRick Macklem m->m_epg_flags |= EPG_FLAG_ANON; 173084d746deSRick Macklem npgs = howmany(len, PAGE_SIZE); 173184d746deSRick Macklem for (i = 0; i < npgs; i++) { 173284d746deSRick Macklem do { 1733a4667e09SMark Johnston pg = vm_page_alloc_noobj(VM_ALLOC_NODUMP | 1734a4667e09SMark Johnston VM_ALLOC_WIRED); 173584d746deSRick Macklem if (pg == NULL) { 173684d746deSRick Macklem if (how == M_NOWAIT) { 173784d746deSRick Macklem m->m_epg_npgs = i; 173884d746deSRick Macklem m_free(m); 173984d746deSRick Macklem return (NULL); 174084d746deSRick Macklem } 174184d746deSRick Macklem vm_wait(NULL); 174284d746deSRick Macklem } 174384d746deSRick Macklem } while (pg == NULL); 174484d746deSRick Macklem m->m_epg_pa[i] = VM_PAGE_TO_PHYS(pg); 174584d746deSRick Macklem } 174684d746deSRick Macklem m->m_epg_npgs = npgs; 174784d746deSRick Macklem return (m); 174884d746deSRick Macklem } 174984d746deSRick Macklem 175084d746deSRick Macklem /* 175184d746deSRick Macklem * Copy the data in the mbuf chain to a chain of mbufs with anonymous external 175284d746deSRick Macklem * unmapped pages. 175384d746deSRick Macklem * len is the length of data in the input mbuf chain. 175484d746deSRick Macklem * mlen is the maximum number of bytes put into each ext_page mbuf. 175584d746deSRick Macklem */ 175684d746deSRick Macklem struct mbuf * 175784d746deSRick Macklem mb_mapped_to_unmapped(struct mbuf *mp, int len, int mlen, int how, 175884d746deSRick Macklem struct mbuf **mlast) 175984d746deSRick Macklem { 176084d746deSRick Macklem struct mbuf *m, *mout; 176184d746deSRick Macklem char *pgpos, *mbpos; 176284d746deSRick Macklem int i, mblen, mbufsiz, pglen, xfer; 176384d746deSRick Macklem 176484d746deSRick Macklem if (len == 0) 176584d746deSRick Macklem return (NULL); 176684d746deSRick Macklem mbufsiz = min(mlen, len); 176784d746deSRick Macklem m = mout = mb_alloc_ext_plus_pages(mbufsiz, how); 176884d746deSRick Macklem if (m == NULL) 176984d746deSRick Macklem return (m); 177084d746deSRick Macklem pgpos = (char *)(void *)PHYS_TO_DMAP(m->m_epg_pa[0]); 177184d746deSRick Macklem pglen = PAGE_SIZE; 177284d746deSRick Macklem mblen = 0; 177384d746deSRick Macklem i = 0; 177484d746deSRick Macklem do { 177584d746deSRick Macklem if (pglen == 0) { 177684d746deSRick Macklem if (++i == m->m_epg_npgs) { 177784d746deSRick Macklem m->m_epg_last_len = PAGE_SIZE; 177884d746deSRick Macklem mbufsiz = min(mlen, len); 177984d746deSRick Macklem m->m_next = mb_alloc_ext_plus_pages(mbufsiz, 178084d746deSRick Macklem how); 178184d746deSRick Macklem m = m->m_next; 178284d746deSRick Macklem if (m == NULL) { 178384d746deSRick Macklem m_freem(mout); 178484d746deSRick Macklem return (m); 178584d746deSRick Macklem } 178684d746deSRick Macklem i = 0; 178784d746deSRick Macklem } 178884d746deSRick Macklem pgpos = (char *)(void *)PHYS_TO_DMAP(m->m_epg_pa[i]); 178984d746deSRick Macklem pglen = PAGE_SIZE; 179084d746deSRick Macklem } 179184d746deSRick Macklem while (mblen == 0) { 179284d746deSRick Macklem if (mp == NULL) { 179384d746deSRick Macklem m_freem(mout); 179484d746deSRick Macklem return (NULL); 179584d746deSRick Macklem } 179684d746deSRick Macklem KASSERT((mp->m_flags & M_EXTPG) == 0, 179784d746deSRick Macklem ("mb_copym_ext_pgs: ext_pgs input mbuf")); 179884d746deSRick Macklem mbpos = mtod(mp, char *); 179984d746deSRick Macklem mblen = mp->m_len; 180084d746deSRick Macklem mp = mp->m_next; 180184d746deSRick Macklem } 180284d746deSRick Macklem xfer = min(mblen, pglen); 180384d746deSRick Macklem memcpy(pgpos, mbpos, xfer); 180484d746deSRick Macklem pgpos += xfer; 180584d746deSRick Macklem mbpos += xfer; 180684d746deSRick Macklem pglen -= xfer; 180784d746deSRick Macklem mblen -= xfer; 180884d746deSRick Macklem len -= xfer; 180984d746deSRick Macklem m->m_len += xfer; 181084d746deSRick Macklem } while (len > 0); 181184d746deSRick Macklem m->m_epg_last_len = PAGE_SIZE - pglen; 181284d746deSRick Macklem if (mlast != NULL) 181384d746deSRick Macklem *mlast = m; 181484d746deSRick Macklem return (mout); 181584d746deSRick Macklem } 1816