--- //depot/vendor/freebsd/src/sys/dev/em/if_em.c	2006/07/27 00:46:24
+++ //depot/user/rwatson/ifnet/src/sys/dev/em/if_em.c	2006/07/29 18:43:14
@@ -735,6 +735,95 @@
 	EM_UNLOCK(sc);
 }
 
+static int
+em_startmbuf(struct ifnet *ifp, struct mbuf *m)
+{
+        struct mbuf    *m_head;
+        struct em_softc *sc = ifp->if_softc;
+	struct ifqueue *ifq = (struct ifqueue *)&ifp->if_snd;
+
+	/*
+	 * Three cases:
+	 *
+	 * (1) Interface isn't running, link is down, or is already active,
+	 *     etc, simply enqueue.
+	 *
+	 * (2) The interface is running, not too busy, and we have no mbufs
+	 *     in the ifnet send queue, so try to hand directly to hardware.
+	 *
+	 * (3) The interface is running, but we have a backlog.  Insert the
+	 *     current mbuf into the queue and process in-order, if possible.
+	 */
+	EM_LOCK(sc);
+	if (((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
+	    IFF_DRV_RUNNING) || !sc->link_active) {
+		if (_IF_QFULL(ifq)) {
+			_IF_DROP(ifq);
+			EM_UNLOCK(sc);
+			m_freem(m);
+			return (ENOBUFS);
+		}
+		_IF_ENQUEUE(ifq, m);
+		EM_UNLOCK(sc);
+		return (0);
+	}
+
+	/*
+	 * XXXRW: Various cases here have historically counted as successes,
+	 * but perhaps they should return ENOBUFS?
+	 */
+	if (_IF_QLEN(ifq) == 0) {
+	 	/*
+		 * em_encap() can modify our pointer, and or make it NULL on
+		 * failure.  In that event, we can't enqueue.
+		 */
+		if (em_encap(sc, &m)) {
+			if (m == NULL) {
+				EM_UNLOCK(sc);
+				return (0);
+			}
+			ifp->if_flags |= IFF_DRV_OACTIVE;
+			_IF_PREPEND(ifq, m);
+			EM_UNLOCK(sc);
+			return (0);
+		}
+		BPF_MTAP(ifp, m);
+		ifp->if_timer = EM_TX_TIMEOUT;
+		EM_UNLOCK(sc);
+		return (0);
+	}
+
+	if (_IF_QFULL(ifq)) {
+		_IF_DROP(ifq);
+		EM_UNLOCK(sc);
+		m_freem(m);
+		return (ENOBUFS);
+	}
+	_IF_ENQUEUE(ifq, m);
+
+	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
+		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
+		if (m_head == NULL)
+			break;
+	 	/*
+		 * em_encap() can modify our pointer, and or make it NULL on
+		 * failure.  In that event, we can't requeue.
+		 */
+		if (em_encap(sc, &m_head)) {
+			if (m_head == NULL)
+				break;
+			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
+			break;
+		}
+		BPF_MTAP(ifp, m_head);
+		ifp->if_timer = EM_TX_TIMEOUT;
+	}
+
+	EM_UNLOCK(sc);
+	return (0);
+}
+
 /*********************************************************************
  *  Ioctl entry point
  *
@@ -2154,6 +2243,7 @@
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = em_ioctl;
 	ifp->if_start = em_start;
+	ifp->if_startmbuf = em_startmbuf;
 	ifp->if_watchdog = em_watchdog;
 	IFQ_SET_MAXLEN(&ifp->if_snd, sc->num_tx_desc - 1);
 	ifp->if_snd.ifq_drv_maxlen = sc->num_tx_desc - 1;
--- //depot/vendor/freebsd/src/sys/net/if.c	2006/07/09 06:06:25
+++ //depot/user/rwatson/ifnet/src/sys/net/if.c	2006/07/26 17:32:50
@@ -2486,28 +2486,111 @@
 	(ifp->if_start)(ifp);
 }
 
+static int	startmbuf_enabled;
+SYSCTL_INT(_net, OID_AUTO, startmbuf_enabled, CTLFLAG_RW, &startmbuf_enabled,
+    0, "");
+
+/*
+ * XXXRW:
+ *
+ * if_var.h and the interface handoff are some of the nastiest pieces of the
+ * BSD network stack.  Generations of hacks, variants, inconsistency, and
+ * foolishness have resulted in essentially unreadable code.  For example,
+ * why are the ifq_* interfaces the ones that use the default ifnet send
+ * queue, and the if_* interfaces the ones that use alternative queues,
+ * possibly with no ifnet at all?  And why do some interfaces return errno
+ * values, but others booleans?
+ */
+
+/*
+ * Handoff function for simple ifnet structures.  Returns an errno value.
+ */
 int
