From 88f772d3b4592d456c2cf3ce79f15f562a6c2428 Mon Sep 17 00:00:00 2001 From: dlg Date: Mon, 18 Aug 2014 04:06:16 +0000 Subject: [PATCH] introduce mbuf list and queue apis. both manage fifo lists of mbufs and a count of the mbufs. struct mbuf_list and the ml_foo() apis can be used to build lists of mbufs where you dont need locking (eg, on the stack). struct mbuf_queue and mq_foo() wrap mbuf_lists with a mutex, and limits the number of mbufs that can be queued. they can be useful for moving mbufs between contexts/subsystems. with help from jmc@ for the manpage bits mpi@ is keen --- share/man/man9/Makefile | 12 +- share/man/man9/mbuf.9 | 262 +++++++++++++++++++++++++++++++++++++++- sys/kern/uipc_mbuf.c | 152 ++++++++++++++++++++++- sys/sys/mbuf.h | 53 +++++++- 4 files changed, 472 insertions(+), 7 deletions(-) diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 5ca494d4e0a..21b3edf6fc4 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.218 2014/07/24 01:18:23 dlg Exp $ +# $OpenBSD: Makefile,v 1.219 2014/08/18 04:06:16 dlg Exp $ # $NetBSD: Makefile,v 1.4 1996/01/09 03:23:01 thorpej Exp $ # Makefile for section 9 (kernel function and variable) manual pages. @@ -255,7 +255,15 @@ MLINKS+=mbuf.9 m_copym2.9 mbuf.9 m_copym.9 mbuf.9 m_free.9 mbuf.9 MFREE.9 \ mbuf.9 MEXTADD.9 mbuf.9 M_ALIGN.9 mbuf.9 MH_ALIGN.9 \ mbuf.9 M_READONLY.9 mbuf.9 M_LEADINGSPACE.9 \ mbuf.9 M_TRAILINGSPACE.9 mbuf.9 mtod.9 \ - mbuf.9 m_dup_pkthdr.9 + mbuf.9 m_dup_pkthdr.9 \ + mbuf.9 ml_init.9 mbuf.9 ml_enqueue.9 mbuf.9 ml_dequeue.9 \ + mbuf.9 ml_dechain.9 mbuf.9 ml_len.9 mbuf.9 ml_empty.9 \ + mbuf.9 MBUF_LIST_INITIALIZER.9 mbuf.9 MBUF_LIST_FOREACH.9 \ + mbuf.9 mq_init.9 mbuf.9 mq_enqueue.9 mbuf.9 mq_dequeue.9 \ + mbuf.9 mq_enlist.9 mbuf.9 mq_delist.9 mbuf.9 mq_dechain.9 \ + mbuf.9 mq_len.9 mbuf.9 mq_empty.9 \ + mbuf.9 mq_drops.9 mbuf.9 mq_set_maxlen.9 \ + mbuf.9 MBUF_QUEUE_INITIALIZER.9 MLINKS+=mbuf_tags.9 m_tag_get.9 mbuf_tags.9 m_tag_find.9 \ mbuf_tags.9 m_tag_prepend.9 mbuf_tags.9 m_tag_delete.9 \ mbuf_tags.9 m_tag_copy.9 mbuf_tags.9 m_tag_delete_chain.9 \ diff --git a/share/man/man9/mbuf.9 b/share/man/man9/mbuf.9 index dd6e5411a6b..f10da3af4fe 100644 --- a/share/man/man9/mbuf.9 +++ b/share/man/man9/mbuf.9 @@ -1,4 +1,4 @@ -.\" $OpenBSD: mbuf.9,v 1.75 2014/07/13 10:59:49 jmc Exp $ +.\" $OpenBSD: mbuf.9,v 1.76 2014/08/18 04:06:16 dlg Exp $ .\" .\" Copyright (c) 2001 Jean-Jacques Bernard-Gundol .\" All rights reserved. @@ -25,11 +25,30 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: July 13 2014 $ +.Dd $Mdocdate: August 18 2014 $ .Dt MBUF 9 .Os .Sh NAME .Nm mbuf +.\" .Nm ml_init , +.\" .Nm ml_enqueue , +.\" .Nm ml_dequeue , +.\" .Nm ml_dechain , +.\" .Nm ml_len , +.\" .Nm ml_empty , +.\" .Nm MBUF_LIST_INITIALIZER , +.\" .Nm MBUF_LIST_FOREACH , +.\" .Nm mq_init , +.\" .Nm mq_enqueue , +.\" .Nm mq_dequeue , +.\" .Nm mq_enlist , +.\" .Nm mq_delist , +.\" .Nm mq_dechain , +.\" .Nm mq_len , +.\" .Nm mq_empty , +.\" .Nm mq_drops , +.\" .Nm mq_set_maxlen , +.\" .Nm MBUF_QUEUE_INITIALIZER .Nd kernel memory management for networking protocols .Sh SYNOPSIS .In sys/mbuf.h @@ -92,6 +111,42 @@ .Fn M_TRAILINGSPACE "struct mbuf *m" .Ft int .Fn m_dup_pkthdr "struct mbuf *to" "struct mbuf *from" "int how" +.Ft void +.Fn "ml_init" "struct mbuf_list *ml" +.Ft void +.Fn "ml_enqueue" "struct mbuf_list *ml" "struct mbuf *m" +.Ft struct mbuf * +.Fn "ml_dequeue" "struct mbuf_list *ml" +.Ft struct mbuf * +.Fn "ml_dechain" "struct mbuf_list *ml" +.Ft unsigned int +.Fn "ml_len" "struct mbuf_list *ml" +.Ft int +.Fn "ml_empty" "struct mbuf_list *ml" +.Ft struct mbuf_list +.Fn "MBUF_LIST_INITIALIZER" +.Fn "MBUF_LIST_FOREACH" "struct mbuf_list *ml" "VARNAME" +.Fn "mq_init" "struct mbuf_queue *mq" "unsigned int maxlen" "int ipl" +.Ft int +.Fn "mq_enqueue" "struct mbuf_queue *mq" "struct mbuf *m" +.Ft struct mbuf * +.Fn "mq_dequeue" "struct mbuf_queue *mq" +.Ft int +.Fn "mq_enlist" "struct mbuf_queue *mq" "struct mbuf_list *ml" +.Ft void +.Fn "mq_delist" "struct mbuf_queue *mq" "struct mbuf_list *ml" +.Ft struct mbuf * +.Fn "mq_dechain" "struct mbuf_queue *mq" +.Ft unsigned int +.Fn "mq_len" "struct mbuf_queue *mq" +.Ft int +.Fn "mq_empty" "struct mbuf_queue *mq" +.Ft unsigned int +.Fn "mq_drops" "struct mbuf_queue *mq" +.Ft void +.Fn "mq_set_maxlen" "struct mbuf_queue *mq" "unsigned int" +.Ft struct mbuf_queue +.Fn "MBUF_QUEUE_INITIALIZER" "unsigned int maxlen" "int ipl" .Bd -literal #define MLEN (MSIZE - sizeof(struct m_hdr)) #define MHLEN (MLEN - sizeof(struct pkthdr)) @@ -685,6 +740,205 @@ See for a description of .Fa how . .El +.Pp +The mbuf list and mbuf queue API provides implementions of data +structures and operations for managing lists of mbufs or for queueing +mbufs and lists of mbufs between contexts. +.Pp +mbuf_list structures support the following functionality: +.Pp +.Bl -enum -compact -offset indent +.It +Insertion of a new mbuf at the end of the list. +.It +Removal of an mbuf from the head of the list. +.It +Removal of the entire chain of mbufs on the list. +.El +.Bl -tag -width Ds +.It Fn "ml_init" "struct mbuf_list *ml" +Initialise the +.Fa ml +mbuf_list structure. +.It Fn "MBUF_LIST_INITIALIZER" +An initialiser for an mbuf_list structure declaration. +.It Fn "ml_enqueue" "struct mbuf_list *ml" "struct mbuf *m" +Enqueue mbuf +.Fa m +on the end of the +.Fa ml +mbuf list. +.It Fn "ml_dequeue" "struct mbuf_list *ml" +Dequeue an mbuf from the front of the +.Fa ml +mbuf list. +.It Fn "ml_dechain" "struct mbuf_list *ml" +Dequeues all mbufs from the +.Fa ml +mbuf list. +.It Fn "ml_len" "struct mbuf_list *ml" +Return the number of mbufs on the +.Fa ml +mbuf list. +.It Fn "ml_empty" "struct mbuf_list *ml" +Return if the +.Fa ml +mbuf list is empty. +.It Fn "MBUF_LIST_FOREACH" "struct mbuf_list *ml" "VARNAME" +A convenience macro that can be used to iterate over the contents of the +.Fa ml +mbuf list. +.Fa VARNAME +identifies the name (not the address) of an mbuf pointer that will +be set to each entry on the list. +Note that it is unsafe to modify the list while iterating over it. +.El +.Pp +mbuf_queue data structures provide a superset of the functionality +available in mbuf_lists, and protect themselves internally with a +.Xr mutex 9 , +making them useful for moving mbufs between contexts or subsystems. +Additionally, mbuf_queues provide a limit on the number of mbufs that +may be queued. +The additional functionality mbuf_queues provides is: +.Pp +.Bl -enum -compact -offset indent +.It +Insertion of the mbufs in an mbuf_list at the end of the queue. +.It +Removal of all the mbufs on the queue as an mbuf_list. +.El +.Bl -tag -width Ds +.It Fn "mq_init" "struct mbuf_queue *mq" "unsigned int maxlen" "int ipl" +Initialises the mbuf queue structure +.Fa mq . +The maximum number of mbufs that can be queued is specified with +.Fa maxlen . +The highest interrupt priority level the queue will be operated at is +specified via +.Fa ipl . +.It Fn "MBUF_QUEUE_INITIALIZER" "unsigned int maxlen" "int ipl" +Initialises an mbuf queue structure declaration. +The maximum number of mbufs that can be queued is specified with +.Fa maxlen . +The highest interrupt priority level the queue will be operated at is +specified via +.Fa ipl . +.It Fn "mq_enqueue" "struct mbuf_queue *mq" "struct mbuf *m" +Enqueue mbuf +.Fa m +on the end of the +.Fa mq +mbuf queue. +.It Fn "mq_dequeue" "struct mbuf_queue *mq" +Dequeue an mbuf from the front of the +.Fa mq +mbuf queue. +.It Fn "mq_enlist" "struct mbuf_queue *mq" "struct mbuf_list *ml" +Enqueue all the mbufs on the +.Fa ml +mbuf list on to the end of the +.Fa mq +mbuf queue. +Note, if the number of mbufs placed on the queue exceeds its maximum length, +the extra mbufs are NOT freed as they are with +.Fn mq_enqueue . +.It Fn "mq_delist" "struct mbuf_queue *mq" "struct mbuf_list *ml" +Dequeue all the mbufs on the +.Fa mq +mbuf queue on to the +.Fa ml +mbuf list. +.It Fn "mq_dechain" "struct mbuf_queue *mq" +Dequeue all mbufs from the +.Fa mq +mbuf queue. +.It Fn "mq_len" "struct mbuf_queue *mq" +Return the number of mbufs on the +.Fa ml +mbuf queue. +.It Fn "mq_empty" "struct mbuf_queue *mq" +Return if the +.Fa mq +mbuf queue is empty. +.It Fn "mq_drops" "struct mbuf_queue *mq" +Return how many mbufs were dropped and freed by +.Xr m_freem 9 +if the +.Fa mq +mbuf queue was too full. +.It Fn "mq_set_maxlen" "struct mbuf_queue *mq" "unsigned int" +Alter the maximum number of mbufs that can be queued on the +.Fa mq +mbuf queue. +Note, +.Fn mq_set_maxlen +will only set a new limit, it will not free any excess mbufs that may +already exist on the queue. +.El +.Sh CONTEXT +.Fn ml_init , +.Fn ml_enqueue , +.Fn ml_dequeue , +.Fn ml_dechain , +.Fn ml_len , +.Fn ml_empty , +.Fn MBUF_LIST_INITIALIZER , +.Fn MBUF_LIST_FOREACH , +.Fn mq_init , +.Fn mq_enqueue , +.Fn mq_dequeue , +.Fn mq_enlist , +.Fn mq_delist , +.Fn mq_dechain , +.Fn mq_len , +.Fn mq_empty , +.Fn mq_drops , +.Fn mq_set_maxlen , +.Fn MBUF_QUEUE_INITIALIZER +can be called during autoconf, from process context, or from interrupt context. +.Sh RETURN VALUES +.Fn ml_dequeue +and +.Fn mq_dequeue +return the mbuf that was at the head of their respective list or queue. +If the list or queue was empty, +.Dv NULL +is returned. +.Pp +.Fn ml_dechain +and +.Fn mq_dechain +return all the mbufs that were on the respective list or queues via +a pointer to an mbuf with the chain accessible via m_nextpkt members. +If the list or queue was empty, +.Dv NULL +is returned. +.Pp +.Fn ml_len +and +.Fn mq_len +return the number of mbufs on the list or queue respectively. +.Pp +.Fn ml_empty +and +.Fn mq_empty +return a non-zero value if the list or queue is empty, +otherwise 0. +.Pp +.Fn mq_enqueue +returns 0 if the mbuf was successfully queued, or non-zero if the +mbuf was freed because it would cause the queue to exceed its maximum +length. +.Pp +.Fn mq_enlist +returns 0 if the new length of the queue after adding the list is less than +the queue's maximum length, otherwise non-zero. +.Pp +.Fn mq_drops +returns the number of mbufs that were freed during +.Fn mq_enqueue +operations that would have caused the queue to exceed its maximum length. .Sh CODE REFERENCES The mbuf management functions are implemented in the files .Pa sys/kern/uipc_mbuf.c @@ -694,7 +948,9 @@ The function prototypes and the macros are located in .Pa sys/sys/mbuf.h . .Sh SEE ALSO .Xr netstat 1 , -.Xr mbuf_tags 9 +.Xr mbuf_tags 9 , +.Xr mutex 9 , +.Xr spl 9 .Rs .%A Jun-Ichiro Hagino .%T "Mbuf issues in 4.4BSD IPv6/IPsec support (experiences from KAME IPv6/IPsec implementation)" diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c index 5d217ad15a3..ae27b0c4444 100644 --- a/sys/kern/uipc_mbuf.c +++ b/sys/kern/uipc_mbuf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_mbuf.c,v 1.192 2014/07/13 15:52:38 tedu Exp $ */ +/* $OpenBSD: uipc_mbuf.c,v 1.193 2014/08/18 04:06:16 dlg Exp $ */ /* $NetBSD: uipc_mbuf.c,v 1.15.4.1 1996/06/13 17:11:44 cgd Exp $ */ /* @@ -1241,6 +1241,156 @@ m_print(void *v, m->m_ext.ext_free, m->m_ext.ext_arg); (*pr)("m_ext.ext_nextref: %p\tm_ext.ext_prevref: %p\n", m->m_ext.ext_nextref, m->m_ext.ext_prevref); + } } #endif + +/* + * mbuf lists + */ + +void ml_join(struct mbuf_list *, struct mbuf_list *); + +void +ml_init(struct mbuf_list *ml) +{ + ml->ml_head = ml->ml_tail = NULL; + ml->ml_len = 0; +} + +void +ml_enqueue(struct mbuf_list *ml, struct mbuf *m) +{ + if (ml->ml_tail == NULL) + ml->ml_head = ml->ml_tail = m; + else { + ml->ml_tail->m_nextpkt = m; + ml->ml_tail = m; + } + + m->m_nextpkt = NULL; + ml->ml_len++; +} + +void +ml_join(struct mbuf_list *mla, struct mbuf_list *mlb) +{ + if (mla->ml_tail == NULL) + *mla = *mlb; + else if (mlb->ml_tail != NULL) { + mla->ml_tail->m_nextpkt = mlb->ml_head; + mla->ml_tail = mlb->ml_tail; + mla->ml_len += mlb->ml_len; + + ml_init(mlb); + } +} + +struct mbuf * +ml_dequeue(struct mbuf_list *ml) +{ + struct mbuf *m; + + m = ml->ml_head; + if (m != NULL) { + ml->ml_head = m->m_nextpkt; + if (ml->ml_head == NULL) + ml->ml_tail = NULL; + + m->m_nextpkt = NULL; + ml->ml_len--; + } + + return (m); +} + +struct mbuf * +ml_dechain(struct mbuf_list *ml) +{ + struct mbuf *m0; + + m0 = ml->ml_head; + + ml_init(ml); + + return (m0); +} + +/* + * mbuf queues + */ + +void +mq_init(struct mbuf_queue *mq, u_int maxlen, int ipl) +{ + mtx_init(&mq->mq_mtx, ipl); + ml_init(&mq->mq_list); + mq->mq_maxlen = maxlen; +} + +int +mq_enqueue(struct mbuf_queue *mq, struct mbuf *m) +{ + int dropped = 0; + + mtx_enter(&mq->mq_mtx); + if (mq_len(mq) < mq->mq_maxlen) + ml_enqueue(&mq->mq_list, m); + else { + mq->mq_drops++; + dropped = 1; + } + mtx_leave(&mq->mq_mtx); + + if (dropped) + m_freem(m); + + return (dropped); +} + +struct mbuf * +mq_dequeue(struct mbuf_queue *mq) +{ + struct mbuf *m; + + mtx_enter(&mq->mq_mtx); + m = ml_dequeue(&mq->mq_list); + mtx_leave(&mq->mq_mtx); + + return (m); +} + +int +mq_enlist(struct mbuf_queue *mq, struct mbuf_list *ml) +{ + int full; + + mtx_enter(&mq->mq_mtx); + ml_join(&mq->mq_list, ml); + full = mq_len(mq) >= mq->mq_maxlen; + mtx_leave(&mq->mq_mtx); + + return (full); +} + +void +mq_delist(struct mbuf_queue *mq, struct mbuf_list *ml) +{ + mtx_enter(&mq->mq_mtx); + *ml = mq->mq_list; + ml_init(&mq->mq_list); + mtx_leave(&mq->mq_mtx); +} + +struct mbuf * +mq_dechain(struct mbuf_queue *mq) +{ + struct mbuf *m0; + + mtx_enter(&mq->mq_mtx); + m0 = ml_dechain(&mq->mq_list); + mtx_leave(&mq->mq_mtx); + + return (m0); +} diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index bedfdfe38a4..f202be72b03 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: mbuf.h,v 1.180 2014/07/13 09:52:48 dlg Exp $ */ +/* $OpenBSD: mbuf.h,v 1.181 2014/08/18 04:06:16 dlg Exp $ */ /* $NetBSD: mbuf.h,v 1.19 1996/02/09 18:25:14 christos Exp $ */ /* @@ -480,5 +480,56 @@ struct m_tag *m_tag_next(struct mbuf *, struct m_tag *); */ #define PACKET_TAG_MAXSIZE 52 +/* + * mbuf lists + */ + +#include + +struct mbuf_list { + struct mbuf *ml_head; + struct mbuf *ml_tail; + u_int ml_len; +}; + +#define MBUF_LIST_INITIALIZER() { NULL, NULL, 0 } + +void ml_init(struct mbuf_list *); +void ml_enqueue(struct mbuf_list *, struct mbuf *); +struct mbuf * ml_dequeue(struct mbuf_list *); +struct mbuf * ml_dechain(struct mbuf_list *); + +#define ml_len(_ml) ((_ml)->ml_len) +#define ml_empty(_ml) ((_ml)->ml_len == 0) + +#define MBUF_LIST_FOREACH(_ml, _m) \ + for ((_m) = (_ml)->ml_head; (_m) != NULL; (_m) = (_m)->m_nextpkt) + +/* + * mbuf queues + */ + +struct mbuf_queue { + struct mutex mq_mtx; + struct mbuf_list mq_list; + u_int mq_maxlen; + u_int mq_drops; +}; + +#define MBUF_QUEUE_INITIALIZER(_maxlen, _ipl) \ + { MUTEX_INITIALIZER(_ipl), MBUF_LIST_INITIALIZER(), (_maxlen), 0 } + +void mq_init(struct mbuf_queue *, u_int, int); +int mq_enqueue(struct mbuf_queue *, struct mbuf *); +struct mbuf * mq_dequeue(struct mbuf_queue *); +int mq_enlist(struct mbuf_queue *, struct mbuf_list *); +void mq_delist(struct mbuf_queue *, struct mbuf_list *); +struct mbuf * mq_dechain(struct mbuf_queue *); + +#define mq_len(_mq) ml_len(&(_mq)->mq_list) +#define mq_empty(_mq) ml_empty(&(_mq)->mq_list) +#define mq_drops(_mq) ((_mq)->mq_drops) +#define mq_set_maxlen(_mq, _l) ((_mq)->mq_maxlen = (_l)) + #endif /* _KERNEL */ #endif /* _SYS_MBUF_H_ */ -- 2.20.1