return types, 64-bit changes: ioctl cmd -> u_long, casts, etc.
[unix-history] / usr / src / sys / kern / uipc_mbuf.c
index 90ac721..faf82a2 100644 (file)
@@ -1,30 +1,29 @@
 /*
 /*
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that this notice is preserved and that due credit is given
- * to the University of California at Berkeley. The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission. This software
- * is provided ``as is'' without express or implied warranty.
+ * %sccs.include.redist.c%
  *
  *
- *     @(#)uipc_mbuf.c 7.7 (Berkeley) %G%
+ *     @(#)uipc_mbuf.c 8.3 (Berkeley) %G%
  */
 
  */
 
-#include "../machine/pte.h"
-
-#include "param.h"
-#include "dir.h"
-#include "user.h"
-#include "proc.h"
-#include "cmap.h"
-#include "map.h"
-#include "mbuf.h"
-#include "vm.h"
-#include "kernel.h"
-#include "syslog.h"
-#include "domain.h"
-#include "protosw.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/map.h>
+#define MBTYPES
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+
+#include <vm/vm.h>
+
+extern vm_map_t mb_map;
+struct mbuf *mbutl;
+char   *mclrefcnt;
 
 mbinit()
 {
 
 mbinit()
 {
@@ -36,9 +35,7 @@ mbinit()
 #define NCL_INIT       1
 #endif
        s = splimp();
 #define NCL_INIT       1
 #endif
        s = splimp();
-       if (m_clalloc(NCL_INIT, MPG_MBUFS, M_DONTWAIT) == 0)
-               goto bad;
-       if (m_clalloc(NCL_INIT, MPG_CLUSTERS, M_DONTWAIT) == 0)
+       if (m_clalloc(NCL_INIT, M_DONTWAIT) == 0)
                goto bad;
        splx(s);
        return;
                goto bad;
        splx(s);
        return;
@@ -47,122 +44,119 @@ bad:
 }
 
 /*
 }
 
 /*
+ * Allocate some number of mbuf clusters
+ * and place on cluster free list.
  * Must be called at splimp.
  */
 /* ARGSUSED */
  * Must be called at splimp.
  */
 /* ARGSUSED */
-caddr_t
-m_clalloc(ncl, how, canwait)
+m_clalloc(ncl, nowait)
        register int ncl;
        register int ncl;
-       int how;
+       int nowait;
 {
 {
-       int npg, mbx;
-       register struct mbuf *m;
-       register int i;
        static int logged;
        static int logged;
+       register caddr_t p;
+       register int i;
+       int npg;
 
        npg = ncl * CLSIZE;
 
        npg = ncl * CLSIZE;
-       mbx = rmalloc(mbmap, (long)npg);
-       if (mbx == 0) {
+       p = (caddr_t)kmem_malloc(mb_map, ctob(npg), !nowait);
+       if (p == NULL) {
                if (logged == 0) {
                        logged++;
                if (logged == 0) {
                        logged++;
-                       log(LOG_ERR, "mbuf map full\n");
+                       log(LOG_ERR, "mb_map full\n");
                }
                return (0);
        }
                }
                return (0);
        }
-       m = cltom(mbx * NBPG / MCLBYTES);
-       if (memall(&Mbmap[mbx], npg, proc, CSYS) == 0) {
-               rmfree(mbmap, (long)npg, (long)mbx);
-               return (0);
-       }
-       vmaccess(&Mbmap[mbx], (caddr_t)m, npg);
-       switch (how) {
-
-       case MPG_CLUSTERS:
-               ncl = ncl * CLBYTES / MCLBYTES;
-               for (i = 0; i < ncl; i++) {
-                       m->m_off = 0;
-                       m->m_next = mclfree;
-                       mclfree = m;
-                       m += MCLBYTES / sizeof (*m);
-                       mbstat.m_clfree++;
-               }
-               mbstat.m_clusters += ncl;
-               break;
-
-       case MPG_MBUFS:
-               for (i = ncl * CLBYTES / sizeof (*m); i > 0; i--) {
-                       m->m_off = 0;
-                       m->m_type = MT_DATA;
-                       mbstat.m_mtypes[MT_DATA]++;
-                       mbstat.m_mbufs++;
-                       (void) m_free(m);
-                       m++;
-               }
-               break;
+       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++;
        }
        }
-       return ((caddr_t)m);
+       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);
 }
 
 /*
 }
 
 /*
- * Must be called at splimp.
+ * As above; retry an MGETHDR.
  */
  */
-m_expand(canwait)
-       int canwait;
+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;
 {
        register struct domain *dp;
        register struct protosw *pr;
-       int tries;
+       int s = splimp();
 
 
-       for (tries = 0;; ) {
-               if (m_clalloc(1, MPG_MBUFS, canwait))
-                       return (1);
-               if (canwait == 0 || tries++)
-                       return (0);
-
-               /* ask protocols to free space */
-               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)();
-               mbstat.m_drain++;
-       }
+       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++;
 }
 
 }
 
