summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbluhm <bluhm@openbsd.org>2018-02-07 22:35:14 +0000
committerbluhm <bluhm@openbsd.org>2018-02-07 22:35:14 +0000
commit7379147ac38e139e31ca13c253bc8c56ed7ff10b (patch)
treeb3388c56aa6dbe4f21738dbcb283e29d213927fb
parentupdate the gre driver. (diff)
downloadwireguard-openbsd-7379147ac38e139e31ca13c253bc8c56ed7ff10b.tar.xz
wireguard-openbsd-7379147ac38e139e31ca13c253bc8c56ed7ff10b.zip
Sporadically the network over gem(4) interface hang on sparc64 and
macppc. Receiving packets stopped, ifconfig down/up made it work again. In the tick timeout handler refill the receive ring if it is empty. The logic is taken from hme(4). Also protect the register access and ifp counters with splnet(). In gem_rx_watchdog() is a workaround for a hardware bug. It resets the hardware when there is no progress. If the fifo pointers advanced a bit, it got stuck anyway. So restart the receive watchdog timeout in that case. OK mpi@
-rw-r--r--sys/dev/ic/gem.c44
1 files changed, 31 insertions, 13 deletions
diff --git a/sys/dev/ic/gem.c b/sys/dev/ic/gem.c
index c1c34e68b4f..ced354c5b15 100644
--- a/sys/dev/ic/gem.c
+++ b/sys/dev/ic/gem.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: gem.c,v 1.122 2017/06/08 01:34:00 dlg Exp $ */
+/* $OpenBSD: gem.c,v 1.123 2018/02/07 22:35:14 bluhm Exp $ */
/* $NetBSD: gem.c,v 1.1 2001/09/16 00:11:43 eeh Exp $ */
/*
@@ -423,6 +423,7 @@ gem_tick(void *arg)
int s;
u_int32_t v;
+ s = splnet();
/* unload collisions counters */
v = bus_space_read_4(t, mac, GEM_MAC_EXCESS_COLL_CNT) +
bus_space_read_4(t, mac, GEM_MAC_LATE_COLL_CNT);
@@ -448,7 +449,15 @@ gem_tick(void *arg)
bus_space_write_4(t, mac, GEM_MAC_RX_CRC_ERR_CNT, 0);
bus_space_write_4(t, mac, GEM_MAC_RX_CODE_VIOL, 0);
- s = splnet();
+ /*
+ * If buffer allocation fails, the receive ring may become
+ * empty. There is no receive interrupt to recover from that.
+ */
+ if (if_rxr_inuse(&sc->sc_rx_ring) == 0) {
+ gem_fill_rx_ring(sc);
+ bus_space_write_4(t, mac, GEM_RX_KICK, sc->sc_rx_prod);
+ }
+
mii_tick(&sc->sc_mii);
splx(s);
@@ -1200,17 +1209,26 @@ gem_rx_watchdog(void *arg)
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.
- */
- gem_init(ifp);
+ if ((state & GEM_MAC_STATE_OVERFLOW) == GEM_MAC_STATE_OVERFLOW) {
+ if ((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.
+ */
+ gem_init(ifp);
+ } else {
+ /*
+ * We made some progress, but is not certain that the
+ * overflow condition has been resolved. Check again.
+ */
+ sc->sc_rx_fifo_wr_ptr = rx_fifo_wr_ptr;
+ sc->sc_rx_fifo_rd_ptr = rx_fifo_rd_ptr;
+ timeout_add_msec(&sc->sc_rx_watchdog, 400);
+ }
}
}