summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordlg <dlg@openbsd.org>2015-12-08 10:06:11 +0000
committerdlg <dlg@openbsd.org>2015-12-08 10:06:11 +0000
commit44430e2114803229a42ac89a0660eef0e372d9da (patch)
tree425e26a25aef8faa98f0e6c53071f83eb73b7618
parentMatch 3rd party Xbox 360 controllers, from Christian Heckendorf. (diff)
downloadwireguard-openbsd-44430e2114803229a42ac89a0660eef0e372d9da.tar.xz
wireguard-openbsd-44430e2114803229a42ac89a0660eef0e372d9da.zip
split the interface send queue (struct ifqueue) implementation out.
the intention is to make it more clear what belongs to a transmit queue and what belongs to an interface. suggested by and ok mpi@
-rw-r--r--sys/conf/files3
-rw-r--r--sys/net/if.c369
-rw-r--r--sys/net/if_var.h75
-rw-r--r--sys/net/ifq.c409
-rw-r--r--sys/net/ifq.h96
5 files changed, 511 insertions, 441 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 212a2923f3e..199aabe5e3b 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.609 2015/12/03 12:42:03 goda Exp $
+# $OpenBSD: files,v 1.610 2015/12/08 10:06:11 dlg Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -759,6 +759,7 @@ file net/art.c art
file net/bpf.c bpfilter needs-count
file net/bpf_filter.c bpfilter
file net/if.c
+file net/ifq.c
file net/if_ethersubr.c ether needs-flag
file net/if_etherip.c etherip needs-flag
file net/if_spppsubr.c sppp
diff --git a/sys/net/if.c b/sys/net/if.c
index bc2d21f6bb9..ff469a17b61 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if.c,v 1.422 2015/12/05 10:07:55 tedu Exp $ */
+/* $OpenBSD: if.c,v 1.423 2015/12/08 10:06:12 dlg Exp $ */
/* $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $ */
/*
@@ -557,68 +557,6 @@ if_start_locked(struct ifnet *ifp)
KERNEL_UNLOCK();
}
-static inline unsigned int
-ifq_enter(struct ifqueue *ifq)
-{
- return (atomic_inc_int_nv(&ifq->ifq_serializer) == 1);
-}
-
-static inline unsigned int
-ifq_leave(struct ifqueue *ifq)
-{
- if (atomic_cas_uint(&ifq->ifq_serializer, 1, 0) == 1)
- return (1);
-
- ifq->ifq_serializer = 1;
-
- return (0);
-}
-
-void
-if_start_mpsafe(struct ifnet *ifp)
-{
- struct ifqueue *ifq = &ifp->if_snd;
-
- if (!ifq_enter(ifq))
- return;
-
- do {
- if (__predict_false(!ISSET(ifp->if_flags, IFF_RUNNING))) {
- ifq->ifq_serializer = 0;
- wakeup_one(&ifq->ifq_serializer);
- return;
- }
-
- if (ifq_empty(ifq) || ifq_is_oactive(ifq))
- continue;
-
- ifp->if_start(ifp);
-
- } while (!ifq_leave(ifq));
-}
-
-void
-if_start_barrier(struct ifnet *ifp)
-{
- struct sleep_state sls;
- struct ifqueue *ifq = &ifp->if_snd;
-
- /* this should only be called from converted drivers */
- KASSERT(ISSET(ifp->if_xflags, IFXF_MPSAFE));
-
- /* drivers should only call this on the way down */
- KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING));
-
- if (ifq->ifq_serializer == 0)
- return;
-
- if_start_mpsafe(ifp); /* spin the wheel to guarantee a wakeup */
- do {
- sleep_setup(&sls, &ifq->ifq_serializer, PWAIT, "ifbar");
- sleep_finish(&sls, ifq->ifq_serializer != 0);
- } while (ifq->ifq_serializer != 0);
-}
-
int
if_enqueue(struct ifnet *ifp, struct mbuf *m)
{
@@ -2768,311 +2706,6 @@ niq_enlist(struct niqueue *niq, struct mbuf_list *ml)
return (rv);
}
-/*
- * send queues.
- */
-
-void *priq_alloc(void *);
-void priq_free(void *);
-int priq_enq(struct ifqueue *, struct mbuf *);
-struct mbuf *priq_deq_begin(struct ifqueue *, void **);
-void priq_deq_commit(struct ifqueue *, struct mbuf *, void *);
-void priq_purge(struct ifqueue *, struct mbuf_list *);
-
-const struct ifq_ops priq_ops = {
- priq_alloc,
- priq_free,
- priq_enq,
- priq_deq_begin,
- priq_deq_commit,
- priq_purge,
-};
-
-const struct ifq_ops * const ifq_priq_ops = &priq_ops;
-
-struct priq_list {
- struct mbuf *head;
- struct mbuf *tail;
-};
-
-struct priq {
- struct priq_list pq_lists[IFQ_NQUEUES];
-};
-
-void *
-priq_alloc(void *null)
-{
- return (malloc(sizeof(struct priq), M_DEVBUF, M_WAITOK | M_ZERO));
-}
-
-void
-priq_free(void *pq)
-{
- free(pq, M_DEVBUF, sizeof(struct priq));
-}
-
-int
-priq_enq(struct ifqueue *ifq, struct mbuf *m)
-{
- struct priq *pq;
- struct priq_list *pl;
-
- if (ifq_len(ifq) >= ifq->ifq_maxlen)
- return (ENOBUFS);
-
- pq = ifq->ifq_q;
- KASSERT(m->m_pkthdr.pf.prio <= IFQ_MAXPRIO);
- pl = &pq->pq_lists[m->m_pkthdr.pf.prio];
-
- m->m_nextpkt = NULL;
- if (pl->tail == NULL)
- pl->head = m;
- else
- pl->tail->m_nextpkt = m;
- pl->tail = m;
-
- return (0);
-}
-
-struct mbuf *
-priq_deq_begin(struct ifqueue *ifq, void **cookiep)
-{
- struct priq *pq = ifq->ifq_q;
- struct priq_list *pl;
- unsigned int prio = nitems(pq->pq_lists);
- struct mbuf *m;
-
- do {
- pl = &pq->pq_lists[--prio];
- m = pl->head;
- if (m != NULL) {
- *cookiep = pl;
- return (m);
- }
- } while (prio > 0);
-
- return (NULL);
-}
-
-void
-priq_deq_commit(struct ifqueue *ifq, struct mbuf *m, void *cookie)
-{
- struct priq_list *pl = cookie;
-
- KASSERT(pl->head == m);
-
- pl->head = m->m_nextpkt;
- m->m_nextpkt = NULL;
-
- if (pl->head == NULL)
- pl->tail = NULL;
-}
-
-void
-priq_purge(struct ifqueue *ifq, struct mbuf_list *ml)
-{
- struct priq *pq = ifq->ifq_q;
- struct priq_list *pl;
- unsigned int prio = nitems(pq->pq_lists);
- struct mbuf *m, *n;
-
- do {
- pl = &pq->pq_lists[--prio];
-
- for (m = pl->head; m != NULL; m = n) {
- n = m->m_nextpkt;
- ml_enqueue(ml, m);
- }
-
- pl->head = pl->tail = NULL;
- } while (prio > 0);
-}
-
-int
-ifq_enqueue_try(struct ifqueue *ifq, struct mbuf *m)
-{
- int rv;
-
- mtx_enter(&ifq->ifq_mtx);
- rv = ifq->ifq_ops->ifqop_enq(ifq, m);
- if (rv == 0)
- ifq->ifq_len++;
- else
- ifq->ifq_drops++;
- mtx_leave(&ifq->ifq_mtx);
-
- return (rv);
-}
-
-int
-ifq_enqueue(struct ifqueue *ifq, struct mbuf *m)
-{
- int err;
-
- err = ifq_enqueue_try(ifq, m);
- if (err != 0)
- m_freem(m);
-
- return (err);
-}
-
-struct mbuf *
-ifq_deq_begin(struct ifqueue *ifq)
-{
- struct mbuf *m = NULL;
- void *cookie;
-
- mtx_enter(&ifq->ifq_mtx);
- if (ifq->ifq_len == 0 ||
- (m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie)) == NULL) {
- mtx_leave(&ifq->ifq_mtx);
- return (NULL);
- }
-
- m->m_pkthdr.ph_cookie = cookie;
-
- return (m);
-}
-
-void
-ifq_deq_commit(struct ifqueue *ifq, struct mbuf *m)
-{
- void *cookie;
-
- KASSERT(m != NULL);
- cookie = m->m_pkthdr.ph_cookie;
-
- ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie);
- ifq->ifq_len--;
- mtx_leave(&ifq->ifq_mtx);
-}
-
-void
-ifq_deq_rollback(struct ifqueue *ifq, struct mbuf *m)
-{
- KASSERT(m != NULL);
-
- mtx_leave(&ifq->ifq_mtx);
-}
-
-struct mbuf *
-ifq_dequeue(struct ifqueue *ifq)
-{
- struct mbuf *m;
-
- m = ifq_deq_begin(ifq);
- if (m == NULL)
- return (NULL);
-
- ifq_deq_commit(ifq, m);
-
- return (m);
-}
-
-unsigned int
-ifq_purge(struct ifqueue *ifq)
-{
- struct mbuf_list ml = MBUF_LIST_INITIALIZER();
- unsigned int rv;
-
- mtx_enter(&ifq->ifq_mtx);
- ifq->ifq_ops->ifqop_purge(ifq, &ml);
- rv = ifq->ifq_len;
- ifq->ifq_len = 0;
- ifq->ifq_drops += rv;
- mtx_leave(&ifq->ifq_mtx);
-
- KASSERT(rv == ml_len(&ml));
-
- ml_purge(&ml);
-
- return (rv);
-}
-
-void
-ifq_init(struct ifqueue *ifq)
-{
- mtx_init(&ifq->ifq_mtx, IPL_NET);
- ifq->ifq_drops = 0;
-
- /* default to priq */
- ifq->ifq_ops = &priq_ops;
- ifq->ifq_q = priq_ops.ifqop_alloc(NULL);
-
- ifq->ifq_serializer = 0;
- ifq->ifq_len = 0;
-
- if (ifq->ifq_maxlen == 0)
- ifq_set_maxlen(ifq, IFQ_MAXLEN);
-}
-
-void
-ifq_attach(struct ifqueue *ifq, const struct ifq_ops *newops, void *opsarg)
-{
- struct mbuf_list ml = MBUF_LIST_INITIALIZER();
- struct mbuf_list free_ml = MBUF_LIST_INITIALIZER();
- struct mbuf *m;
- const struct ifq_ops *oldops;
- void *newq, *oldq;
-
- newq = newops->ifqop_alloc(opsarg);
-
- mtx_enter(&ifq->ifq_mtx);
- ifq->ifq_ops->ifqop_purge(ifq, &ml);
- ifq->ifq_len = 0;
-
- oldops = ifq->ifq_ops;
- oldq = ifq->ifq_q;
-
- ifq->ifq_ops = newops;
- ifq->ifq_q = newq;
-
- while ((m = ml_dequeue(&ml)) != NULL) {
- if (ifq->ifq_ops->ifqop_enq(ifq, m) != 0) {
- ifq->ifq_drops++;
- ml_enqueue(&free_ml, m);
- } else
- ifq->ifq_len++;
- }
- mtx_leave(&ifq->ifq_mtx);
-
- oldops->ifqop_free(oldq);
-
- ml_purge(&free_ml);
-}
-
-void *
-ifq_q_enter(struct ifqueue *ifq, const struct ifq_ops *ops)
-{
- mtx_enter(&ifq->ifq_mtx);
- if (ifq->ifq_ops == ops)
- return (ifq->ifq_q);
-
- mtx_leave(&ifq->ifq_mtx);
-
- return (NULL);
-}
-
-void
-ifq_q_leave(struct ifqueue *ifq, void *q)
-{
- KASSERT(q == ifq->ifq_q);
- mtx_leave(&ifq->ifq_mtx);
-}
-
-void
-ifq_destroy(struct ifqueue *ifq)
-{
- struct mbuf_list ml = MBUF_LIST_INITIALIZER();
-
- /* don't need to lock because this is the last use of the ifq */
-
- ifq->ifq_ops->ifqop_purge(ifq, &ml);
- ifq->ifq_ops->ifqop_free(ifq->ifq_q);
-
- ml_purge(&ml);
-}
-
__dead void
unhandled_af(int af)
{
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index 5f57c6920bc..631e4cd9f21 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_var.h,v 1.64 2015/12/05 16:24:59 mpi Exp $ */
+/* $OpenBSD: if_var.h,v 1.65 2015/12/08 10:06:12 dlg Exp $ */
/* $NetBSD: if.h,v 1.23 1996/05/07 02:40:27 thorpej Exp $ */
/*
@@ -44,6 +44,8 @@
#include <sys/refcnt.h>
#include <sys/time.h>
+#include <net/ifq.h>
+
/*
* Structures defining a network interface, providing a packet
* transport mechanism (ala level 0 of the PUP protocols).
@@ -91,35 +93,6 @@ struct if_clone {
{ { 0 }, name, sizeof(name) - 1, create, destroy }
/*
- * Structure defining the send queue for a network interface.
- */
-
-struct ifqueue;
-
-struct ifq_ops {
- void *(*ifqop_alloc)(void *);
- void (*ifqop_free)(void *);
- int (*ifqop_enq)(struct ifqueue *, struct mbuf *);
- struct mbuf *(*ifqop_deq_begin)(struct ifqueue *, void **);
- void (*ifqop_deq_commit)(struct ifqueue *,
- struct mbuf *, void *);
- void (*ifqop_purge)(struct ifqueue *,
- struct mbuf_list *);
-};
-
-struct ifqueue {
- struct mutex ifq_mtx;
- uint64_t ifq_drops;
- const struct ifq_ops *ifq_ops;
- void *ifq_q;
- unsigned int ifq_len;
- unsigned int ifq_serializer;
- unsigned int ifq_oactive;
-
- unsigned int ifq_maxlen;
-};
-
-/*
* Structure defining a queue for a network interface.
*
* (Would like to call this struct ``if'', but C isn't PL/1.)
@@ -262,48 +235,6 @@ struct ifg_list {
TAILQ_ENTRY(ifg_list) ifgl_next;
};
-/*
- * Interface send queues.
- */
-
-void ifq_init(struct ifqueue *);
-void ifq_attach(struct ifqueue *, const struct ifq_ops *, void *);
-void ifq_destroy(struct ifqueue *);
-int ifq_enqueue_try(struct ifqueue *, struct mbuf *);
-int ifq_enqueue(struct ifqueue *, struct mbuf *);
-struct mbuf *ifq_deq_begin(struct ifqueue *);
-void ifq_deq_commit(struct ifqueue *, struct mbuf *);
-void ifq_deq_rollback(struct ifqueue *, struct mbuf *);
-struct mbuf *ifq_dequeue(struct ifqueue *);
-unsigned int ifq_purge(struct ifqueue *);
-void *ifq_q_enter(struct ifqueue *, const struct ifq_ops *);
-void ifq_q_leave(struct ifqueue *, void *);
-
-#define ifq_len(_ifq) ((_ifq)->ifq_len)
-#define ifq_empty(_ifq) (ifq_len(_ifq) == 0)
-#define ifq_set_maxlen(_ifq, _l) ((_ifq)->ifq_maxlen = (_l))
-
-static inline void
-ifq_set_oactive(struct ifqueue *ifq)
-{
- ifq->ifq_oactive = 1;
-}
-
-static inline void
-ifq_clr_oactive(struct ifqueue *ifq)
-{
- ifq->ifq_oactive = 0;
-}
-
-static inline unsigned int
-ifq_is_oactive(struct ifqueue *ifq)
-{
- return (ifq->ifq_oactive);
-}
-
-extern const struct ifq_ops * const ifq_priq_ops;
-
-#define IFQ_MAXLEN 256
#define IFNET_SLOWHZ 1 /* granularity is 1 second */
/*
diff --git a/sys/net/ifq.c b/sys/net/ifq.c
new file mode 100644
index 00000000000..51d41dac4d9
--- /dev/null
+++ b/sys/net/ifq.c
@@ -0,0 +1,409 @@
+/* $OpenBSD: ifq.c,v 1.1 2015/12/08 10:06:12 dlg Exp $ */
+
+/*
+ * Copyright (c) 2015 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/atomic.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+/*
+ * priq glue
+ */
+void *priq_alloc(void *);
+void priq_free(void *);
+int priq_enq(struct ifqueue *, struct mbuf *);
+struct mbuf *priq_deq_begin(struct ifqueue *, void **);
+void priq_deq_commit(struct ifqueue *, struct mbuf *, void *);
+void priq_purge(struct ifqueue *, struct mbuf_list *);
+
+const struct ifq_ops priq_ops = {
+ priq_alloc,
+ priq_free,
+ priq_enq,
+ priq_deq_begin,
+ priq_deq_commit,
+ priq_purge,
+};
+
+const struct ifq_ops * const ifq_priq_ops = &priq_ops;
+
+/*
+ * priq internal structures
+ */
+
+struct priq_list {
+ struct mbuf *head;
+ struct mbuf *tail;
+};
+
+struct priq {
+ struct priq_list pq_lists[IFQ_NQUEUES];
+};
+
+/*
+ * ifqueue serialiser
+ */
+
+static inline unsigned int
+ifq_enter(struct ifqueue *ifq)
+{
+ return (atomic_inc_int_nv(&ifq->ifq_serializer) == 1);
+}
+
+static inline unsigned int
+ifq_leave(struct ifqueue *ifq)
+{
+ if (atomic_cas_uint(&ifq->ifq_serializer, 1, 0) == 1)
+ return (1);
+
+ ifq->ifq_serializer = 1;
+
+ return (0);
+}
+
+void
+if_start_mpsafe(struct ifnet *ifp)
+{
+ struct ifqueue *ifq = &ifp->if_snd;
+
+ if (!ifq_enter(ifq))
+ return;
+
+ do {
+ if (__predict_false(!ISSET(ifp->if_flags, IFF_RUNNING))) {
+ ifq->ifq_serializer = 0;
+ wakeup_one(&ifq->ifq_serializer);
+ return;
+ }
+
+ if (ifq_empty(ifq) || ifq_is_oactive(ifq))
+ continue;
+
+ ifp->if_start(ifp);
+
+ } while (!ifq_leave(ifq));
+}
+
+void
+if_start_barrier(struct ifnet *ifp)
+{
+ struct sleep_state sls;
+ struct ifqueue *ifq = &ifp->if_snd;
+
+ /* this should only be called from converted drivers */
+ KASSERT(ISSET(ifp->if_xflags, IFXF_MPSAFE));
+
+ /* drivers should only call this on the way down */
+ KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING));
+
+ if (ifq->ifq_serializer == 0)
+ return;
+
+ if_start_mpsafe(ifp); /* spin the wheel to guarantee a wakeup */
+ do {
+ sleep_setup(&sls, &ifq->ifq_serializer, PWAIT, "ifbar");
+ sleep_finish(&sls, ifq->ifq_serializer != 0);
+ } while (ifq->ifq_serializer != 0);
+}
+
+/*
+ * ifqueue mbuf queue API
+ */
+
+void
+ifq_init(struct ifqueue *ifq)
+{
+ mtx_init(&ifq->ifq_mtx, IPL_NET);
+ ifq->ifq_drops = 0;
+
+ /* default to priq */
+ ifq->ifq_ops = &priq_ops;
+ ifq->ifq_q = priq_ops.ifqop_alloc(NULL);
+
+ ifq->ifq_serializer = 0;
+ ifq->ifq_len = 0;
+
+ if (ifq->ifq_maxlen == 0)
+ ifq_set_maxlen(ifq, IFQ_MAXLEN);
+}
+
+void
+ifq_attach(struct ifqueue *ifq, const struct ifq_ops *newops, void *opsarg)
+{
+ struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+ struct mbuf_list free_ml = MBUF_LIST_INITIALIZER();
+ struct mbuf *m;
+ const struct ifq_ops *oldops;
+ void *newq, *oldq;
+
+ newq = newops->ifqop_alloc(opsarg);
+
+ mtx_enter(&ifq->ifq_mtx);
+ ifq->ifq_ops->ifqop_purge(ifq, &ml);
+ ifq->ifq_len = 0;
+
+ oldops = ifq->ifq_ops;
+ oldq = ifq->ifq_q;
+
+ ifq->ifq_ops = newops;
+ ifq->ifq_q = newq;
+
+ while ((m = ml_dequeue(&ml)) != NULL) {
+ if (ifq->ifq_ops->ifqop_enq(ifq, m) != 0) {
+ ifq->ifq_drops++;
+ ml_enqueue(&free_ml, m);
+ } else
+ ifq->ifq_len++;
+ }
+ mtx_leave(&ifq->ifq_mtx);
+
+ oldops->ifqop_free(oldq);
+
+ ml_purge(&free_ml);
+}
+
+void
+ifq_destroy(struct ifqueue *ifq)
+{
+ struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+
+ /* don't need to lock because this is the last use of the ifq */
+
+ ifq->ifq_ops->ifqop_purge(ifq, &ml);
+ ifq->ifq_ops->ifqop_free(ifq->ifq_q);
+
+ ml_purge(&ml);
+}
+
+int
+ifq_enqueue_try(struct ifqueue *ifq, struct mbuf *m)
+{
+ int rv;
+
+ mtx_enter(&ifq->ifq_mtx);
+ rv = ifq->ifq_ops->ifqop_enq(ifq, m);
+ if (rv == 0)
+ ifq->ifq_len++;
+ else
+ ifq->ifq_drops++;
+ mtx_leave(&ifq->ifq_mtx);
+
+ return (rv);
+}
+
+int
+ifq_enqueue(struct ifqueue *ifq, struct mbuf *m)
+{
+ int err;
+
+ err = ifq_enqueue_try(ifq, m);
+ if (err != 0)
+ m_freem(m);
+
+ return (err);
+}
+
+struct mbuf *
+ifq_deq_begin(struct ifqueue *ifq)
+{
+ struct mbuf *m = NULL;
+ void *cookie;
+
+ mtx_enter(&ifq->ifq_mtx);
+ if (ifq->ifq_len == 0 ||
+ (m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie)) == NULL) {
+ mtx_leave(&ifq->ifq_mtx);
+ return (NULL);
+ }
+
+ m->m_pkthdr.ph_cookie = cookie;
+
+ return (m);
+}
+
+void
+ifq_deq_commit(struct ifqueue *ifq, struct mbuf *m)
+{
+ void *cookie;
+
+ KASSERT(m != NULL);
+ cookie = m->m_pkthdr.ph_cookie;
+
+ ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie);
+ ifq->ifq_len--;
+ mtx_leave(&ifq->ifq_mtx);
+}
+
+void
+ifq_deq_rollback(struct ifqueue *ifq, struct mbuf *m)
+{
+ KASSERT(m != NULL);
+
+ mtx_leave(&ifq->ifq_mtx);
+}
+
+struct mbuf *
+ifq_dequeue(struct ifqueue *ifq)
+{
+ struct mbuf *m;
+
+ m = ifq_deq_begin(ifq);
+ if (m == NULL)
+ return (NULL);
+
+ ifq_deq_commit(ifq, m);
+
+ return (m);
+}
+
+unsigned int
+ifq_purge(struct ifqueue *ifq)
+{
+ struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+ unsigned int rv;
+
+ mtx_enter(&ifq->ifq_mtx);
+ ifq->ifq_ops->ifqop_purge(ifq, &ml);
+ rv = ifq->ifq_len;
+ ifq->ifq_len = 0;
+ ifq->ifq_drops += rv;
+ mtx_leave(&ifq->ifq_mtx);
+
+ KASSERT(rv == ml_len(&ml));
+
+ ml_purge(&ml);
+
+ return (rv);
+}
+
+void *
+ifq_q_enter(struct ifqueue *ifq, const struct ifq_ops *ops)
+{
+ mtx_enter(&ifq->ifq_mtx);
+ if (ifq->ifq_ops == ops)
+ return (ifq->ifq_q);
+
+ mtx_leave(&ifq->ifq_mtx);
+
+ return (NULL);
+}
+
+void
+ifq_q_leave(struct ifqueue *ifq, void *q)
+{
+ KASSERT(q == ifq->ifq_q);
+ mtx_leave(&ifq->ifq_mtx);
+}
+
+/*
+ * priq implementation
+ */
+
+void *
+priq_alloc(void *null)
+{
+ return (malloc(sizeof(struct priq), M_DEVBUF, M_WAITOK | M_ZERO));
+}
+
+void
+priq_free(void *pq)
+{
+ free(pq, M_DEVBUF, sizeof(struct priq));
+}
+
+int
+priq_enq(struct ifqueue *ifq, struct mbuf *m)
+{
+ struct priq *pq;
+ struct priq_list *pl;
+
+ if (ifq_len(ifq) >= ifq->ifq_maxlen)
+ return (ENOBUFS);
+
+ pq = ifq->ifq_q;
+ KASSERT(m->m_pkthdr.pf.prio <= IFQ_MAXPRIO);
+ pl = &pq->pq_lists[m->m_pkthdr.pf.prio];
+
+ m->m_nextpkt = NULL;
+ if (pl->tail == NULL)
+ pl->head = m;
+ else
+ pl->tail->m_nextpkt = m;
+ pl->tail = m;
+
+ return (0);
+}
+
+struct mbuf *
+priq_deq_begin(struct ifqueue *ifq, void **cookiep)
+{
+ struct priq *pq = ifq->ifq_q;
+ struct priq_list *pl;
+ unsigned int prio = nitems(pq->pq_lists);
+ struct mbuf *m;
+
+ do {
+ pl = &pq->pq_lists[--prio];
+ m = pl->head;
+ if (m != NULL) {
+ *cookiep = pl;
+ return (m);
+ }
+ } while (prio > 0);
+
+ return (NULL);
+}
+
+void
+priq_deq_commit(struct ifqueue *ifq, struct mbuf *m, void *cookie)
+{
+ struct priq_list *pl = cookie;
+
+ KASSERT(pl->head == m);
+
+ pl->head = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+
+ if (pl->head == NULL)
+ pl->tail = NULL;
+}
+
+void
+priq_purge(struct ifqueue *ifq, struct mbuf_list *ml)
+{
+ struct priq *pq = ifq->ifq_q;
+ struct priq_list *pl;
+ unsigned int prio = nitems(pq->pq_lists);
+ struct mbuf *m, *n;
+
+ do {
+ pl = &pq->pq_lists[--prio];
+
+ for (m = pl->head; m != NULL; m = n) {
+ n = m->m_nextpkt;
+ ml_enqueue(ml, m);
+ }
+
+ pl->head = pl->tail = NULL;
+ } while (prio > 0);
+}
diff --git a/sys/net/ifq.h b/sys/net/ifq.h
new file mode 100644
index 00000000000..e16dbf4f399
--- /dev/null
+++ b/sys/net/ifq.h
@@ -0,0 +1,96 @@
+/* $OpenBSD: ifq.h,v 1.1 2015/12/08 10:06:12 dlg Exp $ */
+
+/*
+ * Copyright (c) 2015 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _NET_IFQ_H_
+#define _NET_IFQ_H_
+
+struct ifnet;
+
+struct ifq_ops;
+
+struct ifqueue {
+ struct mutex ifq_mtx;
+ uint64_t ifq_drops;
+ const struct ifq_ops *ifq_ops;
+ void *ifq_q;
+ unsigned int ifq_len;
+ unsigned int ifq_serializer;
+ unsigned int ifq_oactive;
+
+ unsigned int ifq_maxlen;
+};
+
+#ifdef _KERNEL
+
+#define IFQ_MAXLEN 256
+
+struct ifq_ops {
+ void *(*ifqop_alloc)(void *);
+ void (*ifqop_free)(void *);
+ int (*ifqop_enq)(struct ifqueue *, struct mbuf *);
+ struct mbuf *(*ifqop_deq_begin)(struct ifqueue *, void **);
+ void (*ifqop_deq_commit)(struct ifqueue *,
+ struct mbuf *, void *);
+ void (*ifqop_purge)(struct ifqueue *,
+ struct mbuf_list *);
+};
+
+/*
+ * Interface send queues.
+ */
+
+void ifq_init(struct ifqueue *);
+void ifq_attach(struct ifqueue *, const struct ifq_ops *, void *);
+void ifq_destroy(struct ifqueue *);
+int ifq_enqueue_try(struct ifqueue *, struct mbuf *);
+int ifq_enqueue(struct ifqueue *, struct mbuf *);
+struct mbuf *ifq_deq_begin(struct ifqueue *);
+void ifq_deq_commit(struct ifqueue *, struct mbuf *);
+void ifq_deq_rollback(struct ifqueue *, struct mbuf *);
+struct mbuf *ifq_dequeue(struct ifqueue *);
+unsigned int ifq_purge(struct ifqueue *);
+void *ifq_q_enter(struct ifqueue *, const struct ifq_ops *);
+void ifq_q_leave(struct ifqueue *, void *);
+
+#define ifq_len(_ifq) ((_ifq)->ifq_len)
+#define ifq_empty(_ifq) (ifq_len(_ifq) == 0)
+#define ifq_set_maxlen(_ifq, _l) ((_ifq)->ifq_maxlen = (_l))
+
+static inline void
+ifq_set_oactive(struct ifqueue *ifq)
+{
+ ifq->ifq_oactive = 1;
+}
+
+static inline void
+ifq_clr_oactive(struct ifqueue *ifq)
+{
+ ifq->ifq_oactive = 0;
+}
+
+static inline unsigned int
+ifq_is_oactive(struct ifqueue *ifq)
+{
+ return (ifq->ifq_oactive);
+}
+
+extern const struct ifq_ops * const ifq_priq_ops;
+
+#endif /* _KERNEL */
+
+#endif /* _NET_IFQ_H_ */