diff options
author | yongari <yongari@FreeBSD.org> | 2013-06-30 05:12:18 +0000 |
---|---|---|
committer | yongari <yongari@FreeBSD.org> | 2013-06-30 05:12:18 +0000 |
commit | c43c218e2376600315f1fbd5f72f8db54c391e08 (patch) | |
tree | 5cb975405a4897759167534809d9ee4047925af7 | |
parent | 6b85443c9fe28d9057f94044a2ef66408625279c (diff) | |
download | FreeBSD-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>
-rw-r--r-- | sys/dev/bce/if_bce.c | 33 | ||||
-rw-r--r-- | sys/dev/bce/if_bcereg.h | 1 |
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; |