BSD 4_3_Reno release
[unix-history] / usr / src / sys / kern / uipc_mbuf.c
index 7e8ffab..ba17d3f 100644 (file)
-/* uipc_mbuf.c 1.2 81/10/23 */
-
-#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 "../bbnnet/net.h"
-#include "../bbnnet/mbuf.h"
-#include "../bbnnet/tcp.h"
-#include "../bbnnet/ip.h"
-#include "../h/vm.h"
+/*
+ * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution is only permitted until one year after the first shipment
+ * of 4.4BSD by the Regents.  Otherwise, redistribution and use in source and
+ * binary forms are permitted provided that: (1) source distributions retain
+ * this entire copyright notice and comment, and (2) distributions including
+ * binaries display the following acknowledgement:  This product includes
+ * software developed by the University of California, Berkeley and its
+ * contributors'' in the documentation or other materials provided with the
+ * distribution and in all advertising materials mentioning features or use
+ * of this software.  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 AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)uipc_mbuf.c 7.17 (Berkeley) 7/25/90
+ */
+
+#include "param.h"
+#include "user.h"
+#include "proc.h"
+#include "cmap.h"
+#include "malloc.h"
+#include "map.h"
+#define MBTYPES
+#include "mbuf.h"
+#include "vm.h"
+#include "kernel.h"
+#include "syslog.h"
+#include "domain.h"
+#include "protosw.h"
+#include "machine/pte.h"
 
 
+mbinit()
+{
+       int s;
+
+#if MCLBYTES < 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;
+       mbx = rmalloc(mbmap, (long)npg);
+       if (mbx == 0) {
+               if (logged == 0) {
+                       logged++;
+                       log(LOG_ERR, "mbuf map full\n");
+               }
+               return (0);
+       }
+       p = cltom(mbx * NBPG / MCLBYTES);
+       if (memall(&Mbmap[mbx], npg, proc, CSYS) == 0) {
+               rmfree(mbmap, (long)npg, (long)mbx);
+               return (0);
+       }
+       vmaccess(&Mbmap[mbx], p, npg);
+       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 *
 struct mbuf *
-m_get(canwait)
-       int canwait;
+m_retry(i, t)
+       int i, t;
 {
        register struct mbuf *m;
 
 {
        register struct mbuf *m;
 
-COUNT(M_GET);
-       MGET(m, canwait);
+       m_reclaim();
+#define m_retry(i, t)  (struct mbuf *)0
+       MGET(m, i, t);
+#undef m_retry
        return (m);
 }
 
        return (m);
 }
 
+/*
+ * As above; retry an MGETHDR.
+ */
 struct mbuf *
 struct mbuf *
-m_free(m)
-       struct mbuf *m;
+m_retryhdr(i, t)
+       int i, t;
 {
 {
-       register struct mbuf *n;
+       register struct mbuf *m;
 
 
-COUNT(M_FREE);
-       MFREE(m, n);
-       return (n);
+       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 *
 struct mbuf *
-m_more(type)
-       int type;
+m_get(canwait, type)
+       int canwait, type;
 {
 {
-       int s;
        register struct mbuf *m;
 
        register struct mbuf *m;
 
-COUNT(M_MORE);
-       if (!m_expand()) {
-               netstat.m_drops++;
-               return (NULL);
-       }
-#define m_more(x) ((struct mbuf *)panic("m_more"))
-       MGET(m, 0);
+       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);
 }
 
        return (m);
 }
 
-m_freem(m)                      /* free mbuf chain headed by 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;
        register struct mbuf *m;
 {
        register struct mbuf *n;
-       register int s, cnt;
 
 
-COUNT(M_FREEM);
        if (m == NULL)
        if (m == NULL)
-               return (0);
-       cnt = 0;
-       s = spl_imp();
+               return;
        do {
                MFREE(m, n);
        do {
                MFREE(m, n);
-               cnt++;
        } while (m = n);
        } while (m = n);
-       splx(s);
-       return (cnt);
 }
 
 }
 
+/*
+ * Mbuffer utility routines.
+ */
 
 
-mbufinit()                      /* init network buffer mgmt system */
-{
+/*
+ * 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;
        register struct mbuf *m;
-       register i;
-
-COUNT(MBUFINIT);
-       m = (struct mbuf *)&netutl[0];  /* ->start of buffer virt mem */
-       vmemall(&Netmap[0], 2, proc, CSYS);
-       vmaccess(&Netmap[0], m, 2);
-       for (i=0; i < NMBPG; i++) {
-               m->m_off = 0;
-               m_free(m);
-               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);
        }
        }
-       pg_alloc(3);
-       netcb.n_pages = 4;
-       netcb.n_bufs = 32;
-       netcb.n_lowat = 16;
-       netcb.n_hiwat = 32;
+       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);
 }
 
 }
 
