diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2008-11-25 19:09:35 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2008-11-25 19:09:35 +0000 |
commit | 48067d44310d1ca65790cbb1591d89464321336d (patch) | |
tree | 8725a32fabef88ff1c95769acc02e4b54de94fa7 /sys | |
parent | ac56c3ccb75c4bbbd84ca4166e9cd2289c1f76d7 (diff) |
m_defrag() a mbuf chain defragmenter. It will collaps a mbuf chain into a
single buffer without changing the head mbuf. This is done with a lot of
magic so there will be dragons.
Tested and OK dlg@, kettenis@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/uipc_mbuf.c | 104 | ||||
-rw-r--r-- | sys/sys/mbuf.h | 3 |
2 files changed, 88 insertions, 19 deletions
diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c index 4a5e0af1327..ffde4c32209 100644 --- a/sys/kern/uipc_mbuf.c +++ b/sys/kern/uipc_mbuf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_mbuf.c,v 1.102 2008/11/25 17:01:14 dlg Exp $ */ +/* $OpenBSD: uipc_mbuf.c,v 1.103 2008/11/25 19:09:34 claudio Exp $ */ /* $NetBSD: uipc_mbuf.c,v 1.15.4.1 1996/06/13 17:11:44 cgd Exp $ */ /* @@ -115,6 +115,7 @@ int max_protohdr; /* largest protocol header */ int max_hdr; /* largest link+protocol header */ int max_datalen; /* MHLEN - max_hdr */ +void m_extfree(struct mbuf *); struct mbuf *m_copym0(struct mbuf *, int, int, int, int); void nmbclust_update(void); @@ -198,6 +199,10 @@ m_get(int nowait, int type) return (m); } +/* + * ATTN: When changing anything here check m_inithdr() and m_defrag() those + * may need to change as well. + */ struct mbuf * m_gethdr(int nowait, int type) { @@ -316,23 +321,8 @@ m_free(struct mbuf *m) mbstat.m_mtypes[m->m_type]--; if (m->m_flags & M_PKTHDR) m_tag_delete_chain(m); - if (m->m_flags & M_EXT) { - if (MCLISREFERENCED(m)) { - m->m_ext.ext_nextref->m_ext.ext_prevref = - m->m_ext.ext_prevref; - m->m_ext.ext_prevref->m_ext.ext_nextref = - m->m_ext.ext_nextref; - } else if (m->m_flags & M_CLUSTER) { - m_cluncount(m, 0); - pool_put(&mclpools[m->m_ext.ext_backend], - m->m_ext.ext_buf); - } else if (m->m_ext.ext_free) - (*(m->m_ext.ext_free))(m->m_ext.ext_buf, - m->m_ext.ext_size, m->m_ext.ext_arg); - else - free(m->m_ext.ext_buf,m->m_ext.ext_type); - m->m_ext.ext_size = 0; - } + if (m->m_flags & M_EXT) + m_extfree(m); m->m_flags = 0; n = m->m_next; pool_put(&mbpool, m); @@ -342,6 +332,27 @@ m_free(struct mbuf *m) } void +m_extfree(struct mbuf *m) +{ + if (MCLISREFERENCED(m)) { + m->m_ext.ext_nextref->m_ext.ext_prevref = + m->m_ext.ext_prevref; + m->m_ext.ext_prevref->m_ext.ext_nextref = + m->m_ext.ext_nextref; + } else if (m->m_flags & M_CLUSTER) { + m_cluncount(m, 0); + pool_put(&mclpools[m->m_ext.ext_backend], + m->m_ext.ext_buf); + } else if (m->m_ext.ext_free) + (*(m->m_ext.ext_free))(m->m_ext.ext_buf, + m->m_ext.ext_size, m->m_ext.ext_arg); + else + panic("unknown type of extension buffer"); + m->m_ext.ext_size = 0; + m->m_flags &= ~(M_EXT|M_CLUSTER); +} + +void m_freem(struct mbuf *m) { struct mbuf *n; @@ -354,6 +365,63 @@ m_freem(struct mbuf *m) } /* + * mbuf chain defragmenter. This function uses some evil tricks to defragment + * an mbuf chain into a single buffer without changing the mbuf pointer. + * This needs to know a lot of the mbuf internals to make this work. + */ +int +m_defrag(struct mbuf *m, int how) +{ + struct mbuf *m0; + +#ifdef DIAGNOSTIC + if (!(m->m_flags & M_PKTHDR) || m->m_next == NULL) + panic("m_defrag: no packet hdr or not a chain"); +#endif + + if ((m0 = m_gethdr(how, m->m_type)) == NULL) + return -1; + if (m->m_pkthdr.len > MHLEN) { + MCLGETI(m0, how, NULL, m->m_pkthdr.len); + if (!(m0->m_flags & M_EXT)) { + m_free(m0); + return -1; + } + } + m_copydata(m, 0, m->m_pkthdr.len, mtod(m0, caddr_t)); + m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len; + + /* free chain behind and possible ext buf on the first mbuf */ + m_freem(m->m_next); + m->m_next = NULL; + + if (m->m_flags & M_EXT) + m_extfree(m); + + /* + * Bounce copy mbuf over to the original mbuf and set everything up. + * This needs to reset or clear all pointers that may go into the + * original mbuf chain. + */ + if (m0->m_flags & M_EXT) { + bcopy(&m0->m_ext, &m->m_ext, sizeof(struct mbuf_ext)); + MCLINITREFERENCE(m); + m->m_flags |= M_EXT|M_CLUSTER; + m->m_data = m->m_ext.ext_buf; + } else { + m->m_data = m->m_pktdat; + bcopy(&m0->m_data, &m->m_data, m0->m_len); + } + m->m_pkthdr.len = m->m_len = m0->m_len; + m->m_pkthdr.pf.hdr = NULL; /* altq will cope */ + + m0->m_flags &= ~(M_EXT|M_CLUSTER); /* cluster is gone */ + m_free(m0); + + return 0; +} + +/* * Mbuffer utility routines. */ diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index db3a3247425..7d7cb027944 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: mbuf.h,v 1.115 2008/11/25 12:47:00 deraadt Exp $ */ +/* $OpenBSD: mbuf.h,v 1.116 2008/11/25 19:09:34 claudio Exp $ */ /* $NetBSD: mbuf.h,v 1.19 1996/02/09 18:25:14 christos Exp $ */ /* @@ -413,6 +413,7 @@ struct mbuf *m_get(int, int); struct mbuf *m_getclr(int, int); struct mbuf *m_gethdr(int, int); struct mbuf *m_inithdr(struct mbuf *); +int m_defrag(struct mbuf *, int); struct mbuf *m_prepend(struct mbuf *, int, int); struct mbuf *m_pulldown(struct mbuf *, int, int, int *); struct mbuf *m_pullup(struct mbuf *, int); |