Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/ic Apply lockup fixes from Havard Eidnes/OpenBSD in ...
details: https://anonhg.NetBSD.org/src/rev/321738f3b52b
branches: trunk
changeset: 779963:321738f3b52b
user: jdc <jdc%NetBSD.org@localhost>
date: Mon Jul 02 11:23:40 2012 +0000
description:
Apply lockup fixes from Havard Eidnes/OpenBSD in PR port-sparc64/46260:
- add an additional watchdog for RX overflow
- re-initialise the chip on device timeout
Also alter the interrupt blanking rate to 8 packets, as per OpenSolaris.
diffstat:
sys/dev/ic/gem.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++---
sys/dev/ic/gemreg.h | 4 +-
sys/dev/ic/gemvar.h | 7 +++-
3 files changed, 87 insertions(+), 8 deletions(-)
diffs (210 lines):
diff -r af5258024cd1 -r 321738f3b52b sys/dev/ic/gem.c
--- a/sys/dev/ic/gem.c Mon Jul 02 06:55:58 2012 +0000
+++ b/sys/dev/ic/gem.c Mon Jul 02 11:23:40 2012 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: gem.c,v 1.98 2012/02/02 19:43:03 tls Exp $ */
+/* $NetBSD: gem.c,v 1.99 2012/07/02 11:23:40 jdc Exp $ */
/*
*
@@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: gem.c,v 1.98 2012/02/02 19:43:03 tls Exp $");
+__KERNEL_RCSID(0, "$NetBSD: gem.c,v 1.99 2012/07/02 11:23:40 jdc Exp $");
#include "opt_inet.h"
@@ -89,6 +89,7 @@
int gem_ioctl(struct ifnet *, u_long, void *);
void gem_tick(void *);
void gem_watchdog(struct ifnet *);
+void gem_rx_watchdog(void *);
void gem_pcs_start(struct gem_softc *sc);
void gem_pcs_stop(struct gem_softc *sc, int);
int gem_init(struct ifnet *);
@@ -177,6 +178,7 @@
ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
callout_destroy(&sc->sc_tick_ch);
+ callout_destroy(&sc->sc_rx_watchdog);
/*FALLTHROUGH*/
case GEM_ATT_MII:
@@ -613,6 +615,8 @@
#endif
callout_init(&sc->sc_tick_ch, 0);
+ callout_init(&sc->sc_rx_watchdog, 0);
+ callout_setfunc(&sc->sc_rx_watchdog, gem_rx_watchdog, sc);
sc->sc_att_stage = GEM_ATT_FINISHED;
@@ -764,6 +768,8 @@
/* Wait till it finishes */
if (!gem_bitwait(sc, h, GEM_RX_CONFIG, 1, 0))
aprint_error_dev(sc->sc_dev, "cannot disable read dma\n");
+ /* Wait 5ms extra. */
+ delay(5000);
/* Finally, reset the ERX */
bus_space_write_4(t, h2, GEM_RESET, GEM_RESET_RX);
@@ -848,7 +854,7 @@
(3 * sc->sc_rxfifosize / 256) |
((sc->sc_rxfifosize / 256) << 12));
bus_space_write_4(t, h, GEM_RX_BLANKING,
- (6 << GEM_RX_BLANKING_TIME_SHIFT) | 6);
+ (6 << GEM_RX_BLANKING_TIME_SHIFT) | 8);
}
/*
@@ -1824,6 +1830,8 @@
if (gem_add_rxbuf(sc, i) != 0) {
GEM_COUNTER_INCR(sc, sc_ev_rxnobuf);
ifp->if_ierrors++;
+ aprint_error_dev(sc->sc_dev,
+ "receive error: RX no buffer space\n");
GEM_INIT_RXDESC(sc, i);
bus_dmamap_sync(sc->sc_dmatag, rxs->rxs_dmamap, 0,
rxs->rxs_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD);
@@ -2204,12 +2212,20 @@
/*
* At least with GEM_SUN_GEM and some GEM_SUN_ERI
* revisions GEM_MAC_RX_OVERFLOW happen often due to a
- * silicon bug so handle them silently. Moreover, it's
- * likely that the receiver has hung so we reset it.
+ * silicon bug so handle them silently. So if we detect
+ * an RX FIFO overflow, we fire off a timer, and check
+ * whether we're still making progress by looking at the
+ * RX FIFO write and read pointers.
*/
if (rxstat & GEM_MAC_RX_OVERFLOW) {
ifp->if_ierrors++;
- gem_reset_rxdma(sc);
+ aprint_error_dev(sc->sc_dev,
+ "receive error: RX overflow sc->rxptr %d, complete %d\n", sc->sc_rxptr, bus_space_read_4(t, h, GEM_RX_COMPLETION));
+ sc->sc_rx_fifo_wr_ptr =
+ bus_space_read_4(t, h, GEM_RX_FIFO_WR_PTR);
+ sc->sc_rx_fifo_rd_ptr =
+ bus_space_read_4(t, h, GEM_RX_FIFO_RD_PTR);
+ callout_schedule(&sc->sc_rx_watchdog, 400);
} else if (rxstat & ~(GEM_MAC_RX_DONE | GEM_MAC_RX_FRAME_CNT))
printf("%s: MAC rx fault, status 0x%02x\n",
device_xname(sc->sc_dev), rxstat);
@@ -2236,6 +2252,61 @@
return (r);
}
+void
+gem_rx_watchdog(void *arg)
+{
+ struct gem_softc *sc = arg;
+ struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+ bus_space_tag_t t = sc->sc_bustag;
+ bus_space_handle_t h = sc->sc_h1;
+ u_int32_t rx_fifo_wr_ptr;
+ u_int32_t rx_fifo_rd_ptr;
+ u_int32_t state;
+
+ if ((ifp->if_flags & IFF_RUNNING) == 0) {
+ aprint_error_dev(sc->sc_dev, "receiver not running\n");
+ return;
+ }
+
+ rx_fifo_wr_ptr = bus_space_read_4(t, h, GEM_RX_FIFO_WR_PTR);
+ rx_fifo_rd_ptr = bus_space_read_4(t, h, GEM_RX_FIFO_RD_PTR);
+ state = bus_space_read_4(t, h, GEM_MAC_MAC_STATE);
+ if ((state & GEM_MAC_STATE_OVERFLOW) == GEM_MAC_STATE_OVERFLOW &&
+ ((rx_fifo_wr_ptr == rx_fifo_rd_ptr) ||
+ ((sc->sc_rx_fifo_wr_ptr == rx_fifo_wr_ptr) &&
+ (sc->sc_rx_fifo_rd_ptr == rx_fifo_rd_ptr))))
+ {
+ /*
+ * The RX state machine is still in overflow state and
+ * the RX FIFO write and read pointers seem to be
+ * stuck. Whack the chip over the head to get things
+ * going again.
+ */
+ aprint_error_dev(sc->sc_dev,
+ "receiver stuck in overflow, resetting\n");
+ gem_init(ifp);
+ } else {
+ if ((state & GEM_MAC_STATE_OVERFLOW) != GEM_MAC_STATE_OVERFLOW) {
+ aprint_error_dev(sc->sc_dev,
+ "rx_watchdog: not in overflow state: 0x%x\n",
+ state);
+ }
+ if (rx_fifo_wr_ptr != rx_fifo_rd_ptr) {
+ aprint_error_dev(sc->sc_dev,
+ "rx_watchdog: wr & rd ptr different\n");
+ }
+ if (sc->sc_rx_fifo_wr_ptr != rx_fifo_wr_ptr) {
+ aprint_error_dev(sc->sc_dev,
+ "rx_watchdog: wr pointer != saved\n");
+ }
+ if (sc->sc_rx_fifo_rd_ptr != rx_fifo_rd_ptr) {
+ aprint_error_dev(sc->sc_dev,
+ "rx_watchdog: rd pointer != saved\n");
+ }
+ aprint_error_dev(sc->sc_dev, "resetting anyway\n");
+ gem_init(ifp);
+ }
+}
void
gem_watchdog(struct ifnet *ifp)
@@ -2252,6 +2323,7 @@
++ifp->if_oerrors;
/* Try to get more packets going. */
+ gem_init(ifp);
gem_start(ifp);
}
diff -r af5258024cd1 -r 321738f3b52b sys/dev/ic/gemreg.h
--- a/sys/dev/ic/gemreg.h Mon Jul 02 06:55:58 2012 +0000
+++ b/sys/dev/ic/gemreg.h Mon Jul 02 11:23:40 2012 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: gemreg.h,v 1.14 2008/09/15 19:43:24 jdc Exp $ */
+/* $NetBSD: gemreg.h,v 1.15 2012/07/02 11:23:40 jdc Exp $ */
/*
*
@@ -516,6 +516,8 @@
#define GEM_MAC_CC_PASS_PAUSE 0x00000004 /* pass pause up */
#define GEM_MAC_CC_BITS "\177\020b\0TXPAUSE\0b\1RXPAUSE\0b\2NOPAUSE\0\0"
+/* GEM_MAC_MAC_STATE register bits */
+#define GEM_MAC_STATE_OVERFLOW 0x03800000
/*
* Bits in GEM_MAC_SLOT_TIME register
diff -r af5258024cd1 -r 321738f3b52b sys/dev/ic/gemvar.h
--- a/sys/dev/ic/gemvar.h Mon Jul 02 06:55:58 2012 +0000
+++ b/sys/dev/ic/gemvar.h Mon Jul 02 11:23:40 2012 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: gemvar.h,v 1.23 2012/02/02 19:43:03 tls Exp $ */
+/* $NetBSD: gemvar.h,v 1.24 2012/07/02 11:23:40 jdc Exp $ */
/*
*
@@ -130,6 +130,7 @@
struct ethercom sc_ethercom; /* ethernet common data */
struct mii_data sc_mii; /* MII media control */
struct callout sc_tick_ch; /* tick callout */
+ struct callout sc_rx_watchdog; /* RX watchdog callout */
/* The following bus handles are to be provided by the bus front-end */
bus_space_tag_t sc_bustag; /* bus tag */
@@ -223,6 +224,10 @@
struct evcnt sc_ev_rxhist[9];
#endif
+ /* For use by the RX watchdog */
+ u_int32_t sc_rx_fifo_wr_ptr;
+ u_int32_t sc_rx_fifo_rd_ptr;
+
enum gem_attach_stage sc_att_stage;
};
Home |
Main Index |
Thread Index |
Old Index