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;