-/* NEED SOME WAY TO RELEASE SPACE */
-
 /*
  * Space allocation routines.
  * These are also available as macros
  * for critical paths.
  */
 struct mbuf *
 /*
  * Space allocation routines.
  * These are also available as macros
  * for critical paths.
  */
 struct mbuf *
-m_get(canwait, type)
-       int canwait, type;
+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;
 
 {
        register struct mbuf *m;
 
-       MGET(m, canwait, type);
+       MGETHDR(m, nowait, type);
        return (m);
 }
 
 struct mbuf *
        return (m);
 }
 
 struct mbuf *
-m_getclr(canwait, type)
-       int canwait, type;
+m_getclr(nowait, type)
+       int nowait, type;
 {
        register struct mbuf *m;
 
 {
        register struct mbuf *m;
 
-       MGET(m, canwait, type);
+       MGET(m, nowait, type);
        if (m == 0)
                return (0);
        bzero(mtod(m, caddr_t), MLEN);
        if (m == 0)
                return (0);
        bzero(mtod(m, caddr_t), MLEN);
@@ -179,46 +173,17 @@ m_free(m)
        return (n);
 }
 
        return (n);
 }
 
-/*
- * Get more mbufs; called from MGET macro if mfree list is empty.
- * Must be called at splimp.
- */
-/*ARGSUSED*/
-struct mbuf *
-m_more(canwait, type)
-       int canwait, type;
-{
-       register struct mbuf *m;
-
-       while (m_expand(canwait) == 0) {
-               if (canwait == M_WAIT) {
-                       mbstat.m_wait++;
-                       m_want++;
-                       sleep((caddr_t)&mfree, PZERO - 1);
-               } else {
-                       mbstat.m_drops++;
-                       return (NULL);
-               }
-       }
-#define m_more(x,y) (panic("m_more"), (struct mbuf *)0)
-       MGET(m, canwait, type);
-#undef m_more
-       return (m);
-}
-
+void
 m_freem(m)
        register struct mbuf *m;
 {
        register struct mbuf *n;
 m_freem(m)
        register struct mbuf *m;
 {
        register struct mbuf *n;
-       register int s;
 
        if (m == NULL)
                return;
 
        if (m == NULL)
                return;
-       s = splimp();
        do {
                MFREE(m, n);
        } while (m = n);
        do {
                MFREE(m, n);
        } while (m = n);
-       splx(s);
 }
 
 /*
 }
 
 /*
@@ -226,27 +191,59 @@ m_freem(m)
  */
 
 /*
  */
 
 /*
+ * 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 "off" bytes from the beginning,
+ * 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.
  * continuing for "len" bytes.  If len is M_COPYALL, copy to end of mbuf.
- * Should get M_WAIT/M_DONTWAIT from caller.
+ * The wait parameter is a choice of M_WAIT/M_DONTWAIT from caller.
  */
  */
+int MCFail;
+
 struct mbuf *
 struct mbuf *
-m_copy(m, off, len)
+m_copym(m, off0, len, wait)
        register struct mbuf *m;
        register struct mbuf *m;
-       int off;
+       int off0, wait;
        register int len;
 {
        register struct mbuf *n, **np;
        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)
        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)
        while (off > 0) {
                if (m == 0)
-                       panic("m_copy");
+                       panic("m_copym");
                if (off < m->m_len)
                        break;
                off -= m->m_len;
                if (off < m->m_len)
                        break;
                off -= m->m_len;
@@ -257,18 +254,27 @@ m_copy(m, off, len)
        while (len > 0) {
                if (m == 0) {
                        if (len != M_COPYALL)
        while (len > 0) {
                if (m == 0) {
                        if (len != M_COPYALL)
-                               panic("m_copy");
+                               panic("m_copym");
                        break;
                }
                        break;
                }
-               MGET(n, M_DONTWAIT, m->m_type);
+               MGET(n, wait, m->m_type);
                *np = n;
                if (n == 0)
                        goto nospace;
                *np = n;
                if (n == 0)
                        goto nospace;
-               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 (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);
                } else
                        bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t),
                            (unsigned)n->m_len);
@@ -278,9 +284,12 @@ m_copy(m, off, len)
                m = m->m_next;
                np = &n->m_next;
        }
                m = m->m_next;
                np = &n->m_next;
        }
+       if (top == 0)
+               MCFail++;
        return (top);
 nospace:
        m_freem(top);
        return (top);
 nospace:
        m_freem(top);
+       MCFail++;
        return (0);
 }
 
        return (0);
 }
 
