summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorluigi <luigi@FreeBSD.org>2002-08-04 22:33:28 +0000
committerluigi <luigi@FreeBSD.org>2002-08-04 22:33:28 +0000
commiteab6aceed81b6fde781962b732f25806087a9cda (patch)
tree3ee0072c94fb0b731a4fbf50c0a05386e3018bca /sys
parente1a15dd5a4c4e67d3ccf6fcf80ae8abf952c240c (diff)
downloadFreeBSD-src-eab6aceed81b6fde781962b732f25806087a9cda.zip
FreeBSD-src-eab6aceed81b6fde781962b732f25806087a9cda.tar.gz
Fix handling of Receiver Not Ready errors when doing polling.
Also take this chance to cleanup the code in fxp_intr_body. Add a missing block of code to disable interrupts when reinitializing the interface while doing polling (the RELENG_4 version was correct). MFC after: 3 days
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/fxp/if_fxp.c132
1 files changed, 82 insertions, 50 deletions
diff --git a/sys/dev/fxp/if_fxp.c b/sys/dev/fxp/if_fxp.c
index 2790079..bbcc987 100644
--- a/sys/dev/fxp/if_fxp.c
+++ b/sys/dev/fxp/if_fxp.c
@@ -242,6 +242,9 @@ DRIVER_MODULE(if_fxp, pci, fxp_driver, fxp_devclass, 0, 0);
DRIVER_MODULE(if_fxp, cardbus, fxp_driver, fxp_devclass, 0, 0);
DRIVER_MODULE(miibus, fxp, miibus_driver, miibus_devclass, 0, 0);
+static int fxp_rnr;
+SYSCTL_INT(_hw, OID_AUTO, fxp_rnr, CTLFLAG_RW, &fxp_rnr, 0, "fxp rnr events");
+
/*
* Inline function to copy a 16-bit aligned 32-bit quantity.
*/
@@ -1226,6 +1229,12 @@ static void
fxp_intr_body(struct fxp_softc *sc, u_int8_t statack, int count)
{
struct ifnet *ifp = &sc->sc_if;
+ struct mbuf *m;
+ struct fxp_rfa *rfa;
+ int rnr = (statack & FXP_SCB_STATACK_RNR) ? 1 : 0;
+
+ if (rnr)
+ fxp_rnr++;
/*
* Free any finished transmit mbuf chains.
@@ -1264,71 +1273,85 @@ fxp_intr_body(struct fxp_softc *sc, u_int8_t statack, int count)
if (ifp->if_snd.ifq_head != NULL)
fxp_start(ifp);
}
+
+ /*
+ * Just return if nothing happened on the receive side.
+ */
+ if ( (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) == 0)
+ return;
+
/*
* Process receiver interrupts. If a no-resource (RNR)
* condition exists, get whatever packets we can and
* re-start the receiver.
+ * When using polling, we do not process the list to completion,
+ * so when we get an RNR interrupt we must defer the restart
+ * until we hit the last buffer with the C bit set.
+ * If we run out of cycles and rfa_headm has the C bit set,
+ * record the pending RNR in an unused status bit, so that the
+ * info will be used in the subsequent polling cycle.
*/
- if (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) {
- struct mbuf *m;
- struct fxp_rfa *rfa;
-rcvloop:
+
+#define FXP_RFA_RNRMARK 0x4000 /* used to mark a pending RNR intr */
+
+ for (;;) {
m = sc->rfa_headm;
rfa = (struct fxp_rfa *)(m->m_ext.ext_buf +
RFA_ALIGNMENT_FUDGE);
#ifdef DEVICE_POLLING /* loop at most count times if count >=0 */
- if (count < 0 || count-- > 0)
-#endif
- if (rfa->rfa_status & FXP_RFA_STATUS_C) {
- /*
- * Remove first packet from the chain.
- */
- sc->rfa_headm = m->m_next;
- m->m_next = NULL;
+ if (count >= 0 && count-- == 0)
+ break;
+#endif /* DEVICE_POLLING */
- /*
- * Add a new buffer to the receive chain.
- * If this fails, the old buffer is recycled
- * instead.
- */
- if (fxp_add_rfabuf(sc, m) == 0) {
- struct ether_header *eh;
- int total_len;
-
- total_len = rfa->actual_size &
- (MCLBYTES - 1);
- if (total_len <
- sizeof(struct ether_header)) {
- m_freem(m);
- goto rcvloop;
- }
+ if ( (rfa->rfa_status & FXP_RFA_STATUS_C) == 0)
+ break;
- /*
- * Drop the packet if it has CRC
- * errors. This test is only needed
- * when doing 802.1q VLAN on the 82557
- * chip.
- */
- if (rfa->rfa_status &
- FXP_RFA_STATUS_CRC) {
- m_freem(m);
- goto rcvloop;
- }
+ if (rfa->rfa_status & FXP_RFA_RNRMARK)
+ rnr = 1;
+ /*
+ * Remove first packet from the chain.
+ */
+ sc->rfa_headm = m->m_next;
+ m->m_next = NULL;
- m->m_pkthdr.rcvif = ifp;
- m->m_pkthdr.len = m->m_len = total_len;
- eh = mtod(m, struct ether_header *);
- m->m_data +=
- sizeof(struct ether_header);
- m->m_len -=
- sizeof(struct ether_header);
- m->m_pkthdr.len = m->m_len;
- ether_input(ifp, eh, m);
+ /*
+ * Add a new buffer to the receive chain.
+ * If this fails, the old buffer is recycled
+ * instead.
+ */
+ if (fxp_add_rfabuf(sc, m) == 0) {
+ struct ether_header *eh;
+ int total_len;
+
+ /*
+ * Fetch packet length (the top 2 bits of
+ * actual_size are flags set by the controller
+ * upon completion), and drop the packet in case
+ * of bogus length or CRC errors.
+ */
+ total_len = rfa->actual_size & 0x3fff;
+ if (total_len < sizeof(struct ether_header) ||
+ total_len > MCLBYTES - RFA_ALIGNMENT_FUDGE -
+ sizeof(struct fxp_rfa) ||
+ rfa->rfa_status & FXP_RFA_STATUS_CRC) {
+ m_freem(m);
+ continue;
}
- goto rcvloop;
+
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = total_len;
+ eh = mtod(m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+ m->m_len -= sizeof(struct ether_header);
+ m->m_pkthdr.len = m->m_len;
+ ether_input(ifp, eh, m);
}
- if (statack & FXP_SCB_STATACK_RNR) {
+ }
+ if (rnr) {
+ if (rfa->rfa_status & FXP_RFA_STATUS_C)
+ rfa->rfa_status |= FXP_RFA_RNRMARK;
+ else {
fxp_scb_wait(sc);
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL,
vtophys(sc->rfa_headm->m_ext.ext_buf) +
@@ -1763,6 +1786,15 @@ fxp_init(void *xsc)
/*
* Enable interrupts.
*/
+#ifdef DEVICE_POLLING
+ /*
+ * ... but only do that if we are not polling. And because (presumably)
+ * the default is interrupts on, we need to disable them explicitly!
+ */
+ if ( ifp->if_ipending & IFF_POLLING )
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, FXP_SCB_INTR_DISABLE);
+ else
+#endif /* DEVICE_POLLING */
CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, 0);
splx(s);
OpenPOWER on IntegriCloud