Bring in m_colapse() from FreeBSD.
authorRui Paulo <rpaulo@FreeBSD.org>
Mon, 22 Feb 2010 16:25:37 +0000 (16:25 +0000)
committerRui Paulo <rpaulo@FreeBSD.org>
Mon, 22 Feb 2010 16:25:37 +0000 (16:25 +0000)
sys/kern/uipc_mbuf.c
sys/sys/mbuf.h

index aa2ff75..0965af9 100644 (file)
@@ -1898,6 +1898,93 @@ nospace:
        return (NULL);
 }
 
+
+
+/*
+ * Defragment an mbuf chain, returning at most maxfrags separate
+ * mbufs+clusters.  If this is not possible NULL is returned and
+ * the original mbuf chain is left in it's present (potentially
+ * modified) state.  We use two techniques: collapsing consecutive
+ * mbufs and replacing consecutive mbufs by a cluster.
+ *
+ * NB: this should really be named m_defrag but that name is taken
+ */
+struct mbuf *
+m_collapse(struct mbuf *m0, int how, int maxfrags)
+{
+       struct mbuf *m, *n, *n2, **prev;
+       u_int curfrags;
+
+       /*
+        * Calculate the current number of frags.
+        */
+       curfrags = 0;
+       for (m = m0; m != NULL; m = m->m_next)
+               curfrags++;
+       /*
+        * First, try to collapse mbufs.  Note that we always collapse
+        * towards the front so we don't need to deal with moving the
+        * pkthdr.  This may be suboptimal if the first mbuf has much
+        * less data than the following.
+        */
+       m = m0;
+again:
+       for (;;) {
+               n = m->m_next;
+               if (n == NULL)
+                       break;
+               if (n->m_len < M_TRAILINGSPACE(m)) {
+                       bcopy(mtod(n, void *), mtod(m, char *) + m->m_len,
+                           n->m_len);
+                       m->m_len += n->m_len;
+                       m->m_next = n->m_next;
+                       m_free(n);
+                       if (--curfrags <= maxfrags)
+                               return m0;
+               } else
+                       m = n;
+       }
+       KASSERT(maxfrags > 1,
+               ("maxfrags %u, but normal collapse failed", maxfrags));
+       /*
+        * Collapse consecutive mbufs to a cluster.
+        */
+       prev = &m0->m_next;             /* NB: not the first mbuf */
+       while ((n = *prev) != NULL) {
+               if ((n2 = n->m_next) != NULL &&
+                   n->m_len + n2->m_len < MCLBYTES) {
+                       m = m_getcl(how, MT_DATA, 0);
+                       if (m == NULL)
+                               goto bad;
+                       bcopy(mtod(n, void *), mtod(m, void *), n->m_len);
+                       bcopy(mtod(n2, void *), mtod(m, char *) + n->m_len,
+                           n2->m_len);
+                       m->m_len = n->m_len + n2->m_len;
+                       m->m_next = n2->m_next;
+                       *prev = m;
+                       m_free(n);
+                       m_free(n2);
+                       if (--curfrags <= maxfrags)     /* +1 cl -2 mbufs */
+                               return m0;
+                       /*
+                        * Still not there, try the normal collapse
+                        * again before we allocate another cluster.
+                        */
+                       goto again;
+                }
+               prev = &n->m_next;
+       }
+       /*
+        * No place where we can collapse to a cluster; punt.
+        * This can occur if, for example, you request 2 frags
+        * but the packet requires that both be clusters (we
+        * never reallocate the first mbuf to avoid moving the
+        * packet header).
+        */
+bad:
+       return NULL;
+}
+
 /*
  * Move data from uio into mbufs.
  */
index 200f820..1567bea 100644 (file)
@@ -461,6 +461,7 @@ struct      mbuf    *m_copym(const struct mbuf *, int, int, int);
 struct mbuf    *m_copypacket(struct mbuf *, int);
 struct mbuf    *m_defrag(struct mbuf *, int);
 struct mbuf    *m_defrag_nofree(struct mbuf *, int);
+struct mbuf    *m_collapse(struct mbuf *, int, int);
 struct mbuf    *m_devget(char *, int, int, struct ifnet *,
                  void (*copy)(volatile const void *, volatile void *, size_t));
 struct mbuf    *m_dup(struct mbuf *, int);