@@ -288,12 +297,11 @@ nospace:
  * Copy data from an mbuf chain starting "off" bytes from the beginning,
  * continuing for "len" bytes, into the indicated buffer.
  */
  * Copy data from an mbuf chain starting "off" bytes from the beginning,
  * continuing for "len" bytes, into the indicated buffer.
  */
-struct mbuf *
 m_copydata(m, off, len, cp)
        register struct mbuf *m;
 m_copydata(m, off, len, cp)
        register struct mbuf *m;
-       int off;
+       register int off;
        register int len;
        register int len;
-       caddr_t *cp;
+       caddr_t cp;
 {
        register unsigned count;
 
 {
        register unsigned count;
 
@@ -310,22 +318,28 @@ m_copydata(m, off, len, cp)
        while (len > 0) {
                if (m == 0)
                        panic("m_copydata");
        while (len > 0) {
                if (m == 0)
                        panic("m_copydata");
-               count = MIN(m->m_len, len);
+               count = min(m->m_len - off, len);
                bcopy(mtod(m, caddr_t) + off, cp, count);
                len -= count;
                bcopy(mtod(m, caddr_t) + off, cp, count);
                len -= count;
+               cp += count;
                off = 0;
                m = m->m_next;
        }
 }
 
                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) {
 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;
                        /* just join the two chains */
                        m->m_next = n;
                        return;
@@ -338,16 +352,21 @@ m_cat(m, n)
        }
 }
 
        }
 }
 
-m_adj(mp, len)
+void
+m_adj(mp, req_len)
        struct mbuf *mp;
        struct mbuf *mp;
-       register int len;
+       int req_len;
 {
 {
+       register int len = req_len;
        register struct mbuf *m;
        register count;
 
        if ((m = mp) == NULL)
                return;
        if (len >= 0) {
        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;
                while (m != NULL && len > 0) {
                        if (m->m_len <= len) {
                                len -= m->m_len;
@@ -355,10 +374,13 @@ m_adj(mp, len)
                                m = m->m_next;
                        } else {
                                m->m_len -= 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 {
                /*
                 * Trim from tail.  Scan the mbuf chain,
        } else {
                /*
                 * Trim from tail.  Scan the mbuf chain,
@@ -377,15 +399,22 @@ m_adj(mp, len)
                }
                if (m->m_len >= len) {
                        m->m_len -= len;
                }
                if (m->m_len >= len) {
                        m->m_len -= len;
+                       if (mp->m_flags & M_PKTHDR)
+                               mp->m_pkthdr.len -= len;
                        return;
                }
                count -= 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.
                 */
                /*
                 * Correct length for chain is "count".
                 * Find the mbuf with last data, adjust its length,
                 * and toss data from remaining mbufs on chain.
                 */
-               for (m = mp; m; m = m->m_next) {
+               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;
                        if (m->m_len >= count) {
                                m->m_len = count;
                                break;
@@ -402,9 +431,11 @@ m_adj(mp, len)
  * 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.
  * 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 MPULL_EXTRA bytes to the
+ * 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.
  */
  * contiguous region in an attempt to avoid being called next time.
  */
+int MPFail;
+
 struct mbuf *
 m_pullup(n, len)
        register struct mbuf *n;
 struct mbuf *
 m_pullup(n, len)
        register struct mbuf *n;
@@ -414,28 +445,41 @@ m_pullup(n, len)
        register int count;
        int space;
 
        register int count;
        int space;
 
-       if (n->m_off + len <= MMAXOFF && n->m_next) {
+       /*
+        * 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 {
                m = n;
                n = n->m_next;
                len -= m->m_len;
        } else {
-               if (len > MLEN)
+               if (len > MHLEN)
                        goto bad;
                MGET(m, M_DONTWAIT, n->m_type);
                if (m == 0)
                        goto bad;
                m->m_len = 0;
                        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 = MMAXOFF - m->m_off;
+       space = &m->m_dat[MLEN] - (m->m_data + m->m_len);
        do {
        do {
-               count = MIN(MIN(space - m->m_len, len + MPULL_EXTRA), 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_len -= count;
                  (unsigned)count);
                len -= count;
                m->m_len += count;
                n->m_len -= count;
+               space -= count;
                if (n->m_len)
                if (n->m_len)
-                       n->m_off += count;
+                       n->m_data += count;
                else
                        n = m_free(n);
        } while (len > 0 && n);
                else
                        n = m_free(n);
        } while (len > 0 && n);
@@ -447,5 +491,140 @@ m_pullup(n, len)
        return (m);
 bad:
        m_freem(n);
        return (m);
 bad:
        m_freem(n);
+       MPFail++;
        return (0);
 }
        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 = &top;
+       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);
+}