Module Name:    src
Committed By:   knakahara
Date:           Thu May 19 08:20:06 UTC 2016

Modified Files:
        src/sys/dev/pci: if_wm.c

Log Message:
initial TX multiqueue support


To generate a diff of this commit:
cvs rdiff -u -r1.402 -r1.403 src/sys/dev/pci/if_wm.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/pci/if_wm.c
diff -u src/sys/dev/pci/if_wm.c:1.402 src/sys/dev/pci/if_wm.c:1.403
--- src/sys/dev/pci/if_wm.c:1.402	Wed May 18 08:59:56 2016
+++ src/sys/dev/pci/if_wm.c	Thu May 19 08:20:06 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wm.c,v 1.402 2016/05/18 08:59:56 knakahara Exp $	*/
+/*	$NetBSD: if_wm.c,v 1.403 2016/05/19 08:20:06 knakahara Exp $	*/
 
 /*
  * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -83,7 +83,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.402 2016/05/18 08:59:56 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.403 2016/05/19 08:20:06 knakahara Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -103,6 +103,8 @@ __KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.
 #include <sys/queue.h>
 #include <sys/syslog.h>
 #include <sys/interrupt.h>
+#include <sys/cpu.h>
+#include <sys/pcq.h>
 
 #include <sys/rndsource.h>
 
@@ -194,6 +196,8 @@ int	wm_debug = WM_DEBUG_TX | WM_DEBUG_RX
 
 #define	WM_MAXTXDMA		 (2 * round_page(IP_MAXPACKET)) /* for TSO */
 
+#define	WM_TXINTERQSIZE		256
+
 /*
  * Receive descriptor list size.  We have one Rx buffer for normal
  * sized packets.  Jumbo packets consume 5 Rx buffers for a full-sized
@@ -288,6 +292,12 @@ struct wm_txqueue {
 	int txq_fifo_stall;		/* Tx FIFO is stalled */
 
 	/*
+	 * When ncpu > number of Tx queues, a Tx queue is shared by multiple
+	 * CPUs. This queue intermediate them without block.
+	 */
+	pcq_t *txq_interq;
+
+	/*
 	 * NEWQUEUE devices must use not ifp->if_flags but txq->txq_flags
 	 * to manage Tx H/W queue's busy flag.
 	 */
@@ -459,6 +469,7 @@ struct wm_softc {
 };
 
 #define WM_TX_LOCK(_txq)	if ((_txq)->txq_lock) mutex_enter((_txq)->txq_lock)
+#define WM_TX_TRYLOCK(_txq)	((_txq)->txq_lock == NULL || mutex_tryenter((_txq)->txq_lock))
 #define WM_TX_UNLOCK(_txq)	if ((_txq)->txq_lock) mutex_exit((_txq)->txq_lock)
 #define WM_TX_LOCKED(_txq)	(!(_txq)->txq_lock || mutex_owned((_txq)->txq_lock))
 #define WM_RX_LOCK(_rxq)	if ((_rxq)->rxq_lock) mutex_enter((_rxq)->rxq_lock)
@@ -559,6 +570,7 @@ static int	wm_detach(device_t, int);
 static bool	wm_suspend(device_t, const pmf_qual_t *);
 static bool	wm_resume(device_t, const pmf_qual_t *);
 static void	wm_watchdog(struct ifnet *);
+static void	wm_watchdog_txq(struct ifnet *, struct wm_txqueue *);
 static void	wm_tick(void *);
 static int	wm_ifflags_cb(struct ethercom *);
 static int	wm_ioctl(struct ifnet *, u_long, void *);
@@ -615,12 +627,16 @@ static int	wm_tx_offload(struct wm_softc
     uint32_t *, uint8_t *);
 static void	wm_start(struct ifnet *);
 static void	wm_start_locked(struct ifnet *);
