X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/39d536e67f78a1d5cb735af8573e874d3b4aae66..01932c99d1800e02c98500a66b1c9d62d0f820c8:/usr/src/sys/kern/uipc_mbuf.c diff --git a/usr/src/sys/kern/uipc_mbuf.c b/usr/src/sys/kern/uipc_mbuf.c index cedc50038a..7959e40778 100644 --- a/usr/src/sys/kern/uipc_mbuf.c +++ b/usr/src/sys/kern/uipc_mbuf.c @@ -1,98 +1,126 @@ -/* uipc_mbuf.c 1.39 82/10/21 */ - -#include "../h/param.h" -#include "../h/dir.h" -#include "../h/user.h" -#include "../h/proc.h" -#include "../h/pte.h" -#include "../h/cmap.h" -#include "../h/map.h" -#include "../h/mbuf.h" -#include "../h/vm.h" -#include "../h/kernel.h" +/* + * All rights reserved. + * + * %sccs.include.redist.c% + * + * @(#)uipc_mbuf.c 7.22 (Berkeley) %G% + */ + +#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 (m_clalloc(4, MPG_MBUFS) == 0) - goto bad; - if (m_clalloc(32, MPG_CLUSTERS) == 0) +#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"); } -caddr_t -m_clalloc(ncl, how) +/* + * Allocate some number of mbuf clusters + * and place on cluster free list. + * Must be called at splimp. + */ +/* ARGSUSED */ +m_clalloc(ncl, nowait) register int ncl; - int how; + int nowait; { int npg, mbx; - register struct mbuf *m; + register caddr_t p; register int i; - int s; + static int logged; npg = ncl * CLSIZE; - s = splimp(); /* careful: rmalloc isn't reentrant */ - mbx = rmalloc(mbmap, npg); - splx(s); - if (mbx == 0) - return (0); - m = cltom(mbx / CLSIZE); - if (memall(&Mbmap[mbx], npg, proc, CSYS) == 0) - return (0); - vmaccess(&Mbmap[mbx], (caddr_t)m, npg); - switch (how) { - - case MPG_CLUSTERS: - s = splimp(); - for (i = 0; i < ncl; i++) { - m->m_off = 0; - m->m_next = mclfree; - mclfree = m; - m += CLBYTES / sizeof (*m); - mbstat.m_clfree++; - } - mbstat.m_clusters += ncl; - splx(s); - break; - - case MPG_MBUFS: - for (i = ncl * CLBYTES / sizeof (*m); i > 0; i--) { - m->m_off = 0; - m->m_free = 0; - mbstat.m_mbufs++; - (void) m_free(m); - m++; + p = (caddr_t)kmem_malloc(mb_map, ctob(npg), !nowait); + if (p == NULL) { + if (logged == 0) { + logged++; + log(LOG_ERR, "mb_map full\n"); } - break; + return (0); } - return ((caddr_t)m); + 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); } -m_pgfree(addr, n) - caddr_t addr; - int n; +/* + * 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; -#ifdef lint - addr = addr; n = n; -#endif + m_reclaim(); +#define m_retry(i, t) (struct mbuf *)0 + MGET(m, i, t); +#undef m_retry + return (m); } -m_expand() +/* + * As above; retry an MGETHDR. + */ +struct mbuf * +m_retryhdr(i, t) + int i, t; { + register struct mbuf *m; - if (m_clalloc(1, MPG_MBUFS) == 0) - goto steal; - return (1); -steal: - /* should ask protocols to free code */ - return (0); + m_reclaim(); +#define m_retryhdr(i, t) (struct mbuf *)0 + MGETHDR(m, i, t); +#undef m_retryhdr + return (m); } -/* NEED SOME WAY TO RELEASE SPACE */ +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. @@ -100,22 +128,32 @@ steal: * for critical paths. */ struct mbuf * -m_get(canwait) - int canwait; +m_get(nowait, type) + int nowait, type; +{ + register struct mbuf *m; + + MGET(m, nowait, type); + return (m); +} + +struct mbuf * +m_gethdr(nowait, type) + int nowait, type; { register struct mbuf *m; - MGET(m, canwait); + MGETHDR(m, nowait, type); return (m); } struct mbuf * -m_getclr(canwait) - int canwait; +m_getclr(nowait, type) + int nowait, type; { register struct mbuf *m; - m = m_get(canwait); + MGET(m, nowait, type); if (m == 0) return (0); bzero(mtod(m, caddr_t), MLEN); @@ -132,57 +170,77 @@ m_free(m) return (n); } -/*ARGSUSED*/ -struct mbuf * -m_more(type) - int type; -{ - register struct mbuf *m; - - if (!m_expand()) { - mbstat.m_drops++; - return (NULL); - } -#define m_more(x) (panic("m_more"), (struct mbuf *)0) - MGET(m, type); -#undef m_more - return (m); -} - +void m_freem(m) register struct mbuf *m; { register struct mbuf *n; - register int s; if (m == NULL) return; - s = splimp(); do { MFREE(m, n); } while (m = n); - splx(s); } /* * Mbuffer utility routines. */ + +/* + * Lesser-used path for M_PREPEND: + * allocate new mbuf to prepend to chain, + * copy junk along. + */ struct mbuf * -m_copy(m, off, len) +m_prepend(m, len, how) register struct mbuf *m; - int off; + 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; - struct mbuf *top, *p; + register int off = off0; + struct mbuf *top; + int copyhdr = 0; - if (len == 0) - return (0); if (off < 0 || len < 0) - panic("m_copy"); + panic("m_copym"); + if (off == 0 && m->m_flags & M_PKTHDR) + copyhdr = 1; while (off > 0) { if (m == 0) - panic("m_copy"); + panic("m_copym"); if (off < m->m_len) break; off -= m->m_len; @@ -193,18 +251,27 @@ m_copy(m, off, len) while (len > 0) { if (m == 0) { if (len != M_COPYALL) - panic("m_copy"); + panic("m_copym"); break; } - MGET(n, 1); + 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_off > MMAXOFF) { - p = mtod(m, struct mbuf *); - n->m_off = ((int)p - (int)n) + off; - mclrefcnt[mtocl(p)]++; + 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); @@ -214,20 +281,62 @@ m_copy(m, off, len) 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_off >= MMAXOFF || - m->m_off + m->m_len + n->m_len > MMAXOFF) { + 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; @@ -240,15 +349,20 @@ m_cat(m, n) } } -m_adj(mp, len) +m_adj(mp, req_len) struct mbuf *mp; - register int len; + int req_len; { - register struct mbuf *m, *n; + 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; @@ -256,60 +370,116 @@ m_adj(mp, len) m = m->m_next; } else { m->m_len -= len; - m->m_off += len; - break; + m->m_data += len; + len = 0; } } + m = mp; + if (mp->m_flags & M_PKTHDR) + m->m_pkthdr.len -= (req_len - len); } else { - /* a 2 pass algorithm might be better */ + /* + * 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; - while (len > 0 && m->m_len != 0) { - while (m != NULL && m->m_len != 0) { - n = m; - m = m->m_next; - } - if (n->m_len <= len) { - len -= n->m_len; - n->m_len = 0; - m = mp; - } else { - n->m_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(m0, len) - struct mbuf *m0; +m_pullup(n, len) + register struct mbuf *n; int len; { - register struct mbuf *m, *n; - int count; + register struct mbuf *m; + register int count; + int space; - n = m0; - if (len > MLEN) - goto bad; - MGET(m, M_DONTWAIT); - if (m == 0) - goto bad; - m->m_len = 0; + /* + * 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(MLEN - m->m_len, len); - if (count > n->m_len) - count = n->m_len; - bcopy(mtod(n, caddr_t), mtod(m, caddr_t)+m->m_len, + 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_off += count; n->m_len -= count; + space -= count; if (n->m_len) - break; - n = m_free(n); - } while (n); - if (len) { + n->m_data += count; + else + n = m_free(n); + } while (len > 0 && n); + if (len > 0) { (void) m_free(m); goto bad; } @@ -317,5 +487,140 @@ m_pullup(m0, len) return (m); bad: m_freem(n); + MPFail++; return (0); } + +/* + * Partition an mbuf chain in two pieces, returning the tail -- + * all but the first len0 bytes. In case of failure, it returns NULL and + * attempts to restore the chain to its original state. + */ +struct mbuf * +m_split(m0, len0, wait) + register struct mbuf *m0; + int len0, wait; +{ + register struct mbuf *m, *n; + unsigned len = len0, remain; + + for (m = m0; m && len > m->m_len; m = m->m_next) + len -= m->m_len; + if (m == 0) + return (0); + remain = m->m_len - len; + if (m0->m_flags & M_PKTHDR) { + MGETHDR(n, wait, m0->m_type); + if (n == 0) + return (0); + n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif; + n->m_pkthdr.len = m0->m_pkthdr.len - len0; + m0->m_pkthdr.len = len0; + if (m->m_flags & M_EXT) + goto extpacket; + if (remain > MHLEN) { + /* m can't be the lead packet */ + MH_ALIGN(n, 0); + n->m_next = m_split(m, len, wait); + if (n->m_next == 0) { + (void) m_free(n); + return (0); + } else + return (n); + } else + MH_ALIGN(n, remain); + } else if (remain == 0) { + n = m->m_next; + m->m_next = 0; + return (n); + } else { + MGET(n, wait, m->m_type); + if (n == 0) + return (0); + M_ALIGN(n, remain); + } +extpacket: + if (m->m_flags & M_EXT) { + n->m_flags |= M_EXT; + n->m_ext = m->m_ext; + mclrefcnt[mtocl(m->m_ext.ext_buf)]++; + m->m_ext.ext_size = 0; /* For Accounting XXXXXX danger */ + n->m_data = m->m_data + len; + } else { + bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain); + } + n->m_len = remain; + m->m_len = len; + n->m_next = m->m_next; + m->m_next = 0; + return (n); +} +/* + * Routine to copy from device local memory into mbufs. + */ +struct mbuf * +m_devget(buf, totlen, off0, ifp, copy) + char *buf; + int totlen, off0; + struct ifnet *ifp; + void (*copy)(); +{ + register struct mbuf *m; + struct mbuf *top = 0, **mp = ⊤ + register int off = off0, len; + register char *cp; + char *epkt; + + cp = buf; + epkt = cp + totlen; + if (off) { + cp += off + 2 * sizeof(u_short); + totlen -= 2 * sizeof(u_short); + } + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = totlen; + m->m_len = MHLEN; + + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return (0); + } + m->m_len = MLEN; + } + len = min(totlen, epkt - cp); + if (len >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = len = min(len, MCLBYTES); + else + len = m->m_len; + } else { + /* + * Place initial small packet/header at end of mbuf. + */ + if (len < m->m_len) { + if (top == 0 && len + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = len; + } else + len = m->m_len; + } + if (copy) + copy(cp, mtod(m, caddr_t), (unsigned)len); + else + bcopy(cp, mtod(m, caddr_t), (unsigned)len); + cp += len; + *mp = m; + mp = &m->m_next; + totlen -= len; + if (cp == epkt) + cp = buf; + } + return (top); +}