diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2009-02-04 20:02:12 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2009-02-04 20:02:12 +0000 |
commit | b04f397dde5aabc22753f4fa7205ba932bd7b678 (patch) | |
tree | f47e0bc80f71a93943807a797cc5fc2ebfc38335 /sys | |
parent | fa16643f0dbf5b7918ff02a9a8fde83f83f44c3d (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
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/uipc_mbuf.c | 53 |
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; } |