-static int	wm_nq_tx_offload(struct wm_softc *, struct wm_txsoft *,
-    uint32_t *, uint32_t *, bool *);
+static int	wm_nq_tx_offload(struct wm_softc *, struct wm_txqueue *,
+    struct wm_txsoft *, uint32_t *, uint32_t *, bool *);
 static void	wm_nq_start(struct ifnet *);
 static void	wm_nq_start_locked(struct ifnet *);
+static int	wm_nq_transmit(struct ifnet *, struct mbuf *);
+static inline int	wm_nq_select_txqueue(struct ifnet *, struct mbuf *);
+static void	wm_nq_transmit_locked(struct ifnet *, struct wm_txqueue *);
+static void	wm_nq_send_common_locked(struct ifnet *, struct wm_txqueue *, bool);
 /* Interrupt */
-static int	wm_txeof(struct wm_softc *);
+static int	wm_txeof(struct wm_softc *, struct wm_txqueue *);
 static void	wm_rxeof(struct wm_rxqueue *);
 static void	wm_linkintr_gmii(struct wm_softc *, uint32_t);
 static void	wm_linkintr_tbi(struct wm_softc *, uint32_t);
@@ -2394,9 +2410,11 @@ alloc_retry:
 	ifp->if_softc = sc;
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = wm_ioctl;
-	if ((sc->sc_flags & WM_F_NEWQUEUE) != 0)
+	if ((sc->sc_flags & WM_F_NEWQUEUE) != 0) {
 		ifp->if_start = wm_nq_start;
-	else
+		if (sc->sc_ntxqueues > 1)
+			ifp->if_transmit = wm_nq_transmit;
+	} else
 		ifp->if_start = wm_start;
 	ifp->if_watchdog = wm_watchdog;
 	ifp->if_init = wm_init;
@@ -2686,15 +2704,37 @@ wm_resume(device_t self, const pmf_qual_
 static void
 wm_watchdog(struct ifnet *ifp)
 {
+	int qid;
+	struct wm_softc *sc = ifp->if_softc;
+
+	for (qid = 0; qid < sc->sc_ntxqueues; qid++) {
+		struct wm_txqueue *txq = &sc->sc_txq[qid];
+
+		wm_watchdog_txq(ifp, txq);
+	}
+
+	/* Reset the interface. */
+	(void) wm_init(ifp);
+
+	/*
+	 * There are still some upper layer processing which call
+	 * ifp->if_start(). e.g. ALTQ
+	 */
+	/* Try to get more packets going. */
+	ifp->if_start(ifp);
+}
+
+static void
+wm_watchdog_txq(struct ifnet *ifp, struct wm_txqueue *txq)
+{
 	struct wm_softc *sc = ifp->if_softc;
-	struct wm_txqueue *txq = &sc->sc_txq[0];
 
 	/*
 	 * Since we're using delayed interrupts, sweep up
 	 * before we report an error.
 	 */
 	WM_TX_LOCK(txq);
-	wm_txeof(sc);
+	wm_txeof(sc, txq);
 	WM_TX_UNLOCK(txq);
 
 	if (txq->txq_free != WM_NTXDESC(txq)) {
@@ -2725,12 +2765,7 @@ wm_watchdog(struct ifnet *ifp)
 			}
 		}
 #endif
-		/* Reset the interface. */
-		(void) wm_init(ifp);
 	}
