Module Name:    src
Committed By:   martin
Date:           Mon Feb  5 15:07:30 UTC 2018

Modified Files:
        src/share/man/man4 [netbsd-8]: wm.4
        src/sys/dev/pci [netbsd-8]: if_wm.c

Log Message:
Pull up following revision(s) (requested by knakahara in ticket #529):
        sys/dev/pci/if_wm.c: revision 1.560
        sys/dev/pci/if_wm.c: revision 1.561
        sys/dev/pci/if_wm.c: revision 1.562
        share/man/man4/wm.4: revision 1.37
        share/man/man4/wm.4: revision 1.38
        sys/dev/pci/if_wm.c: revision 1.551
        sys/dev/pci/if_wm.c: revision 1.553
        sys/dev/pci/if_wm.c: revision 1.554
        sys/dev/pci/if_wm.c: revision 1.555
        sys/dev/pci/if_wm.c: revision 1.556
        sys/dev/pci/if_wm.c: revision 1.557
        sys/dev/pci/if_wm.c: revision 1.558
        sys/dev/pci/if_wm.c: revision 1.559
PR/52885 - Shinichi Doyashiki -- typo in comment
Fix legacy Tx descriptors printing when WM_DEBUG is enabled.
improve comments
Fix wm_watchdog_txq() lock region.
Not only wm_txeof() but also wm_watchdog_txq() itself requires txq_lock
as it reads Tx descriptor management variables such as "txq_free".
There is almost no influence on performance.
Fix duplicated "rxintr" evcnt counting. Pointed out by ozaki-r@n.o, thanks.
wm_txeof() can limit the loop count the same as wm_rxeof() now.
add WM_TX_PROCESS_LIMIT_DEFAULT and WM_TX_INTR_PROCESS_LIMIT_DEFAULT man.
More markup.
CID-1427779: Fix uninitialized variables
Fix 82574 MSI-X mode cannot receive packets after 82574 receives high rate 
traffic.
In short, 82574 MSI-X mode does not cause RXQ MSI-X vector when 82574's
phys FIFO overflows. I don't know why but 82574 causes not RXQ MSI-X vector
but OTHER MSI-X vector at the situation.
see:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v4.15-rc9&id=4aea7a5c5e940c1723add439f4088844cd26196d
advised by msaitoh@n.o, thanks.
Fix if_wm.c:r1.557 merge miss, sorry.
Fix unmatched return type. The return value of wm_txeof() is not useded yet.
Make wm(4) watchdog MP-safe. There is almost no influence on performance.
wm(4) does not use ifp->if_watchdog now, that is, it does not touch
ifp->if_timer.
It also uses own callout(wm_tick) as watchdog now. The watchdog uses
per-queue counter to check timeout. So, global lock is not required.


To generate a diff of this commit:
cvs rdiff -u -r1.36 -r1.36.4.1 src/share/man/man4/wm.4
cvs rdiff -u -r1.508.4.12 -r1.508.4.13 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/share/man/man4/wm.4
diff -u src/share/man/man4/wm.4:1.36 src/share/man/man4/wm.4:1.36.4.1
--- src/share/man/man4/wm.4:1.36	Thu Apr 13 10:37:36 2017
+++ src/share/man/man4/wm.4	Mon Feb  5 15:07:30 2018
@@ -1,4 +1,4 @@
-.\"	$NetBSD: wm.4,v 1.36 2017/04/13 10:37:36 knakahara Exp $
+.\"	$NetBSD: wm.4,v 1.36.4.1 2018/02/05 15:07:30 martin Exp $
 .\"
 .\" Copyright 2002, 2003 Wasabi Systems, Inc.
 .\" All rights reserved.
@@ -33,7 +33,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd March 22, 2017
+.Dd January 18, 2018
 .Dt WM 4
 .Os
 .Sh NAME
@@ -182,6 +182,9 @@ The value range is from zero to
 The default value is 100.
 When you increase this value, both the receive latency and
 the receive throughput will increase.
+.It Dv WM_TX_PROCESS_LIMIT_DEFAULT
+Transmit side of
+.Dv WM_RX_PROCESS_LIMIT_DEFAULT .
 .It Dv WM_RX_INTR_PROCESS_LIMIT_DEFAULT
 The maximum number of received packets processed in each
 hardware interrupt context.
@@ -191,6 +194,9 @@ The value range is from zero to
 The default value is 0.
 When you increase this value, both the receive latency and
 the receive throughput will decrease.
+.It Dv WM_TX_INTR_PROCESS_LIMIT_DEFAULT
+Transmit side of
+.Dv WM_RX_INTR_PROCESS_LIMIT_DEFAULT .
 .It Dv WM_EVENT_COUNTERS
 Enable many event counters such as each Tx drop counter and Rx interrupt
 counter.

Index: src/sys/dev/pci/if_wm.c
diff -u src/sys/dev/pci/if_wm.c:1.508.4.12 src/sys/dev/pci/if_wm.c:1.508.4.13
--- src/sys/dev/pci/if_wm.c:1.508.4.12	Sat Jan 13 21:42:45 2018
+++ src/sys/dev/pci/if_wm.c	Mon Feb  5 15:07:30 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wm.c,v 1.508.4.12 2018/01/13 21:42:45 snj Exp $	*/
+/*	$NetBSD: if_wm.c,v 1.508.4.13 2018/02/05 15:07:30 martin 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.508.4.12 2018/01/13 21:42:45 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.508.4.13 2018/02/05 15:07:30 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -183,6 +183,11 @@ int	wm_debug = WM_DEBUG_TX | WM_DEBUG_RX
 int wm_disable_msi = WM_DISABLE_MSI;
 int wm_disable_msix = WM_DISABLE_MSIX;
 
+#ifndef WM_WATCHDOG_TIMEOUT
+#define WM_WATCHDOG_TIMEOUT 5
+#endif
+static int wm_watchdog_timeout = WM_WATCHDOG_TIMEOUT;
+
 /*
  * Transmit descriptor list size.  Due to errata, we can only have
  * 256 hardware descriptors in the ring on < 82544, but we use 4096
@@ -213,6 +218,13 @@ int wm_disable_msix = WM_DISABLE_MSIX;
 
 #define	WM_TXINTERQSIZE		256
 
+#ifndef WM_TX_PROCESS_LIMIT_DEFAULT
+#define	WM_TX_PROCESS_LIMIT_DEFAULT		100U
+#endif
+#ifndef WM_TX_INTR_PROCESS_LIMIT_DEFAULT
+#define	WM_TX_INTR_PROCESS_LIMIT_DEFAULT	0U
+#endif
+
 /*
  * Receive descriptor list size.  We have one Rx buffer for normal
  * sized packets.  Jumbo packets consume 5 Rx buffers for a full-sized
@@ -356,6 +368,9 @@ struct wm_txqueue {
 
 	bool txq_stopping;
 
+	bool txq_watchdog;
+	time_t txq_lastsent;
+
 	uint32_t txq_packets;		/* for AIM */
 	uint32_t txq_bytes;		/* for AIM */
 #ifdef WM_EVENT_COUNTERS
@@ -417,6 +432,7 @@ struct wm_rxqueue {
 	uint32_t rxq_bytes;		/* for AIM */
 #ifdef WM_EVENT_COUNTERS
 	WM_Q_EVCNT_DEFINE(rxq, rxintr);		/* Rx interrupts */
+	WM_Q_EVCNT_DEFINE(rxq, rxdefer);	/* Rx deferred processing */
 
 	WM_Q_EVCNT_DEFINE(rxq, rxipsum);	/* IP checksums checked in-bound */
 	WM_Q_EVCNT_DEFINE(rxq, rxtusum);	/* TCP/UDP cksums checked in-bound */
@@ -518,6 +534,8 @@ struct wm_softc {
 
 	int sc_nqueues;
 	struct wm_queue *sc_queue;
+	u_int sc_tx_process_limit;	/* Tx processing repeat limit in softint */
+	u_int sc_tx_intr_process_limit;	/* Tx processing repeat limit in H/W intr */
 	u_int sc_rx_process_limit;	/* Rx processing repeat limit in softint */
 	u_int sc_rx_intr_process_limit;	/* Rx processing repeat limit in H/W intr */
 
@@ -670,7 +688,8 @@ 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_watchdog_txq(struct ifnet *, struct wm_txqueue *, uint16_t *);
+static void	wm_watchdog_txq_locked(struct ifnet *, struct wm_txqueue *, uint16_t *);
 static void	wm_tick(void *);
 static int	wm_ifflags_cb(struct ethercom *);
 static int	wm_ioctl(struct ifnet *, u_long, void *);
@@ -756,7 +775,7 @@ static void	wm_nq_send_common_locked(str
 static void	wm_deferred_start_locked(struct wm_txqueue *);
 static void	wm_handle_queue(void *);
 /* Interrupt */
-static int	wm_txeof(struct wm_softc *, struct wm_txqueue *);
+static int	wm_txeof(struct wm_txqueue *, u_int);
 static void	wm_rxeof(struct wm_rxqueue *, u_int);
 static void	wm_linkintr_gmii(struct wm_softc *, uint32_t);
 static void	wm_linkintr_tbi(struct wm_softc *, uint32_t);
@@ -2672,7 +2691,7 @@ alloc_retry:
 		if (wm_is_using_multiqueue(sc))
 			ifp->if_transmit = wm_transmit;
 	}
-	ifp->if_watchdog = wm_watchdog;
+	/* wm(4) doest not use ifp->if_watchdog, use wm_tick as watchdog. */
 	ifp->if_init = wm_init;
 	ifp->if_stop = wm_stop;
 	IFQ_SET_MAXLEN(&ifp->if_snd, max(WM_IFQUEUELEN, IFQ_MAXLEN));
@@ -2762,6 +2781,8 @@ alloc_retry:
 		ifp->if_capabilities |= IFCAP_TSOv6;
 	}
 
+	sc->sc_tx_process_limit = WM_TX_PROCESS_LIMIT_DEFAULT;
+	sc->sc_tx_intr_process_limit = WM_TX_INTR_PROCESS_LIMIT_DEFAULT;
 	sc->sc_rx_process_limit = WM_RX_PROCESS_LIMIT_DEFAULT;
 	sc->sc_rx_intr_process_limit = WM_RX_INTR_PROCESS_LIMIT_DEFAULT;
 
@@ -2932,36 +2953,57 @@ wm_watchdog(struct ifnet *ifp)
 {
 	int qid;
 	struct wm_softc *sc = ifp->if_softc;
+	uint16_t hang_queue = 0; /* Max queue number of wm(4) is 82576's 16. */
 
 	for (qid = 0; qid < sc->sc_nqueues; qid++) {
 		struct wm_txqueue *txq = &sc->sc_queue[qid].wmq_txq;
 
-		wm_watchdog_txq(ifp, txq);
+		wm_watchdog_txq(ifp, txq, &hang_queue);
 	}
 
-	/* Reset the interface. */
-	(void) wm_init(ifp);
-
 	/*
-	 * There are still some upper layer processing which call
-	 * ifp->if_start(). e.g. ALTQ or one CPU system
+	 * IF any of queues hanged up, reset the interface.
 	 */
-	/* Try to get more packets going. */
-	ifp->if_start(ifp);
+	if (hang_queue != 0) {
+		(void) wm_init(ifp);
+
+		/*
+		 * There are still some upper layer processing which call
+		 * ifp->if_start(). e.g. ALTQ or one CPU system
+		 */
+		/* Try to get more packets going. */
+		ifp->if_start(ifp);
+	}
+}
+
+
+static void
+wm_watchdog_txq(struct ifnet *ifp, struct wm_txqueue *txq, uint16_t *hang)
+{
+
+	mutex_enter(txq->txq_lock);
+	if (txq->txq_watchdog &&
+	    time_uptime - txq->txq_lastsent > wm_watchdog_timeout) {
+		wm_watchdog_txq_locked(ifp, txq, hang);
+	}
+	mutex_exit(txq->txq_lock);
 }
 
 static void
-wm_watchdog_txq(struct ifnet *ifp, struct wm_txqueue *txq)
+wm_watchdog_txq_locked(struct ifnet *ifp, struct wm_txqueue *txq, uint16_t *hang)
 {
 	struct wm_softc *sc = ifp->if_softc;
+	struct wm_queue *wmq = container_of(txq, struct wm_queue, wmq_txq);
+
+	KASSERT(mutex_owned(txq->txq_lock));
 
 	/*
 	 * Since we're using delayed interrupts, sweep up
 	 * before we report an error.
 	 */
-	mutex_enter(txq->txq_lock);
-	wm_txeof(sc, txq);
-	mutex_exit(txq->txq_lock);
+	wm_txeof(txq, UINT_MAX);
+	if (txq->txq_watchdog)
+		*hang |= __BIT(wmq->wmq_id);
 
 	if (txq->txq_free != WM_NTXDESC(txq)) {
 #ifdef WM_DEBUG
@@ -2981,11 +3023,22 @@ wm_watchdog_txq(struct ifnet *ifp, struc
 			i, txs->txs_firstdesc, txs->txs_lastdesc);
 		    for (j = txs->txs_firstdesc; ;
 			j = WM_NEXTTX(txq, j)) {
-			printf("\tdesc %d: 0x%" PRIx64 "\n", j,
-			    txq->txq_nq_descs[j].nqtx_data.nqtxd_addr);
-			printf("\t %#08x%08x\n",
-			    txq->txq_nq_descs[j].nqtx_data.nqtxd_fields,
-			    txq->txq_nq_descs[j].nqtx_data.nqtxd_cmdlen);
+			    if ((sc->sc_flags & WM_F_NEWQUEUE) != 0) {
+				    printf("\tdesc %d: 0x%" PRIx64 "\n", j,
+					txq->txq_nq_descs[j].nqtx_data.nqtxd_addr);
+				    printf("\t %#08x%08x\n",
+					txq->txq_nq_descs[j].nqtx_data.nqtxd_fields,
+					txq->txq_nq_descs[j].nqtx_data.nqtxd_cmdlen);
+			    } else {
+				    printf("\tdesc %d: 0x%" PRIx64 "\n", j,
+					(uint64_t)txq->txq_descs[j].wtx_addr.wa_high << 32 |
+					txq->txq_descs[j].wtx_addr.wa_low);
+				    printf("\t %#04x%02x%02x%08x\n",
+					txq->txq_descs[j].wtx_fields.wtxu_vlan,
+					txq->txq_descs[j].wtx_fields.wtxu_options,
+					txq->txq_descs[j].wtx_fields.wtxu_status,
+					txq->txq_descs[j].wtx_cmdlen);
+			    }
 			if (j == txs->txs_lastdesc)
 				break;
 			}
@@ -3011,8 +3064,13 @@ wm_tick(void *arg)
 
 	WM_CORE_LOCK(sc);
 
-	if (sc->sc_core_stopping)
-		goto out;
+	if (sc->sc_core_stopping) {
+		WM_CORE_UNLOCK(sc);
+#ifndef WM_MPSAFE
+		splx(s);
+#endif
+		return;
+	}
 
 	if (sc->sc_type >= WM_T_82542_2_1) {
 		WM_EVCNT_ADD(&sc->sc_ev_rx_xon, CSR_READ(sc, WMREG_XONRXC));
@@ -3050,12 +3108,11 @@ wm_tick(void *arg)
 	else
 		wm_tbi_tick(sc);
 
-	callout_reset(&sc->sc_tick_ch, hz, wm_tick, sc);
-out:
 	WM_CORE_UNLOCK(sc);
-#ifndef WM_MPSAFE
-	splx(s);
-#endif
+
+	wm_watchdog(ifp);
+
+	callout_reset(&sc->sc_tick_ch, hz, wm_tick, sc);
 }
 
 static int
@@ -4199,6 +4256,10 @@ wm_reset_phy(struct wm_softc *sc)
 	wm_phy_post_reset(sc);
 }
 
+/*
+ * Only used by WM_T_PCH_SPT which does not use multiqueue,
+ * so it is enough to check sc->sc_queue[0] only.
+ */
 static void
 wm_flush_desc_rings(struct wm_softc *sc)
 {
@@ -5939,6 +6000,7 @@ wm_stop_locked(struct ifnet *ifp, int di
 		struct wm_queue *wmq = &sc->sc_queue[qidx];
 		struct wm_txqueue *txq = &wmq->wmq_txq;
 		mutex_enter(txq->txq_lock);
+		txq->txq_watchdog = false; /* ensure watchdog disabled */
 		for (i = 0; i < WM_TXQUEUELEN(txq); i++) {
 			txs = &txq->txq_soft[i];
 			if (txs->txs_mbuf != NULL) {
@@ -5952,7 +6014,6 @@ wm_stop_locked(struct ifnet *ifp, int di
 
 	/* Mark the interface as down and cancel the watchdog timer. */
 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
-	ifp->if_timer = 0;
 
 	if (disable) {
 		for (i = 0; i < sc->sc_nqueues; i++) {
@@ -6431,6 +6492,7 @@ wm_alloc_txrx_queues(struct wm_softc *sc
 		xname = device_xname(sc->sc_dev);
 
 		WM_Q_INTR_EVCNT_ATTACH(rxq, rxintr, rxq, i, xname);
+		WM_Q_INTR_EVCNT_ATTACH(rxq, rxdefer, rxq, i, xname);
 
 		WM_Q_INTR_EVCNT_ATTACH(rxq, rxipsum, rxq, i, xname);
 		WM_Q_INTR_EVCNT_ATTACH(rxq, rxtusum, rxq, i, xname);
@@ -6481,6 +6543,7 @@ wm_free_txrx_queues(struct wm_softc *sc)
 
 #ifdef WM_EVENT_COUNTERS
 		WM_Q_EVCNT_DETACH(rxq, rxintr, rxq, i);
+		WM_Q_EVCNT_DETACH(rxq, rxdefer, rxq, i);
 		WM_Q_EVCNT_DETACH(rxq, rxipsum, rxq, i);
 		WM_Q_EVCNT_DETACH(rxq, rxtusum, rxq, i);
 #endif /* WM_EVENT_COUNTERS */
@@ -6625,6 +6688,8 @@ wm_init_tx_queue(struct wm_softc *sc, st
 	wm_init_tx_descs(sc, txq);
 	wm_init_tx_regs(sc, wmq, txq);
 	wm_init_tx_buffer(sc, txq);
+
+	txq->txq_watchdog = false;
 }
 
 static void
@@ -7112,7 +7177,7 @@ wm_send_common_locked(struct ifnet *ifp,
 
 		/* Get a work queue entry. */
 		if (txq->txq_sfree < WM_TXQUEUE_GC(txq)) {
-			wm_txeof(sc, txq);
+			wm_txeof(txq, UINT_MAX);
 			if (txq->txq_sfree == 0) {
 				DPRINTF(WM_DEBUG_TX,
 				    ("%s: TX: no free job descriptors\n",
@@ -7388,7 +7453,8 @@ wm_send_common_locked(struct ifnet *ifp,
 
 	if (txq->txq_free != ofree) {
 		/* Set a watchdog timer in case the chip flakes out. */
-		ifp->if_timer = 5;
+		txq->txq_lastsent = time_uptime;
+		txq->txq_watchdog = true;
 	}
 }
 
@@ -7711,7 +7777,7 @@ wm_nq_send_common_locked(struct ifnet *i
 
 		/* Get a work queue entry. */
 		if (txq->txq_sfree < WM_TXQUEUE_GC(txq)) {
-			wm_txeof(sc, txq);
+			wm_txeof(txq, UINT_MAX);
 			if (txq->txq_sfree == 0) {
 				DPRINTF(WM_DEBUG_TX,
 				    ("%s: TX: no free job descriptors\n",
@@ -7876,7 +7942,7 @@ wm_nq_send_common_locked(struct ifnet *i
 		lasttx = nexttx;
 		nexttx = WM_NEXTTX(txq, nexttx);
 		/*
-		 * fill in the next descriptors. legacy or adcanced format
+		 * fill in the next descriptors. legacy or advanced format
 		 * is the same here
 		 */
 		for (seg = 1; seg < dmamap->dm_nsegs;
@@ -7960,7 +8026,8 @@ wm_nq_send_common_locked(struct ifnet *i
 
 	if (sent) {
 		/* Set a watchdog timer in case the chip flakes out. */
-		ifp->if_timer = 5;
+		txq->txq_lastsent = time_uptime;
+		txq->txq_watchdog = true;
 	}
 }
 
@@ -8000,11 +8067,11 @@ wm_deferred_start_locked(struct wm_txque
  *	Helper; handle transmit interrupts.
  */
 static int
-wm_txeof(struct wm_softc *sc, struct wm_txqueue *txq)
+wm_txeof(struct wm_txqueue *txq, u_int limit)
 {
+	struct wm_softc *sc = txq->txq_sc;
 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
 	struct wm_txsoft *txs;
-	bool processed = false;
 	int count = 0;
 	int i;
 	uint8_t status;
@@ -8026,6 +8093,9 @@ wm_txeof(struct wm_softc *sc, struct wm_
 	 */
 	for (i = txq->txq_sdirty; txq->txq_sfree != WM_TXQUEUELEN(txq);
 	     i = WM_NEXTTXS(txq, i), txq->txq_sfree++) {
+		if (limit-- == 0)
+			break;
+
 		txs = &txq->txq_soft[i];
 
 		DPRINTF(WM_DEBUG_TX, ("%s: TX: checking job %d\n",
@@ -8042,7 +8112,6 @@ wm_txeof(struct wm_softc *sc, struct wm_
 			break;
 		}
 
-		processed = true;
 		count++;
 		DPRINTF(WM_DEBUG_TX,
 		    ("%s: TX: job %d done: descs %d..%d\n",
@@ -8097,9 +8166,9 @@ wm_txeof(struct wm_softc *sc, struct wm_
 	 * timer.
 	 */
 	if (txq->txq_sfree == WM_TXQUEUELEN(txq))
-		ifp->if_timer = 0;
+		txq->txq_watchdog = false;
 
-	return processed;
+	return count;
 }
 
 static inline uint32_t
@@ -8862,7 +8931,7 @@ wm_intr_legacy(void *arg)
 			WM_Q_EVCNT_INCR(txq, txdw);
 		}
 #endif
-		wm_txeof(sc, txq);
+		wm_txeof(txq, UINT_MAX);
 
 		mutex_exit(txq->txq_lock);
 		WM_CORE_LOCK(sc);
@@ -8917,8 +8986,14 @@ wm_txrxintr_enable(struct wm_queue *wmq)
 
 	wm_itrs_calculate(sc, wmq);
 
+	/*
+	 * ICR_OTHER which is disabled in wm_linkintr_msix() is enabled here.
+	 * There is no need to care about which of RXQ(0) and RXQ(1) enable
+	 * ICR_OTHER in first, because each RXQ/TXQ interrupt is disabled
+	 * while each wm_handle_queue(wmq) is runnig.
+	 */
 	if (sc->sc_type == WM_T_82574)
-		CSR_WRITE(sc, WMREG_IMS, ICR_TXQ(wmq->wmq_id) | ICR_RXQ(wmq->wmq_id));
+		CSR_WRITE(sc, WMREG_IMS, ICR_TXQ(wmq->wmq_id) | ICR_RXQ(wmq->wmq_id) | ICR_OTHER);
 	else if (sc->sc_type == WM_T_82575)
 		CSR_WRITE(sc, WMREG_EIMS, EITR_TX_QUEUE(wmq->wmq_id) | EITR_RX_QUEUE(wmq->wmq_id));
 	else
@@ -8932,7 +9007,8 @@ wm_txrxintr_msix(void *arg)
 	struct wm_txqueue *txq = &wmq->wmq_txq;
 	struct wm_rxqueue *rxq = &wmq->wmq_rxq;
 	struct wm_softc *sc = txq->txq_sc;
-	u_int limit = sc->sc_rx_intr_process_limit;
+	u_int txlimit = sc->sc_tx_intr_process_limit;
+	u_int rxlimit = sc->sc_rx_intr_process_limit;
 
 	KASSERT(wmq->wmq_intr_idx == wmq->wmq_id);
 
@@ -8949,7 +9025,7 @@ wm_txrxintr_msix(void *arg)
 	}
 
 	WM_Q_EVCNT_INCR(txq, txdw);
-	wm_txeof(sc, txq);
+	wm_txeof(txq, txlimit);
 	/* wm_deferred start() is done in wm_handle_queue(). */
 	mutex_exit(txq->txq_lock);
 
@@ -8963,7 +9039,7 @@ wm_txrxintr_msix(void *arg)
 	}
 
 	WM_Q_EVCNT_INCR(rxq, rxintr);
-	wm_rxeof(rxq, limit);
+	wm_rxeof(rxq, rxlimit);
 	mutex_exit(rxq->rxq_lock);
 
 	wm_itrs_writereg(sc, wmq);
@@ -8980,14 +9056,15 @@ wm_handle_queue(void *arg)
 	struct wm_txqueue *txq = &wmq->wmq_txq;
 	struct wm_rxqueue *rxq = &wmq->wmq_rxq;
 	struct wm_softc *sc = txq->txq_sc;
-	u_int limit = sc->sc_rx_process_limit;
+	u_int txlimit = sc->sc_tx_process_limit;
+	u_int rxlimit = sc->sc_rx_process_limit;
 
 	mutex_enter(txq->txq_lock);
 	if (txq->txq_stopping) {
 		mutex_exit(txq->txq_lock);
 		return;
 	}
-	wm_txeof(sc, txq);
+	wm_txeof(txq, txlimit);
 	wm_deferred_start_locked(txq);
 	mutex_exit(txq->txq_lock);
 
@@ -8996,8 +9073,8 @@ wm_handle_queue(void *arg)
 		mutex_exit(rxq->rxq_lock);
 		return;
 	}
-	WM_Q_EVCNT_INCR(rxq, rxintr);
-	wm_rxeof(rxq, limit);
+	WM_Q_EVCNT_INCR(rxq, rxdefer);
+	wm_rxeof(rxq, rxlimit);
 	mutex_exit(rxq->rxq_lock);
 
 	wm_txrxintr_enable(wmq);
@@ -9013,24 +9090,59 @@ wm_linkintr_msix(void *arg)
 {
 	struct wm_softc *sc = arg;
 	uint32_t reg;
+	bool has_rxo;
 
 	DPRINTF(WM_DEBUG_LINK,
 	    ("%s: LINK: got link intr\n", device_xname(sc->sc_dev)));
 
 	reg = CSR_READ(sc, WMREG_ICR);
 	WM_CORE_LOCK(sc);
-	if ((sc->sc_core_stopping) || ((reg & ICR_LSC) == 0))
+	if (sc->sc_core_stopping)
 		goto out;
 
-	WM_EVCNT_INCR(&sc->sc_ev_linkintr);
-	wm_linkintr(sc, ICR_LSC);
+	if((reg & ICR_LSC) != 0) {
+		WM_EVCNT_INCR(&sc->sc_ev_linkintr);
+		wm_linkintr(sc, ICR_LSC);
+	}
+
+	/*
+	 * XXX 82574 MSI-X mode workaround
+	 *
+	 * 82574 MSI-X mode causes receive overrun(RXO) interrupt as ICR_OTHER
+	 * MSI-X vector, furthermore it does not cause neigher ICR_RXQ(0) nor
+	 * ICR_RXQ(1) vector. So, we generate ICR_RXQ(0) and ICR_RXQ(1)
+	 * interrupts by writing WMREG_ICS to process receive packets.
+	 */
+	if (sc->sc_type == WM_T_82574 && ((reg & ICR_RXO) != 0)) {
+#if defined(WM_DEBUG)
+		log(LOG_WARNING, "%s: Receive overrun\n",
+		    device_xname(sc->sc_dev));
+#endif /* defined(WM_DEBUG) */
+
+		has_rxo = true;
+		/*
+		 * The RXO interrupt is very high rate when receive traffic is
+		 * high rate. We use polling mode for ICR_OTHER like Tx/Rx
+		 * interrupts. ICR_OTHER will be enabled at the end of
+		 * wm_txrxintr_msix() which is kicked by both ICR_RXQ(0) and
+		 * ICR_RXQ(1) interrupts.
+		 */
+		CSR_WRITE(sc, WMREG_IMC, ICR_OTHER);
+
+		CSR_WRITE(sc, WMREG_ICS, ICR_RXQ(0) | ICR_RXQ(1));
+	}
+
+
 
 out:
 	WM_CORE_UNLOCK(sc);
 	
-	if (sc->sc_type == WM_T_82574)
-		CSR_WRITE(sc, WMREG_IMS, ICR_OTHER | ICR_LSC);
-	else if (sc->sc_type == WM_T_82575)
+	if (sc->sc_type == WM_T_82574) {
+		if (!has_rxo)
+			CSR_WRITE(sc, WMREG_IMS, ICR_OTHER | ICR_LSC);
+		else
+			CSR_WRITE(sc, WMREG_IMS, ICR_LSC);
+	} else if (sc->sc_type == WM_T_82575)
 		CSR_WRITE(sc, WMREG_EIMS, EITR_OTHER);
 	else
 		CSR_WRITE(sc, WMREG_EIMS, 1 << sc->sc_link_intr_idx);
@@ -12542,7 +12654,7 @@ wm_nvm_version(struct wm_softc *sc)
 	case WM_T_82575:
 	case WM_T_82576:
 	case WM_T_82580:
-		if ((uid1 & NVM_MAJOR_MASK) != NVM_UID_VALID)
+		if (have_uid && (uid1 & NVM_MAJOR_MASK) != NVM_UID_VALID)
 			check_version = true;
 		break;
 	case WM_T_I211:

Reply via email to