summaryrefslogtreecommitdiffstats
path: root/sys/dev/bce
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2013-06-30 05:12:18 +0000
committeryongari <yongari@FreeBSD.org>2013-06-30 05:12:18 +0000
commitc43c218e2376600315f1fbd5f72f8db54c391e08 (patch)
tree5cb975405a4897759167534809d9ee4047925af7 /sys/dev/bce
parent6b85443c9fe28d9057f94044a2ef66408625279c (diff)
downloadFreeBSD-src-c43c218e2376600315f1fbd5f72f8db54c391e08.zip
FreeBSD-src-c43c218e2376600315f1fbd5f72f8db54c391e08.tar.gz
Fix triggering false watchdog timeout when controller is in PAUSE
state. Previously it used to check if controller has sent a PAUSE frame to the remote peer. Reported by: David Imhoff via Brad Smith <brad@OpenBSD.org> Submitted by: davidch (initial version) Reviewed by: davidch, David Imhoff <dimhoff_devel@xs4all.nl>
Diffstat (limited to 'sys/dev/bce')
-rw-r--r--sys/dev/bce/if_bce.c33
-rw-r--r--sys/dev/bce/if_bcereg.h1
2 files changed, 31 insertions, 3 deletions
diff --git a/sys/dev/bce/if_bce.c b/sys/dev/bce/if_bce.c
index cdf9517..644348c 100644
--- a/sys/dev/bce/if_bce.c
+++ b/sys/dev/bce/if_bce.c
@@ -2077,10 +2077,12 @@ bce_miibus_statchg(device_t dev)
DBPRINT(sc, BCE_INFO_PHY,
"%s(): Enabling RX flow control.\n", __FUNCTION__);
BCE_SETBIT(sc, BCE_EMAC_RX_MODE, BCE_EMAC_RX_MODE_FLOW_EN);
+ sc->bce_flags |= BCE_USING_RX_FLOW_CONTROL;
} else {
DBPRINT(sc, BCE_INFO_PHY,
"%s(): Disabling RX flow control.\n", __FUNCTION__);
BCE_CLRBIT(sc, BCE_EMAC_RX_MODE, BCE_EMAC_RX_MODE_FLOW_EN);
+ sc->bce_flags &= ~BCE_USING_RX_FLOW_CONTROL;
}
if ((IFM_OPTIONS(media_active) & IFM_ETH_TXPAUSE) != 0) {
@@ -7828,18 +7830,42 @@ bce_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
static void
bce_watchdog(struct bce_softc *sc)
{
+ uint32_t status;
+
DBENTER(BCE_EXTREME_SEND);
BCE_LOCK_ASSERT(sc);
+ status = 0;
/* If the watchdog timer hasn't expired then just exit. */
if (sc->watchdog_timer == 0 || --sc->watchdog_timer)
goto bce_watchdog_exit;
+ status = REG_RD(sc, BCE_EMAC_RX_STATUS);
/* If pause frames are active then don't reset the hardware. */
- /* ToDo: Should we reset the timer here? */
- if (REG_RD(sc, BCE_EMAC_TX_STATUS) & BCE_EMAC_TX_STATUS_XOFFED)
- goto bce_watchdog_exit;
+ if ((sc->bce_flags & BCE_USING_RX_FLOW_CONTROL) != 0) {
+ if ((status & BCE_EMAC_RX_STATUS_FFED) != 0) {
+ /*
+ * If link partner has us in XOFF state then wait for
+ * the condition to clear.
+ */
+ sc->watchdog_timer = BCE_TX_TIMEOUT;
+ goto bce_watchdog_exit;
+ } else if ((status & BCE_EMAC_RX_STATUS_FF_RECEIVED) != 0 &&
+ (status & BCE_EMAC_RX_STATUS_N_RECEIVED) != 0) {
+ /*
+ * If we're not currently XOFF'ed but have recently
+ * been XOFF'd/XON'd then assume that's delaying TX
+ * this time around.
+ */
+ sc->watchdog_timer = BCE_TX_TIMEOUT;
+ goto bce_watchdog_exit;
+ }
+ /*
+ * Any other condition is unexpected and the controller
+ * should be reset.
+ */
+ }
BCE_PRINTF("%s(%d): Watchdog timeout occurred, resetting!\n",
__FILE__, __LINE__);
@@ -7863,6 +7889,7 @@ bce_watchdog(struct bce_softc *sc)
sc->bce_ifp->if_oerrors++;
bce_watchdog_exit:
+ REG_WR(sc, BCE_EMAC_RX_STATUS, status);
DBEXIT(BCE_EXTREME_SEND);
}
diff --git a/sys/dev/bce/if_bcereg.h b/sys/dev/bce/if_bcereg.h
index 7138f3c..dab19c4 100644
--- a/sys/dev/bce/if_bcereg.h
+++ b/sys/dev/bce/if_bcereg.h
@@ -6465,6 +6465,7 @@ struct bce_softc
#define BCE_USING_MSIX_FLAG 0x00000100
#define BCE_PCIE_FLAG 0x00000200
#define BCE_USING_TX_FLOW_CONTROL 0x00000400
+#define BCE_USING_RX_FLOW_CONTROL 0x00000800
/* Controller capability flags. */
u32 bce_cap_flags;
OpenPOWER on IntegriCloud