-
-	/* Try to get more packets going. */
-	ifp->if_start(ifp);
 }
 
 /*
@@ -4294,9 +4329,6 @@ wm_adjust_qnum(struct wm_softc *sc, int 
 		sc->sc_ntxqueues = ncpu;
 	if (ncpu < sc->sc_nrxqueues)
 		sc->sc_nrxqueues = ncpu;
-
-	/* XXX Currently, this driver supports RX multiqueue only. */
-	sc->sc_ntxqueues = 1;
 }
 
 /*
@@ -5623,6 +5655,13 @@ wm_alloc_txrx_queues(struct wm_softc *sc
 			wm_free_tx_descs(sc, txq);
 			break;
 		}
+		txq->txq_interq = pcq_create(WM_TXINTERQSIZE, KM_SLEEP);
+		if (txq->txq_interq == NULL) {
+			wm_free_tx_descs(sc, txq);
+			wm_free_tx_buffer(sc, txq);
+			error = ENOMEM;
+			break;
+		}
 		tx_done++;
 	}
 	if (error)
@@ -5679,6 +5718,7 @@ wm_alloc_txrx_queues(struct wm_softc *sc
  fail_1:
 	for (i = 0; i < tx_done; i++) {
 		struct wm_txqueue *txq = &sc->sc_txq[i];
+		pcq_destroy(txq->txq_interq);
 		wm_free_tx_buffer(sc, txq);
 		wm_free_tx_descs(sc, txq);
 		if (txq->txq_lock)
@@ -6201,7 +6241,7 @@ wm_start_locked(struct ifnet *ifp)
 
 		/* Get a work queue entry. */
 		if (txq->txq_sfree < WM_TXQUEUE_GC(txq)) {
-			wm_txeof(sc);
+			wm_txeof(sc, txq);
 			if (txq->txq_sfree == 0) {
 				DPRINTF(WM_DEBUG_TX,
 				    ("%s: TX: no free job descriptors\n",
@@ -6477,10 +6517,9 @@ wm_start_locked(struct ifnet *ifp)
  *	specified packet, for NEWQUEUE devices
  */
 static int
-wm_nq_tx_offload(struct wm_softc *sc, struct wm_txsoft *txs,
-    uint32_t *cmdlenp, uint32_t *fieldsp, bool *do_csum)
+wm_nq_tx_offload(struct wm_softc *sc, struct wm_txqueue *txq,
+    struct wm_txsoft *txs, uint32_t *cmdlenp, uint32_t *fieldsp, bool *do_csum)
 {
-	struct wm_txqueue *txq = &sc->sc_txq[0];
 	struct mbuf *m0 = txs->txs_mbuf;
 	struct m_tag *mtag;
 	uint32_t vl_len, mssidx, cmdc;
@@ -6691,6 +6730,67 @@ wm_nq_start_locked(struct ifnet *ifp)
 {
 	struct wm_softc *sc = ifp->if_softc;
 	struct wm_txqueue *txq = &sc->sc_txq[0];
+
+	wm_nq_send_common_locked(ifp, txq, false);
+}
+
+static inline int
+wm_nq_select_txqueue(struct ifnet *ifp, struct mbuf *m)
+{
+	struct wm_softc *sc = ifp->if_softc;
+	u_int cpuid = cpu_index(curcpu());
+
+	/*
+	 * Currently, simple distribute strategy.
+	 * TODO:
+	 * destribute by flowid(RSS has value).
+	 */
+
+	return cpuid % sc->sc_ntxqueues;
+}
+
+static int
+wm_nq_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+	int qid;
+	struct wm_softc *sc = ifp->if_softc;
+	struct wm_txqueue *txq;
+
+	qid = wm_nq_select_txqueue(ifp, m);
+	txq = &sc->sc_txq[qid];
+
+	if (__predict_false(!pcq_put(txq->txq_interq, m))) {
+		m_freem(m);
+		WM_EVCNT_INCR(&sc->sc_ev_txdrop);
+		return ENOBUFS;
+	}
+
+	if (WM_TX_TRYLOCK(txq)) {
+		/* XXXX should be per TX queue */
+		ifp->if_obytes += m->m_pkthdr.len;
+		if (m->m_flags & M_MCAST)
+			ifp->if_omcasts++;
+
+		if (!sc->sc_stopping)
+			wm_nq_transmit_locked(ifp, txq);
+		WM_TX_UNLOCK(txq);
+	}
+
+	return 0;
+}
+
+static void
+wm_nq_transmit_locked(struct ifnet *ifp, struct wm_txqueue *txq)
+{
+
+	wm_nq_send_common_locked(ifp, txq, true);
+}
+
+static void
+wm_nq_send_common_locked(struct ifnet *ifp, struct wm_txqueue *txq,
+    bool is_transmit)
+{
+	struct wm_softc *sc = ifp->if_softc;
 	struct mbuf *m0;
 	struct m_tag *mtag;
 	struct wm_txsoft *txs;
@@ -6717,7 +6817,7 @@ wm_nq_start_locked(struct ifnet *ifp)
 
 		/* Get a work queue entry. */
 		if (txq->txq_sfree < WM_TXQUEUE_GC(txq)) {
-			wm_txeof(sc);
+			wm_txeof(sc, txq);
 			if (txq->txq_sfree == 0) {
 				DPRINTF(WM_DEBUG_TX,
 				    ("%s: TX: no free job descriptors\n",
@@ -6728,7 +6828,10 @@ wm_nq_start_locked(struct ifnet *ifp)
 		}
 
 		/* Grab a packet off the queue. */
-		IFQ_DEQUEUE(&ifp->if_snd, m0);
+		if (is_transmit)
+			m0 = pcq_get(txq->txq_interq);
+		else
+			IFQ_DEQUEUE(&ifp->if_snd, m0);
 		if (m0 == NULL)
 			break;
 
@@ -6820,7 +6923,7 @@ wm_nq_start_locked(struct ifnet *ifp)
 		    (M_CSUM_TSOv4 | M_CSUM_TSOv6 |
 			M_CSUM_IPv4 | M_CSUM_TCPv4 | M_CSUM_UDPv4 |
 			M_CSUM_TCPv6 | M_CSUM_UDPv6)) {
-			if (wm_nq_tx_offload(sc, txs, &cmdlen, &fields,
+			if (wm_nq_tx_offload(sc, txq, txs, &cmdlen, &fields,
 			    &do_csum) != 0) {
 				/* Error message already displayed. */
 				bus_dmamap_unload(sc->sc_dmat, dmamap);
@@ -6970,9 +7073,8 @@ wm_nq_start_locked(struct ifnet *ifp)
  *	Helper; handle transmit interrupts.
  */
 static int
-wm_txeof(struct wm_softc *sc)
+wm_txeof(struct wm_softc *sc, struct wm_txqueue *txq)
 {
-	struct wm_txqueue *txq = &sc->sc_txq[0];
 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
 	struct wm_txsoft *txs;
 	bool processed = false;
@@ -7564,7 +7666,7 @@ wm_intr_legacy(void *arg)
 			WM_EVCNT_INCR(&sc->sc_ev_txdw);
 		}
 #endif
-		wm_txeof(sc);
+		wm_txeof(sc, txq);
 
 		WM_TX_UNLOCK(txq);
 		WM_CORE_LOCK(sc);
@@ -7622,7 +7724,7 @@ wm_txintr_msix(void *arg)
 		goto out;
 
 	WM_EVCNT_INCR(&sc->sc_ev_txdw);
-	wm_txeof(sc);
+	wm_txeof(sc, txq);
 
 out:
 	WM_TX_UNLOCK(txq);
@@ -7634,9 +7736,19 @@ out:
 	else
 		CSR_WRITE(sc, WMREG_EIMS, 1 << txq->txq_intr_idx);
 
-	if (!IFQ_IS_EMPTY(&ifp->if_snd)) {
-		/* Try to get more packets going. */
-		ifp->if_start(ifp);
+	/* Try to get more packets going. */
+	if (pcq_peek(txq->txq_interq) != NULL) {
+		WM_TX_LOCK(txq);
+		wm_nq_transmit_locked(ifp, txq);
+		WM_TX_UNLOCK(txq);
+	}
+	/*
+	 * There are still some upper layer processing which call
+	 * ifp->if_start(). e.g. ALTQ
+	 */
+	if (txq->txq_id == 0) {
+		if (!IFQ_IS_EMPTY(&ifp->if_snd))
+			ifp->if_start(ifp);
 	}
 
 	return 1;

Reply via email to