diff options
author | wpaul <wpaul@FreeBSD.org> | 2000-01-03 15:28:47 +0000 |
---|---|---|
committer | wpaul <wpaul@FreeBSD.org> | 2000-01-03 15:28:47 +0000 |
commit | 2ef650c9f40ab002e4d7427ece558f562556a982 (patch) | |
tree | 5d5c7eff961579f5fea73966f9effacbc66270bf /sys/pci/if_xl.c | |
parent | 1d91115edd58dad4b64dff8bfb94651ffb803799 (diff) | |
download | FreeBSD-src-2ef650c9f40ab002e4d7427ece558f562556a982.zip FreeBSD-src-2ef650c9f40ab002e4d7427ece558f562556a982.tar.gz |
It appears that under certain circumstances that I still can't quite pin
down, the dc driver and receiver can fall out of sync with one another,
resulting in a condition where the chip continues to receive packets
but the driver never notices. Normally, the receive handler checks each
descriptor starting from the current producer index to see if the chip
has relinquished ownership, indicating that a packet has been received.
The driver hands the packet off to ether_input() and then prepares the
descriptor to receive another frame before moving on to the next
descriptor in the ring. But sometimes, the chip appears to skip a
descriptor. This leaves the driver testing the status word in a descriptor
that never gets updated. The driver still gets "RX done" interrupts but
never advances further into the RX ring, until the ring fills up and the
chip interrupts again to signal an error condition. Sometimes, the
driver will remain in this desynchronized state, resulting in spotty
performance until the interface is reset.
Fortunately, it's fairly simple to detect this condition: if we call
the rxeof routine but the number of received packets doesn't increase,
we suspect that there could be a problem. In this case, we call a new
routine called dc_rx_resync(), which scans ahead in the RX ring to see
if there's a frame waiting for us somewhere beyond that the driver thinks
is the current producer index. If it finds one, it bumps up the index
and calls the rxeof handler again to snarf up the packet and bring the
driver back in sync with the chip. (It may actually do this several times
in the event that there's more than one "hole" in the ring.)
So far the only card supported by if_dc which has exhibited this problem
is a LinkSys LNE100TX v2.0 (82c115 PNIC II), and it only seems to happen
on one particular system, however the fix is general enough and has low
enough overhead that we may as well apply it for all supported chipsets.
I also implemented the same fix for the 3Com xl driver, which is apparently
vulnerable to the same problem.
Problem originally noted and patch tested by: Matt Dillon
Diffstat (limited to 'sys/pci/if_xl.c')
-rw-r--r-- | sys/pci/if_xl.c | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/sys/pci/if_xl.c b/sys/pci/if_xl.c index 4730185..a48823c 100644 --- a/sys/pci/if_xl.c +++ b/sys/pci/if_xl.c @@ -205,6 +205,7 @@ static int xl_encap_90xB __P((struct xl_softc *, struct xl_chain *, struct mbuf * )); static void xl_rxeof __P((struct xl_softc *)); +static int xl_rx_resync __P((struct xl_softc *)); static void xl_txeof __P((struct xl_softc *)); static void xl_txeof_90xB __P((struct xl_softc *)); static void xl_txeoc __P((struct xl_softc *)); @@ -1676,6 +1677,28 @@ static int xl_newbuf(sc, c) return(0); } +static int xl_rx_resync(sc) + struct xl_softc *sc; +{ + struct xl_chain_onefrag *pos; + int i; + + pos = sc->xl_cdata.xl_rx_head; + + for (i = 0; i < XL_RX_LIST_CNT; i++) { + if (pos->xl_ptr->xl_status) + break; + pos = pos->xl_next; + } + + if (i == XL_RX_LIST_CNT) + return(0); + + sc->xl_cdata.xl_rx_head = pos; + + return(EAGAIN); +} + /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. @@ -1981,8 +2004,16 @@ static void xl_intr(arg) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|(status & XL_INTRS)); - if (status & XL_STAT_UP_COMPLETE) + if (status & XL_STAT_UP_COMPLETE) { + int curpkts; + + curpkts = ifp->if_ipackets; xl_rxeof(sc); + if (curpkts == ifp->if_ipackets) { + while (xl_rx_resync(sc)) + xl_rxeof(sc); + } + } if (status & XL_STAT_DOWN_COMPLETE) { if (sc->xl_type == XL_TYPE_905B) |