home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1982, 1986, 1988, 1991 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)uipc_mbuf.c 7.19 (Berkeley) 4/20/91
- */
-
- #include "param.h"
- #include "proc.h"
- #include "malloc.h"
- #include "map.h"
- #define MBTYPES
- #include "mbuf.h"
- #include "kernel.h"
- #include "syslog.h"
- #include "domain.h"
- #include "protosw.h"
- #include "vm/vm.h"
-
- extern vm_map_t mb_map;
- struct mbuf *mbutl;
- char *mclrefcnt;
-
- mbinit()
- {
- int s;
-
- #if CLBYTES < 4096
- #define NCL_INIT (4096/CLBYTES)
- #else
- #define NCL_INIT 1
- #endif
- s = splimp();
- if (m_clalloc(NCL_INIT, M_DONTWAIT) == 0)
- goto bad;
- splx(s);
- return;
- bad:
- panic("mbinit");
- }
-
- /*
- * Allocate some number of mbuf clusters
- * and place on cluster free list.
- * Must be called at splimp.
- */
- /* ARGSUSED */
- m_clalloc(ncl, canwait)
- register int ncl;
- {
- int npg, mbx;
- register caddr_t p;
- register int i;
- static int logged;
-
- npg = ncl * CLSIZE;
- p = (caddr_t)kmem_malloc(mb_map, ctob(npg), canwait);
- if (p == NULL) {
- if (logged == 0) {
- logged++;
- log(LOG_ERR, "mb_map full\n");
- }
- return (0);
- }
- ncl = ncl * CLBYTES / MCLBYTES;
- for (i = 0; i < ncl; i++) {
- ((union mcluster *)p)->mcl_next = mclfree;
- mclfree = (union mcluster *)p;
- p += MCLBYTES;
- mbstat.m_clfree++;
- }
- mbstat.m_clusters += ncl;
- return (1);
- }
-
- /*
- * When MGET failes, ask protocols to free space when short of memory,
- * then re-attempt to allocate an mbuf.
- */
- struct mbuf *
- m_retry(i, t)
- int i, t;
- {
- register struct mbuf *m;
-
- m_reclaim();
- #define m_retry(i, t) (struct mbuf *)0
- MGET(m, i, t);
- #undef m_retry
- return (m);
- }
-
- /*
- * As above; retry an MGETHDR.
- */
- struct mbuf *
- m_retryhdr(i, t)
- int i, t;
- {
- register struct mbuf *m;
-
- m_reclaim();
- #define m_retryhdr(i, t) (struct mbuf *)0
- MGETHDR(m, i, t);
- #undef m_retryhdr
- return (m);
- }
-
- m_reclaim()
- {
- register struct domain *dp;
- register struct protosw *pr;
- int s = splimp();
-
- for (dp = domains; dp; dp = dp->dom_next)
- for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
- if (pr->pr_drain)
- (*pr->pr_drain)();
- splx(s);
- mbstat.m_drain++;
- }
-
- /*
- * Space allocation routines.
- * These are also available as macros
- * for critical paths.
- */
- struct mbuf *
- m_get(canwait, type)
- int canwait, type;
- {
- register struct mbuf *m;
-
- MGET(m, canwait, type);
- return (m);
- }
-
- struct mbuf *
- m_gethdr(canwait, type)
- int canwait, type;
- {
- register struct mbuf *m;
-
- MGETHDR(m, canwait, type);
- return (m);
- }
-
- struct mbuf *
- m_getclr(canwait, type)
- int canwait, type;
- {
- register struct mbuf *m;
-
- MGET(m, canwait, type);
- if (m == 0)
- return (0);
- bzero(mtod(m, caddr_t), MLEN);
- return (m);
- }
-
- struct mbuf *
- m_free(m)
- struct mbuf *m;
- {
- register struct mbuf *n;
-
- MFREE(m, n);
- return (n);
- }
-
- m_freem(m)
- register struct mbuf *m;
- {
- register struct mbuf *n;
-
- if (m == NULL)
- return;
- do {
- MFREE(m, n);
- } while (m = n);
- }
-
- /*
- * Mbuffer utility routines.
- */
-
- /*
- * Lesser-used path for M_PREPEND:
- * allocate new mbuf to prepend to chain,
- * copy junk along.
- */
- struct mbuf *
- m_prepend(m, len, how)
- register struct mbuf *m;
- int len, how;
- {
- struct mbuf *mn;
-
- MGET(mn, how, m->m_type);
- if (mn == (struct mbuf *)NULL) {
- m_freem(m);
- return ((struct mbuf *)NULL);
- }
- if (m->m_flags & M_PKTHDR) {
- M_COPY_PKTHDR(mn, m);
- m->m_flags &= ~M_PKTHDR;
- }
- mn->m_next = m;
- m = mn;
- if (len < MHLEN)
- MH_ALIGN(m, len);
- m->m_len = len;
- return (m);
- }
-
- /*
- * Make a copy of an mbuf chain starting "off0" bytes from the beginning,
- * continuing for "len" bytes. If len is M_COPYALL, copy to end of mbuf.
- * The wait parameter is a choice of M_WAIT/M_DONTWAIT from caller.
- */
- int MCFail;
-
- struct mbuf *
- m_copym(m, off0, len, wait)
- register struct mbuf *m;
- int off0, wait;
- register int len;
- {
- register struct mbuf *n, **np;
- register int off = off0;
- struct mbuf *top;
- int copyhdr = 0;
-
- if (off < 0 || len < 0)
- panic("m_copym");
- if (off == 0 && m->m_flags & M_PKTHDR)
- copyhdr = 1;
- while (off > 0) {
- if (m == 0)
- panic("m_copym");
- if (off < m->m_len)
- break;
- off -= m->m_len;
- m = m->m_next;
- }
- np = ⊤
- top = 0;
- while (len > 0) {
- if (m == 0) {
- if (len != M_COPYALL)
- panic("m_copym");
- break;
- }
- MGET(n, wait, m->m_type);
- *np = n;
- if (n == 0)
- goto nospace;
- if (copyhdr) {
- M_COPY_PKTHDR(n, m);
- if (len == M_COPYALL)
- n->m_pkthdr.len -= off0;
- else
- n->m_pkthdr.len = len;
- copyhdr = 0;
- }
- n->m_len = MIN(len, m->m_len - off);
- if (m->m_flags & M_EXT) {
- n->m_data = m->m_data + off;
- mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
- n->m_ext = m->m_ext;
- n->m_flags |= M_EXT;
- } else
- bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t),
- (unsigned)n->m_len);
- if (len != M_COPYALL)
- len -= n->m_len;
- off = 0;
- m = m->m_next;
- np = &n->m_next;
- }
- if (top == 0)
- MCFail++;
- return (top);
- nospace:
- m_freem(top);
- MCFail++;
- return (0);
- }
-
- /*
- * Copy data from an mbuf chain starting "off" bytes from the beginning,
- * continuing for "len" bytes, into the indicated buffer.
- */
- m_copydata(m, off, len, cp)
- register struct mbuf *m;
- register int off;
- register int len;
- caddr_t cp;
- {
- register unsigned count;
-
- if (off < 0 || len < 0)
- panic("m_copydata");
- while (off > 0) {
- if (m == 0)
- panic("m_copydata");
- if (off < m->m_len)
- break;
- off -= m->m_len;
- m = m->m_next;
- }
- while (len > 0) {
- if (m == 0)
- panic("m_copydata");
- count = MIN(m->m_len - off, len);
- bcopy(mtod(m, caddr_t) + off, cp, count);
- len -= count;
- cp += count;
- off = 0;
- m = m->m_next;
- }
- }
-
- /*
- * Concatenate mbuf chain n to m.
- * Both chains must be of the same type (e.g. MT_DATA).
- * Any m_pkthdr is not updated.
- */
- m_cat(m, n)
- register struct mbuf *m, *n;
- {
- while (m->m_next)
- m = m->m_next;
- while (n) {
- if (m->m_flags & M_EXT ||
- m->m_data + m->m_len + n->m_len >= &m->m_dat[MLEN]) {
- /* just join the two chains */
- m->m_next = n;
- return;
- }
- /* splat the data from one into the other */
- bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
- (u_int)n->m_len);
- m->m_len += n->m_len;
- n = m_free(n);
- }
- }
-
- m_adj(mp, req_len)
- struct mbuf *mp;
- {
- register int len = req_len;
- register struct mbuf *m;
- register count;
-
- if ((m = mp) == NULL)
- return;
- if (len >= 0) {
- /*
- * Trim from head.
- */
- while (m != NULL && len > 0) {
- if (m->m_len <= len) {
- len -= m->m_len;
- m->m_len = 0;
- m = m->m_next;
- } else {
- m->m_len -= len;
- m->m_data += len;
- len = 0;
- }
- }
- m = mp;
- if (mp->m_flags & M_PKTHDR)
- m->m_pkthdr.len -= (req_len - len);
- } else {
- /*
- * Trim from tail. Scan the mbuf chain,
- * calculating its length and finding the last mbuf.
- * If the adjustment only affects this mbuf, then just
- * adjust and return. Otherwise, rescan and truncate
- * after the remaining size.
- */
- len = -len;
- count = 0;
- for (;;) {
- count += m->m_len;
- if (m->m_next == (struct mbuf *)0)
- break;
- m = m->m_next;
- }
- if (m->m_len >= len) {
- m->m_len -= len;
- if ((mp = m)->m_flags & M_PKTHDR)
- m->m_pkthdr.len -= len;
- return;
- }
- count -= len;
- if (count < 0)
- count = 0;
- /*
- * Correct length for chain is "count".
- * Find the mbuf with last data, adjust its length,
- * and toss data from remaining mbufs on chain.
- */
- m = mp;
- if (m->m_flags & M_PKTHDR)
- m->m_pkthdr.len = count;
- for (; m; m = m->m_next) {
- if (m->m_len >= count) {
- m->m_len = count;
- break;
- }
- count -= m->m_len;
- }
- while (m = m->m_next)
- m->m_len = 0;
- }
- }
-
- /*
- * Rearange an mbuf chain so that len bytes are contiguous
- * and in the data area of an mbuf (so that mtod and dtom
- * will work for a structure of size len). Returns the resulting
- * mbuf chain on success, frees it and returns null on failure.
- * If there is room, it will add up to max_protohdr-len extra bytes to the
- * contiguous region in an attempt to avoid being called next time.
- */
- int MPFail;
-
- struct mbuf *
- m_pullup(n, len)
- register struct mbuf *n;
- int len;
- {
- register struct mbuf *m;
- register int count;
- int space;
-
- /*
- * If first mbuf has no cluster, and has room for len bytes
- * without shifting current data, pullup into it,
- * otherwise allocate a new mbuf to prepend to the chain.
- */
- if ((n->m_flags & M_EXT) == 0 &&
- n->m_data + len < &n->m_dat[MLEN] && n->m_next) {
- if (n->m_len >= len)
- return (n);
- m = n;
- n = n->m_next;
- len -= m->m_len;
- } else {
- if (len > MHLEN)
- goto bad;
- MGET(m, M_DONTWAIT, n->m_type);
- if (m == 0)
- goto bad;
- m->m_len = 0;
- if (n->m_flags & M_PKTHDR) {
- M_COPY_PKTHDR(m, n);
- n->m_flags &= ~M_PKTHDR;
- }
- }
- space = &m->m_dat[MLEN] - (m->m_data + m->m_len);
- do {
- count = min(min(max(len, max_protohdr), space), n->m_len);
- bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
- (unsigned)count);
- len -= count;
- m->m_len += count;
- n->m_len -= count;
- space -= count;
- if (n->m_len)
- n->m_data += count;
- else
- n = m_free(n);
- } while (len > 0 && n);
- if (len > 0) {
- (void) m_free(m);
- goto bad;
- }
- m->m_next = n;
- return (m);
- bad:
- m_freem(n);
- MPFail++;
- return (0);
- }
-