diff options
author | 2018-02-07 22:35:14 +0000 | |
---|---|---|
committer | 2018-02-07 22:35:14 +0000 | |
commit | 7379147ac38e139e31ca13c253bc8c56ed7ff10b (patch) | |
tree | b3388c56aa6dbe4f21738dbcb283e29d213927fb | |
parent | update the gre driver. (diff) | |
download | wireguard-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.c | 44 |
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); + } } } |