-if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp, int adjust)
+ifq_handoff(struct ifnet *ifp, struct mbuf *m, int adjust)
+{
+	int error, len, startmbuf;
+	short mflags;
+
+	len = m->m_pkthdr.len;
+	mflags = m->m_flags;
+
+	if (startmbuf_enabled && ifp->if_startmbuf != NULL)
+		startmbuf = 1;
+	else
+		startmbuf = 0;
+
+	if (startmbuf)
+		error = ifp->if_startmbuf(ifp, m);
+	else
+		IFQ_ENQUEUE(&ifp->if_snd, m, error);
+	if (error == 0) {
+		ifp->if_obytes += len + adjust;
+		if (mflags & (M_BCAST|M_MCAST))
+			ifp->if_omcasts++;
+	}
+	if (!startmbuf && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0)
+		if_start(ifp);
+	return (error);
+}
+
+/*
+ * Handoff function for an ifqueue with an optionally affilitiated ifnet.
+ * Returns a boolean.
+ */
+int
+if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp,
+    int adjust)
+{
+	int len, active, startmbuf, success;
+	short mflags;
+
+	active = 0;
+	len = m->m_pkthdr.len;
+	mflags = m->m_flags;
+
+	if (startmbuf_enabled && ifp != NULL && ifp->if_startmbuf != NULL)
+		startmbuf = 1;
+	else
+		startmbuf = 0;
+
+	if (startmbuf)
+		success = (ifp->if_startmbuf(ifp, m) == 0);
+	else {
+		IF_LOCK(ifq);
+		if (_IF_QFULL(ifq)) {
+			_IF_DROP(ifq);
+			m_freem(m);
+			success = 0;
+		} else {
+			_IF_ENQUEUE(ifq, m);
+			success = 1;
+		}
+		IF_UNLOCK(ifq);
+		if (ifp != NULL && !(ifp->if_drv_flags & IFF_DRV_OACTIVE))
+			if_start(ifp);
+	}
+	if (success && ifp != NULL) {
+		ifp->if_obytes += len + adjust;
+		if (m->m_flags & (M_BCAST|M_MCAST))
+			ifp->if_omcasts++;
+	}
+	return (success);
+}
+
+/*
+ * Utility function to be used by device drivers when they need to enqueue a
+ * packet to an interface-related queue rather than immediately delivering.
+ */
+int
+if_startmbuf_enqueue(struct ifqueue *ifq, struct mbuf *m)
 {
-	int active = 0;
 
-	IF_LOCK(ifq);
 	if (_IF_QFULL(ifq)) {
 		_IF_DROP(ifq);
-		IF_UNLOCK(ifq);
 		m_freem(m);
 		return (0);
 	}
-	if (ifp != NULL) {
-		ifp->if_obytes += m->m_pkthdr.len + adjust;
-		if (m->m_flags & (M_BCAST|M_MCAST))
-			ifp->if_omcasts++;
-		active = ifp->if_drv_flags & IFF_DRV_OACTIVE;
-	}
 	_IF_ENQUEUE(ifq, m);
-	IF_UNLOCK(ifq);
-	if (ifp != NULL && !active)
-		if_start(ifp);
 	return (1);
 }
 
--- //depot/vendor/freebsd/src/sys/net/if_var.h	2006/06/19 22:21:22
+++ //depot/user/rwatson/ifnet/src/sys/net/if_var.h	2006/07/30 10:11:54
@@ -162,7 +162,8 @@
 		(struct ifnet *, struct sockaddr **, struct sockaddr *);
 	struct	ifaddr	*if_addr;	/* pointer to link-level address */
 	void	*if_spare2;		/* spare pointer 2 */
-	void	*if_spare3;		/* spare pointer 3 */
+	int	(*if_startmbuf)		/* enqueue and start output */
+		(struct ifnet *, struct mbuf *);
 	int	if_drv_flags;		/* driver-managed status flags */
 	u_int	if_spare_flags2;	/* spare flags 2 */
 	struct  ifaltq if_snd;		/* output queue (includes altq) */
@@ -370,12 +371,15 @@
 		mtx_unlock(&Giant);					\
 } while (0)
 
+int	ifq_handoff(struct ifnet *ifp, struct mbuf *m, int adjust);
 int	if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp,
 	    int adjust);
+int	if_startmbuf_enqueue(struct ifqueue *ifq, struct mbuf *m);
+
+#define	IF_HANDOFF_ADJ(ifq, m, ifp, adj)	\
+	if_handoff((struct ifqueue *)ifq, m, ifp, adj)
 #define	IF_HANDOFF(ifq, m, ifp)			\
 	if_handoff((struct ifqueue *)ifq, m, ifp, 0)
-#define	IF_HANDOFF_ADJ(ifq, m, ifp, adj)	\
-	if_handoff((struct ifqueue *)ifq, m, ifp, adj)
 
 void	if_start(struct ifnet *);
 
@@ -459,25 +463,8 @@
 #define	IFQ_INC_DROPS(ifq)		((ifq)->ifq_drops++)
 #define	IFQ_SET_MAXLEN(ifq, len)	((ifq)->ifq_maxlen = (len))
 
-/*
- * The IFF_DRV_OACTIVE test should really occur in the device driver, not in
- * the handoff logic, as that flag is locked by the device driver.
- */
-#define	IFQ_HANDOFF_ADJ(ifp, m, adj, err)				\
-do {									\
-	int len;							\
-	short mflags;							\
-									\
-	len = (m)->m_pkthdr.len;					\
-	mflags = (m)->m_flags;						\
-	IFQ_ENQUEUE(&(ifp)->if_snd, m, err);				\
-	if ((err) == 0) {						\
-		(ifp)->if_obytes += len + (adj);			\
-		if (mflags & M_MCAST)					\
-			(ifp)->if_omcasts++;				\
-		if (((ifp)->if_drv_flags & IFF_DRV_OACTIVE) == 0)	\
-			if_start(ifp);					\
-	}								\
+#define	IFQ_HANDOFF_ADJ(ifp, m, adj, err) do {				\
+	err = ifq_handoff(ifp, m, adj);					\
 } while (0)
 
 #define	IFQ_HANDOFF(ifp, m, err)					\
