summaryrefslogtreecommitdiffstats
path: root/sys/pci/if_xl.c
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>2000-01-03 15:28:47 +0000
committerwpaul <wpaul@FreeBSD.org>2000-01-03 15:28:47 +0000
commit2ef650c9f40ab002e4d7427ece558f562556a982 (patch)
tree5d5c7eff961579f5fea73966f9effacbc66270bf /sys/pci/if_xl.c
parent1d91115edd58dad4b64dff8bfb94651ffb803799 (diff)
downloadFreeBSD-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.c33
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)
OpenPOWER on IntegriCloud