summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authorarchie <archie@FreeBSD.org>1999-12-01 22:31:32 +0000
committerarchie <archie@FreeBSD.org>1999-12-01 22:31:32 +0000
commit8e6dc36b73cc5b08e51f9c0baf804c7a01842fbf (patch)
tree132944441fa46dc24a89d771fb0065502025e06f /sys/kern
parentabcc28c7f8416570111b9f237ccac06a980119a1 (diff)
downloadFreeBSD-src-8e6dc36b73cc5b08e51f9c0baf804c7a01842fbf.zip
FreeBSD-src-8e6dc36b73cc5b08e51f9c0baf804c7a01842fbf.tar.gz
The functions m_copym() and m_copypacket() return read-only copies,
because in the case of mbuf clusters they only increment the reference count rather than actually copying the data. Add comments to this effect, and add a new routine called m_dup() that returns a real, writable copy of an mbuf chain. This is preliminary work required for implementing 'ipfw tee'. Reviewed by: julian
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/uipc_mbuf.c78
1 files changed, 78 insertions, 0 deletions
diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c
index 6ba86b7..1684c92 100644
--- a/sys/kern/uipc_mbuf.c
+++ b/sys/kern/uipc_mbuf.c
@@ -423,6 +423,8 @@ m_prepend(m, len, how)
* 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.
+ * Note that the copy is read-only, because clusters are not copied,
+ * only their reference counts are incremented.
*/
#define MCFail (mbstat.m_mcfail)
@@ -499,6 +501,8 @@ nospace:
/*
* Copy an entire packet, including header (which must be present).
* An optimization of the common case `m_copym(m, 0, M_COPYALL, how)'.
+ * Note that the copy is read-only, because clusters are not copied,
+ * only their reference counts are incremented.
*/
struct mbuf *
m_copypacket(m, how)
@@ -593,6 +597,80 @@ m_copydata(m, off, len, cp)
}
/*
+ * Copy a packet header mbuf chain into a completely new chain, including
+ * copying any mbuf clusters. Use this instead of m_copypacket() when
+ * you need a writable copy of an mbuf chain.
+ */
+struct mbuf *
+m_dup(m, how)
+ struct mbuf *m;
+ int how;
+{
+ struct mbuf **p, *top = NULL;
+ int remain, moff, nsize;
+
+ /* Sanity check */
+ if (m == NULL)
+ return (0);
+ KASSERT((m->m_flags & M_PKTHDR) != 0, ("%s: !PKTHDR", __FUNCTION__));
+
+ /* While there's more data, get a new mbuf, tack it on, and fill it */
+ remain = m->m_pkthdr.len;
+ moff = 0;
+ p = &top;
+ while (remain > 0 || top == NULL) { /* allow m->m_pkthdr.len == 0 */
+ struct mbuf *n;
+
+ /* Get the next new mbuf */
+ MGET(n, how, m->m_type);
+ if (n == NULL)
+ goto nospace;
+ if (top == NULL) { /* first one, must be PKTHDR */
+ M_COPY_PKTHDR(n, m);
+ nsize = MHLEN;
+ } else /* not the first one */
+ nsize = MLEN;
+ if (remain >= MINCLSIZE) {
+ MCLGET(n, how);
+ if ((n->m_flags & M_EXT) == 0) {
+ (void)m_free(n);
+ goto nospace;
+ }
+ nsize = MCLBYTES;
+ }
+ n->m_len = 0;
+
+ /* Link it into the new chain */
+ *p = n;
+ p = &n->m_next;
+
+ /* Copy data from original mbuf(s) into new mbuf */
+ while (n->m_len < nsize && m != NULL) {
+ int chunk = min(nsize - n->m_len, m->m_len - moff);
+
+ bcopy(m->m_data + moff, n->m_data + n->m_len, chunk);
+ moff += chunk;
+ n->m_len += chunk;
+ remain -= chunk;
+ if (moff == m->m_len) {
+ m = m->m_next;
+ moff = 0;
+ }
+ }
+
+ /* Check correct total mbuf length */
+ KASSERT((remain > 0 && m != NULL) || (remain == 0 && m == NULL),
+ ("%s: bogus m_pkthdr.len", __FUNCTION__));
+ }
+ return (top);
+
+nospace:
+ m_freem(top);
+ MCFail++;
+ return (0);
+}
+
+/*
* Concatenate mbuf chain n to m.
* Both chains must be of the same type (e.g. MT_DATA).
* Any m_pkthdr is not updated.
OpenPOWER on IntegriCloud