introduce mbuf list and queue apis. both manage fifo lists of mbufs
authordlg <dlg@openbsd.org>
Mon, 18 Aug 2014 04:06:16 +0000 (04:06 +0000)
committerdlg <dlg@openbsd.org>
Mon, 18 Aug 2014 04:06:16 +0000 (04:06 +0000)
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
share/man/man9/mbuf.9
sys/kern/uipc_mbuf.c
sys/sys/mbuf.h

index 5ca494d..21b3edf 100644 (file)
@@ -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 \
index dd6e541..f10da3a 100644 (file)
@@ -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 <jjbg@openbsd.org>
 .\" All rights reserved.
 .\" (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
 .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)"
index 5d217ad..ae27b0c 100644 (file)
@@ -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);
+}
index bedfdfe..f202be7 100644 (file)
@@ -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 <sys/mutex.h>
+
+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_ */