Index: sys/net/if.c
===================================================================
--- sys/net/if.c	(revision 279670)
+++ sys/net/if.c	(working copy)
@@ -3538,12 +3538,21 @@ if_foreach_maddr(if_t ifp, ifmaddr_cb_t cb, void *
 
 /*
  * Generic software queue, that many non-high-end drivers use.  For now
- * it is minimalistic version of classic BSD ifqueue, but we can swap it
- * to any other implementation later.
+ * it is based on classic BSD ifqueue, but we can swap it to any other
+ * implementation later.
  */
 struct ifqueue {
+	struct mtx	ifq_mtx;
 	struct mbufq	ifq_mbq;
-	struct mtx	ifq_mtx;
+	/*
+	 * The ifqueue is a very simple and apparently not scalable
+	 * multiple producer / single consumer queue.  Multiple threads
+	 * put packets on the queue, and one of threads would dequeue
+	 * and process them.  Its burst is limited to queue length, to
+	 * avoid starvation.
+	 */
+	struct thread	*ifq_owner;
+	int		ifq_burst;
 };
 
 static struct ifqueue *
@@ -3554,6 +3563,8 @@ if_snd_alloc(int maxlen)
 	ifq = malloc(sizeof(struct ifqueue), M_IFNET, M_WAITOK);
 	mbufq_init(&ifq->ifq_mbq, maxlen);
 	mtx_init(&ifq->ifq_mtx, "ifqueue", NULL, MTX_DEF | MTX_NEW);
+	ifq->ifq_owner = NULL;
+	ifq->ifq_burst = 0;
 
 	return (ifq);
 }
@@ -3595,11 +3606,21 @@ if_snd_enqueue(struct ifnet *ifp, struct mbuf *m)
 
 	mtx_lock(&ifq->ifq_mtx);
 	error = mbufq_enqueue(&ifq->ifq_mbq, m);
-	mtx_unlock(&ifq->ifq_mtx);
 	if (error) {
+		mtx_unlock(&ifq->ifq_mtx);
 		m_freem(m);
 		if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
+		return (error);
 	}
+	if (ifq->ifq_burst <= 0) {
+		/* Take ownership of the queue. */
+		KASSERT(ifq->ifq_owner != curthread, ("%s: %p is owner",
+		    __func__, curthread));
+		ifq->ifq_owner = curthread;
+		ifq->ifq_burst = ifq->ifq_mbq.mq_maxlen;
+	} else
+		error = EBUSY;
+	mtx_unlock(&ifq->ifq_mtx);
 	return (error);
 }
 
@@ -3610,22 +3631,58 @@ if_snd_dequeue(if_t ifp)
 	struct mbuf *m;
 
 	mtx_lock(&ifq->ifq_mtx);
+	if (ifq->ifq_owner != curthread) {
+		if (ifq->ifq_owner == 0) {
+			ifq->ifq_owner = curthread;
+			ifq->ifq_burst = ifq->ifq_mbq.mq_maxlen;
+		} else {
+			/* Other thread preempted us. */
+			mtx_unlock(&ifq->ifq_mtx);
+			return (NULL);
+		}
+	}
 	m = mbufq_dequeue(&ifq->ifq_mbq);
+	if (m == NULL) {
+		ifq->ifq_owner = NULL;
+		ifq->ifq_burst = 0;
+	} else
+		ifq->ifq_burst--;
 	mtx_unlock(&ifq->ifq_mtx);
 	return (m);
 }
 
 void
-if_snd_prepend(if_t ifp, struct mbuf *m)
+if_snd_unown(if_t ifp)
 {
 	struct ifqueue *ifq = ifp->if_snd;
 
 	mtx_lock(&ifq->ifq_mtx);
-	mbufq_prepend(&ifq->ifq_mbq, m);
+	if (ifq->ifq_owner == curthread) {
+		ifq->ifq_owner = NULL;
+		ifq->ifq_burst = 0;
+	}
 	mtx_unlock(&ifq->ifq_mtx);
 }
 
 /*
+ * Put back dequeued mbuf (which can be NULL), and unown the queue.
+ */
+void
+if_snd_putback(if_t ifp, struct mbuf *m)
+{
+	struct ifqueue *ifq = ifp->if_snd;
+
+	mtx_lock(&ifq->ifq_mtx);
+	if (m != NULL)
+		mbufq_prepend(&ifq->ifq_mbq, m);
+	if (ifq->ifq_owner == curthread) {
+		ifq->ifq_owner = NULL;
+		ifq->ifq_burst = 0;
+	}
+	mtx_unlock(&ifq->ifq_mtx);
+}
+
+/*
  * Implementation of if ops, that can be called from drivers.
  */
 void
Index: sys/net/if.h
===================================================================
--- sys/net/if.h	(revision 279669)
+++ sys/net/if.h	(working copy)
@@ -740,7 +740,8 @@ void	if_foreach_maddr(if_t, ifmaddr_cb_t, void *);
 int	if_snd_len(if_t);
 int	if_snd_enqueue(if_t, struct mbuf *);
 struct mbuf * if_snd_dequeue(if_t);
-void	if_snd_prepend(if_t, struct mbuf *);
+void	if_snd_unown(if_t);
+void	if_snd_putback(if_t, struct mbuf *);
 
 /*
  * Type-enforcing inliners over if_getsoftc().
Index: sys/dev/msk/if_msk.c
===================================================================
--- sys/dev/msk/if_msk.c	(revision 279669)
+++ sys/dev/msk/if_msk.c	(working copy)
@@ -2890,12 +2890,14 @@ msk_transmit(if_t ifp, struct mbuf *m)
 	struct msk_if_softc *sc_if;
 	int error;
 
-	if ((error = if_snd_enqueue(ifp, m)) != 0)
+	error = if_snd_enqueue(ifp, m);
+	if (error == EBUSY)
+		return (0);
+	else if (error)
 		return (error);
 
 	sc_if = if_getsoftc(ifp, IF_DRIVER_SOFTC);
-	if (MSK_IF_TRYLOCK(sc_if) == 0)
-		return (0);
+	MSK_IF_LOCK(sc_if);
 	error = msk_start(sc_if);
 	MSK_IF_UNLOCK(sc_if);
 	return (error);
@@ -2909,17 +2911,22 @@ msk_start(struct msk_if_softc *sc_if)
 
 	MSK_IF_LOCK_ASSERT(sc_if);
 
-	if ((sc_if->msk_flags & MSK_FLAG_LINK) == 0)
+	if ((sc_if->msk_flags & MSK_FLAG_LINK) == 0) {
+		if_snd_unown(sc_if->msk_ifp);
 		return (ENETDOWN);
+	}
 
 	error = enq = 0;
-	while (sc_if->msk_cdata.msk_tx_cnt <
-	    (MSK_TX_RING_CNT - MSK_RESERVED_TX_DESC_CNT) &&
-	    (m = if_snd_dequeue(sc_if->msk_ifp)) != NULL) {
+	for (;;) {
+		if (sc_if->msk_cdata.msk_tx_cnt >=
+		    MSK_TX_RING_CNT - MSK_RESERVED_TX_DESC_CNT) {
+			if_snd_unown(sc_if->msk_ifp);
+			break;
+		}
+		if ((m = if_snd_dequeue(sc_if->msk_ifp)) == NULL)
+			break;
 		if ((error = msk_encap(sc_if, &m)) != 0) {
-			if (m == NULL)
-				break;
-			if_snd_prepend(sc_if->msk_ifp, m);
+			if_snd_putback(sc_if->msk_ifp, m);
 			break;
 		}
 		enq++;