-pg_alloc(n)
-       register int n;
-{
-       register i, j, k;
+/*
+ * 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;
        register struct mbuf *m;
-       int bufs, s;
+       int off0, wait;
+       register int len;
+{
+       register struct mbuf *n, **np;
+       register int off = off0;
+       struct mbuf *top;
+       int copyhdr = 0;
 
 
-COUNT(PG_ALLOC);
-       k = n << 1;
-       if ((i = rmalloc(netmap, n)) == 0)
-               return (0);
-       j = i<<1;
-       m = pftom(i);
-       /* should use vmemall sometimes */
-       if (memall(&Netmap[j], k, proc, CSYS) == 0)
-               return (0);
-       vmaccess(&Netmap[j], (caddr_t)m, k);
-       bufs = n << 3;
-       s = spl_imp();
-       for (j=0; j < bufs; j++) {
-               m->m_off = 0;
-               m_free(m);
-               m++;
+       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;
        }
        }
-       splx(s);
-       netcb.n_pages += n;
-       return (1);
+       np = &top;
+       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);
 }
 
 }
 
-m_expand()
+/*
+ * 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 i;
-       register struct ipq *fp;
-       register struct ip *q;
-       register struct tcb *tp;
-       register struct mbuf *m, *n;
-       int need, needp, needs;
-
-COUNT(M_EXPAND);
-       needs = need = netcb.n_hiwat - netcb.n_bufs;    /* #bufs to add */
-       needp = need >> 3;                              /* #pages to add */
-       if (pg_alloc(needp))
-               return (1);
-       for (i=0; i < needp; i++, need-=NMBPG)
-               if (needp == 1 || pg_alloc(1) == 0)             /* ??? */
-                       goto steal;
-       return (need < needs);
-steal:
-       fp = netcb.n_ip_tail;           /* ip reass.q */
-       while (need > 0 && fp) {
-               q = fp->iqx.ip_next;    /* free mbufs assoc. w/chain */
-               while (q != (struct ip *)fp) {
-                       need -= m_freem(dtom(q));
-                       q = q->ip_next;
-               }
-               ip_freef(fp);           /* free header */
-               fp = netcb.n_ip_tail;
+       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;
        }
        }
-       tp = netcb.n_tcb_tail;          /* ->tcbs */
-       while (need > 0 && tp != NULL) {
-               m = tp->t_rcv_unack;
-               while (m != NULL) {
-                       n = m->m_act;
-                       need -= m_freem(m);
-                       m = n;
-               }
-               tp->t_rcv_unack = NULL;
-               tp = tp->t_tcb_prev;
+       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;
        }
        }
-       return (need < needs);
 }
 
 }
 
-#ifdef notdef
-m_relse()
+/*
+ * 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;
 {
 {
-       int free;
-
-COUNT(M_RELSE);
-       free = (netcb.n_bufs - netcb.n_hiwat) >> 3;    /* # excess free pages */
-       return;
+       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);
+       }
 }
 }
-#endif
 
 
-struct mbuf *
-m_adj(mp, len)
+m_adj(mp, req_len)
        struct mbuf *mp;
        struct mbuf *mp;
-       register len;
 {
 {
-       register struct mbuf *m, *n;
+       register int len = req_len;
+       register struct mbuf *m;
+       register count;
 
 
-COUNT(M_ADJ);
        if ((m = mp) == NULL)
                return;
        if ((m = mp) == NULL)
                return;
-       if (len >= 0) {                 /* adjust from top of msg chain */
+       if (len >= 0) {
+               /*
+                * Trim from head.
+                */
                while (m != NULL && len > 0) {
                while (m != NULL && len > 0) {
-                       if (m->m_len <= len) {          /* free this mbuf */
+                       if (m->m_len <= len) {
                                len -= m->m_len;
                                m->m_len = 0;
                                m = m->m_next;
                                len -= m->m_len;
                                m->m_len = 0;
                                m = m->m_next;
-                       } else {                        /* adjust mbuf */
+                       } else {
                                m->m_len -= len;
                                m->m_len -= len;
-                               m->m_off += len;
-                               break;
+                               m->m_data += len;
+                               len = 0;
                        }
                }
                        }
                }
-
-       } else {                        /* adjust from bottom of msg chain */
+               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;
                len = -len;
-               while (len > 0 && m->m_len != 0) {
-                       /* find end of chain */
-                       while (m != NULL && m->m_len != 0) {
-                               n = m;
-                               m = m->m_next;
-                       }
-                       if (n->m_len <= len) {          /* last mbuf */
-                               len -= n->m_len;
-                               n->m_len = 0;
-                               m = mp;
-                       } else {                        /* adjust length */
-                               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;
                        }
                                break;
                        }
+                       count -= m->m_len;
                }
                }
+               while (m = m->m_next)
+                       m->m_len = 0;
        }
 }
 
 /*
        }
 }
 
 /*
- * convert mbuf virtual to physical addr for uballoc
+ * 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.
  */
  */
-mtophys(m)
-       register struct mbuf *m;
+int MPFail;
+
+struct mbuf *
+m_pullup(n, len)
+       register struct mbuf *n;
+       int len;
 {
 {
-       register i;
-       register unsigned long addr;
-       register struct pte *pte;
+       register struct mbuf *m;
+       register int count;
+       int space;
 
 
-COUNT(MTOPHYS);
-       i = (((int)m & ~PGOFSET) - (int)netutl) >> PGSHIFT;
-       pte = &Netmap[i];
-       addr = (pte->pg_pfnum << PGSHIFT) | ((int)m & PGOFSET);
-       return (addr);
+       /*
+        * 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);
 }
 }