summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2009-02-04 20:02:12 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2009-02-04 20:02:12 +0000
commitb04f397dde5aabc22753f4fa7205ba932bd7b678 (patch)
treef47e0bc80f71a93943807a797cc5fc2ebfc38335
parentfa16643f0dbf5b7918ff02a9a8fde83f83f44c3d (diff)
Make m_copyback() a lot smarter. Make it use all of the last mbuf
(M_TRAILINGSPACE()) and allocate one cluster if needed (instead of chaining many mbufs). Somewhat needed for the rl(4) fix to ensure that the ethernet header is in one mbuf for sure. Tested by landry@ and myself
-rw-r--r--sys/kern/uipc_mbuf.c53
1 files changed, 37 insertions, 16 deletions
diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c
index cf1778038fa..bb41329f098 100644
--- a/sys/kern/uipc_mbuf.c
+++ b/sys/kern/uipc_mbuf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_mbuf.c,v 1.116 2009/01/27 09:17:51 dlg Exp $ */
+/* $OpenBSD: uipc_mbuf.c,v 1.117 2009/02/04 20:02:11 claudio Exp $ */
/* $NetBSD: uipc_mbuf.c,v 1.15.4.1 1996/06/13 17:11:44 cgd Exp $ */
/*
@@ -712,9 +712,8 @@ m_copydata(struct mbuf *m, int off, int len, caddr_t cp)
void
m_copyback(struct mbuf *m0, int off, int len, const void *_cp)
{
- int mlen;
+ int mlen, totlen = 0;
struct mbuf *m = m0, *n;
- int totlen = 0;
caddr_t cp = (caddr_t)_cp;
if (m0 == NULL)
@@ -723,34 +722,56 @@ m_copyback(struct mbuf *m0, int off, int len, const void *_cp)
off -= mlen;
totlen += mlen;
if (m->m_next == NULL) {
- n = m_getclr(M_DONTWAIT, m->m_type);
- if (n == NULL)
+ if ((n = m_get(M_DONTWAIT, m->m_type)) == NULL)
goto out;
- n->m_len = min(MLEN, len + off);
+
+ if (off + len > MLEN) {
+ MCLGETI(n, M_DONTWAIT, NULL, off + len);
+ if (!(n->m_flags & M_EXT)) {
+ m_free(n);
+ goto out;
+ }
+ }
+ bzero(mtod(n, caddr_t), off);
+ n->m_len = len + off;
m->m_next = n;
}
m = m->m_next;
}
while (len > 0) {
- mlen = min (m->m_len - off, len);
- bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen);
+ if (M_READONLY(m))
+ panic("m_copyback called on a readonly cluster");
+ /* extend last packet to be filled fully */
+ if (m->m_next == NULL && (len > m->m_len - off))
+ m->m_len += min(len - (m->m_len - off),
+ M_TRAILINGSPACE(m));
+ mlen = min(m->m_len - off, len);
+ bcopy(cp, mtod(m, caddr_t) + off, (size_t)mlen);
cp += mlen;
len -= mlen;
- mlen += off;
- off = 0;
- totlen += mlen;
+ totlen += mlen + off;
if (len == 0)
break;
+ off = 0;
+
if (m->m_next == NULL) {
- n = m_get(M_DONTWAIT, m->m_type);
- if (n == NULL)
- break;
- n->m_len = min(MLEN, len);
+ if ((n = m_get(M_DONTWAIT, m->m_type)) == NULL)
+ goto out;
+
+ if (len > MLEN) {
+ MCLGETI(n, M_DONTWAIT, NULL, len);
+ if (!(n->m_flags & M_EXT)) {
+ m_free(n);
+ goto out;
+ }
+ }
+ n->m_len = len;
m->m_next = n;
}
m = m->m_next;
}
-out: if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
+out:
+ if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
m->m_pkthdr.len = totlen;
}