generalize the buffer pool so that NFS can become a client
[unix-history] / usr / src / sys / kern / uipc_mbuf.c
index d7b1ffe..d301a64 100644 (file)
@@ -1,66 +1,97 @@
-/*     uipc_mbuf.c     1.42    82/12/17        */
+/*
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, 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'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)uipc_mbuf.c 7.4.1.3 (Berkeley) %G%
+ */
 
 #include "../machine/pte.h"
 
 
 #include "../machine/pte.h"
 
-#include "../h/param.h"
-#include "../h/dir.h"
-#include "../h/user.h"
-#include "../h/proc.h"
-#include "../h/cmap.h"
-#include "../h/map.h"
-#include "../h/mbuf.h"
-#include "../h/vm.h"
-#include "../h/kernel.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"
 
 mbinit()
 {
 
 mbinit()
 {
+       int s;
 
 
-       if (m_clalloc(4096/CLBYTES, MPG_MBUFS) == 0)
+#if CLBYTES < 4096
+#define NCL_INIT       (4096/CLBYTES)
+#else
+#define NCL_INIT       1
+#endif
+       s = splimp();
+       if (m_clalloc(NCL_INIT, MPG_MBUFS, M_DONTWAIT) == 0)
                goto bad;
                goto bad;
-       if (m_clalloc(8*4096/CLBYTES, MPG_CLUSTERS) == 0)
+       if (m_clalloc(NCL_INIT, MPG_CLUSTERS, M_DONTWAIT) == 0)
                goto bad;
                goto bad;
+       splx(s);
        return;
 bad:
        panic("mbinit");
 }
 
        return;
 bad:
        panic("mbinit");
 }
 
+/*
+ * Must be called at splimp.
+ */
+/* ARGSUSED */
 caddr_t
 caddr_t
-m_clalloc(ncl, how)
+m_clalloc(ncl, how, canwait)
        register int ncl;
        int how;
 {
        int npg, mbx;
        register struct mbuf *m;
        register int i;
        register int ncl;
        int how;
 {
        int npg, mbx;
        register struct mbuf *m;
        register int i;
-       int s;
+       static int logged;
 
        npg = ncl * CLSIZE;
 
        npg = ncl * CLSIZE;
-       s = splimp();           /* careful: rmalloc isn't reentrant */
        mbx = rmalloc(mbmap, (long)npg);
        mbx = rmalloc(mbmap, (long)npg);
-       splx(s);
-       if (mbx == 0)
+       if (mbx == 0) {
+               if (logged == 0) {
+                       logged++;
+                       log(LOG_ERR, "mbuf map full\n");
+               }
                return (0);
                return (0);
-       m = cltom(mbx / CLSIZE);
+       }
+       m = cltom(mbx * NBPG / MCLBYTES);
        if (memall(&Mbmap[mbx], npg, proc, CSYS) == 0) {
        if (memall(&Mbmap[mbx], npg, proc, CSYS) == 0) {
-               s = splimp();
                rmfree(mbmap, (long)npg, (long)mbx);
                rmfree(mbmap, (long)npg, (long)mbx);
-               splx(s);
                return (0);
        }
        vmaccess(&Mbmap[mbx], (caddr_t)m, npg);
        switch (how) {
 
        case MPG_CLUSTERS:
                return (0);
        }
        vmaccess(&Mbmap[mbx], (caddr_t)m, npg);
        switch (how) {
 
        case MPG_CLUSTERS:
-               s = splimp();
+               ncl = ncl * CLBYTES / MCLBYTES;
                for (i = 0; i < ncl; i++) {
                        m->m_off = 0;
                        m->m_next = mclfree;
                        mclfree = m;
                for (i = 0; i < ncl; i++) {
                        m->m_off = 0;
                        m->m_next = mclfree;
                        mclfree = m;
-                       m += CLBYTES / sizeof (*m);
+                       m += MCLBYTES / sizeof (*m);
                        mbstat.m_clfree++;
                }
                mbstat.m_clusters += ncl;
                        mbstat.m_clfree++;
                }
                mbstat.m_clusters += ncl;
-               splx(s);
                break;
 
        case MPG_MBUFS:
                break;
 
        case MPG_MBUFS:
@@ -77,25 +108,30 @@ m_clalloc(ncl, how)
        return ((caddr_t)m);
 }
 
        return ((caddr_t)m);
 }
 
-m_pgfree(addr, n)
-       caddr_t addr;
-       int n;
+/*
+ * Must be called at splimp.
+ */
+m_expand(canwait)
+       int canwait;
 {
 {
+       register struct domain *dp;
+       register struct protosw *pr;
+       int tries;
 
 
-#ifdef lint
-       addr = addr; n = n;
-#endif
-}
-
-m_expand()
-{
+       for (tries = 0;; ) {
+               if (m_clalloc(1, MPG_MBUFS, canwait))
+                       return (1);
+               if (canwait == 0 || tries++)
+                       return (0);
 
 
-       if (m_clalloc(1, MPG_MBUFS) == 0)
-               goto steal;
-       return (1);
-steal:
-       /* should ask protocols to free code */
-       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++;
+       }
 }
 
 /* NEED SOME WAY TO RELEASE SPACE */
 }
 
 /* NEED SOME WAY TO RELEASE SPACE */
@@ -121,7 +157,7 @@ m_getclr(canwait, type)
 {
        register struct mbuf *m;
 
 {
        register struct mbuf *m;
 
-       m = m_get(canwait, type);
+       MGET(m, canwait, type);
        if (m == 0)
                return (0);
        bzero(mtod(m, caddr_t), MLEN);
        if (m == 0)
                return (0);
        bzero(mtod(m, caddr_t), MLEN);
@@ -138,6 +174,10 @@ 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)
 /*ARGSUSED*/
 struct mbuf *
 m_more(canwait, type)
@@ -145,9 +185,17 @@ m_more(canwait, type)
 {
        register struct mbuf *m;
 
 {
        register struct mbuf *m;
 
-       if (!m_expand()) {
-               mbstat.m_drops++;
-               return (NULL);
+       while (m_expand(canwait) == 0) {
+               if (canwait == M_WAIT) {
+                       mbstat.m_wait++;
+                       m_want++;
+                       sleep((caddr_t)&mfree, PZERO - 1);
+                       if (mfree)
+                               break;
+               } else {
+                       mbstat.m_drops++;
+                       return (NULL);
+               }
        }
 #define m_more(x,y) (panic("m_more"), (struct mbuf *)0)
        MGET(m, canwait, type);
        }
 #define m_more(x,y) (panic("m_more"), (struct mbuf *)0)
        MGET(m, canwait, type);
@@ -173,6 +221,13 @@ m_freem(m)
 /*
  * Mbuffer utility routines.
  */
 /*
  * Mbuffer utility routines.
  */
+
+/*
+/*
+ * Make a copy of an mbuf chain starting "off" bytes from the beginning,
+ * continuing for "len" bytes.  If len is M_COPYALL, copy to end of mbuf.
+ * Should get M_WAIT/M_DONTWAIT from caller.
+ */
 struct mbuf *
 m_copy(m, off, len)
        register struct mbuf *m;
 struct mbuf *
 m_copy(m, off, len)
        register struct mbuf *m;
@@ -181,13 +236,11 @@ m_copy(m, off, len)
 {
        register struct mbuf *n, **np;
        struct mbuf *top, *p;
 {
        register struct mbuf *n, **np;
        struct mbuf *top, *p;
-       int type;
 
        if (len == 0)
                return (0);
        if (off < 0 || len < 0)
                panic("m_copy");
 
        if (len == 0)
                return (0);
        if (off < 0 || len < 0)
                panic("m_copy");
-       type = m->m_type;
        while (off > 0) {
                if (m == 0)
                        panic("m_copy");
        while (off > 0) {
                if (m == 0)
                        panic("m_copy");
@@ -204,7 +257,7 @@ m_copy(m, off, len)
                                panic("m_copy");
                        break;
                }
                                panic("m_copy");
                        break;
                }
-               MGET(n, M_WAIT, type);
+               MGET(n, M_DONTWAIT, m->m_type);
                *np = n;
                if (n == 0)
                        goto nospace;
                *np = n;
                if (n == 0)
                        goto nospace;
@@ -228,6 +281,40 @@ nospace:
        return (0);
 }
 
        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;
+       }
+}
+
 m_cat(m, n)
        register struct mbuf *m, *n;
 {
 m_cat(m, n)
        register struct mbuf *m, *n;
 {
@@ -252,7 +339,8 @@ m_adj(mp, len)
        struct mbuf *mp;
        register int len;
 {
        struct mbuf *mp;
        register int len;
 {
-       register struct mbuf *m, *n;
+       register struct mbuf *m;
+       register count;
 
        if ((m = mp) == NULL)
                return;
 
        if ((m = mp) == NULL)
                return;
@@ -269,55 +357,86 @@ m_adj(mp, len)
                        }
                }
        } else {
                        }
                }
        } 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;
                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;
+                       return;
+               }
+               count -= len;
+               /*
+                * 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) {
+                       if (m->m_len >= count) {
+                               m->m_len = count;
                                break;
                        }
                                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 MPULL_EXTRA bytes to the
+ * contiguous region in an attempt to avoid being called next time.
+ */
 struct mbuf *
 struct mbuf *
-m_pullup(m0, len)
-       struct mbuf *m0;
+m_pullup(n, len)
+       register struct mbuf *n;
        int len;
 {
        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, n->m_type);
-       if (m == 0)
-               goto bad;
-       m->m_len = 0;
+       if (n->m_off + len <= MMAXOFF && n->m_next) {
+               m = n;
+               n = n->m_next;
+               len -= m->m_len;
+       } else {
+               if (len > MLEN)
+                       goto bad;
+               MGET(m, M_DONTWAIT, n->m_type);
+               if (m == 0)
+                       goto bad;
+               m->m_len = 0;
+       }
+       space = MMAXOFF - m->m_off;
        do {
        do {
-               count = MIN(MLEN - m->m_len, len);
-               if (count > n->m_len)
-                       count = n->m_len;
+               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,
                  (unsigned)count);
                len -= count;
                m->m_len += count;
                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;
                if (n->m_len)
                n->m_len -= count;
                if (n->m_len)
-                       break;
-               n = m_free(n);
-       } while (n);
-       if (len) {
+                       n->m_off += count;
+               else
+                       n = m_free(n);
+       } while (len > 0 && n);
+       if (len > 0) {
                (void) m_free(m);
                goto bad;
        }
                (void) m_free(m);
                goto bad;
        }