summaryrefslogtreecommitdiffstats
path: root/sys/dev/vr
diff options
context:
space:
mode:
authorru <ru@FreeBSD.org>2004-04-05 17:39:57 +0000
committerru <ru@FreeBSD.org>2004-04-05 17:39:57 +0000
commit52fdcf73b01b92339a356bf0e0569ce02bb6be08 (patch)
tree2869ef6c88cb39bba3dc715288077ea9a8450cef /sys/dev/vr
parent8b7744ab537f8854119e0ee36a9cc035a8607d05 (diff)
downloadFreeBSD-src-52fdcf73b01b92339a356bf0e0569ce02bb6be08.zip
FreeBSD-src-52fdcf73b01b92339a356bf0e0569ce02bb6be08.tar.gz
- Rewritten TX to use only two pointers to track producer/consumer.
- Added polling(4) support! - Bugfix: don't forget to set IFF_OACTIVE when TX list is full. - Minor: tidy up vr_encap().
Diffstat (limited to 'sys/dev/vr')
-rw-r--r--sys/dev/vr/if_vr.c280
-rw-r--r--sys/dev/vr/if_vrreg.h8
2 files changed, 161 insertions, 127 deletions
diff --git a/sys/dev/vr/if_vr.c b/sys/dev/vr/if_vr.c
index a91a5a7..33a583a 100644
--- a/sys/dev/vr/if_vr.c
+++ b/sys/dev/vr/if_vr.c
@@ -137,7 +137,6 @@ static int vr_encap (struct vr_softc *, struct vr_chain *,
static void vr_rxeof (struct vr_softc *);
static void vr_rxeoc (struct vr_softc *);
static void vr_txeof (struct vr_softc *);
-static void vr_txeoc (struct vr_softc *);
static void vr_tick (void *);
static void vr_intr (void *);
static void vr_start (struct ifnet *);
@@ -952,8 +951,7 @@ vr_list_tx_init(sc)
&cd->vr_tx_chain[i + 1];
}
- cd->vr_tx_free = &cd->vr_tx_chain[0];
- cd->vr_tx_tail = cd->vr_tx_head = NULL;
+ cd->vr_tx_cons = cd->vr_tx_prod = &cd->vr_tx_chain[0];
return(0);
}
@@ -1048,7 +1046,7 @@ static void
vr_rxeof(sc)
struct vr_softc *sc;
{
- struct mbuf *m;
+ struct mbuf *m, *m0;
struct ifnet *ifp;
struct vr_chain_onefrag *cur_rx;
int total_len = 0;
@@ -1060,8 +1058,14 @@ vr_rxeof(sc)
while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) &
VR_RXSTAT_OWN)) {
- struct mbuf *m0 = NULL;
-
+#ifdef DEVICE_POLLING
+ if (ifp->if_flags & IFF_POLLING) {
+ if (sc->rxcycles <= 0)
+ break;
+ sc->rxcycles--;
+ }
+#endif /* DEVICE_POLLING */
+ m0 = NULL;
cur_rx = sc->vr_cdata.vr_rx_head;
sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc;
m = cur_rx->vr_mbuf;
@@ -1173,22 +1177,15 @@ vr_txeof(sc)
ifp = &sc->arpcom.ac_if;
- /* Reset the timeout timer; if_txeoc will clear it. */
- ifp->if_timer = 5;
-
- /* Sanity check. */
- if (sc->vr_cdata.vr_tx_head == NULL)
- return;
-
/*
* Go through our tx list and free mbufs for those
* frames that have been transmitted.
*/
- while(sc->vr_cdata.vr_tx_head->vr_mbuf != NULL) {
+ cur_tx = sc->vr_cdata.vr_tx_cons;
+ while (cur_tx->vr_mbuf != NULL) {
u_int32_t txstat;
int i;
- cur_tx = sc->vr_cdata.vr_tx_head;
txstat = cur_tx->vr_ptr->vr_status;
if ((txstat & VR_TXSTAT_ABRT) ||
@@ -1221,41 +1218,15 @@ vr_txeof(sc)
ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3;
ifp->if_opackets++;
- if (cur_tx->vr_mbuf != NULL) {
- m_freem(cur_tx->vr_mbuf);
- cur_tx->vr_mbuf = NULL;
- }
-
- if (sc->vr_cdata.vr_tx_head == sc->vr_cdata.vr_tx_tail) {
- sc->vr_cdata.vr_tx_head = NULL;
- sc->vr_cdata.vr_tx_tail = NULL;
- break;
- }
+ m_freem(cur_tx->vr_mbuf);
+ cur_tx->vr_mbuf = NULL;
+ ifp->if_flags &= ~IFF_OACTIVE;
- sc->vr_cdata.vr_tx_head = cur_tx->vr_nextdesc;
+ cur_tx = cur_tx->vr_nextdesc;
}
-
- return;
-}
-
-/*
- * TX 'end of channel' interrupt handler.
- */
-static void
-vr_txeoc(sc)
- struct vr_softc *sc;
-{
- struct ifnet *ifp;
-
- ifp = &sc->arpcom.ac_if;
-
- if (sc->vr_cdata.vr_tx_head == NULL) {
- ifp->if_flags &= ~IFF_OACTIVE;
- sc->vr_cdata.vr_tx_tail = NULL;
+ sc->vr_cdata.vr_tx_cons = cur_tx;
+ if (cur_tx->vr_mbuf == NULL)
ifp->if_timer = 0;
- }
-
- return;
}
static void
@@ -1285,6 +1256,78 @@ vr_tick(xsc)
return;
}
+#ifdef DEVICE_POLLING
+static poll_handler_t vr_poll;
+
+static void
+vr_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+ struct vr_softc *sc = ifp->if_softc;
+
+ VR_LOCK(sc);
+ if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */
+ CSR_WRITE_2(sc, VR_IMR, VR_INTRS);
+ goto done;
+ }
+
+ sc->rxcycles = count;
+ vr_rxeof(sc);
+ vr_txeof(sc);
+ if (ifp->if_snd.ifq_head != NULL)
+ vr_start(ifp);
+
+ if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */
+ u_int16_t status;
+
+ status = CSR_READ_2(sc, VR_ISR);
+ if (status)
+ CSR_WRITE_2(sc, VR_ISR, status);
+
+ if ((status & VR_INTRS) == 0)
+ goto done;
+
+ if (status & VR_ISR_RX_DROPPED) {
+ printf("vr%d: rx packet lost\n", sc->vr_unit);
+ ifp->if_ierrors++;
+ }
+
+ if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) ||
+ (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW)) {
+ printf("vr%d: receive error (%04x)",
+ sc->vr_unit, status);
+ if (status & VR_ISR_RX_NOBUF)
+ printf(" no buffers");
+ if (status & VR_ISR_RX_OFLOW)
+ printf(" overflow");
+ if (status & VR_ISR_RX_DROPPED)
+ printf(" packet lost");
+ printf("\n");
+ vr_rxeoc(sc);
+ }
+
+ if ((status & VR_ISR_BUSERR) || (status & VR_ISR_TX_UNDERRUN)) {
+ vr_reset(sc);
+ vr_init(sc);
+ goto done;
+ }
+
+ if ((status & VR_ISR_UDFI) ||
+ (status & VR_ISR_TX_ABRT2) ||
+ (status & VR_ISR_TX_ABRT)) {
+ ifp->if_oerrors++;
+ if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) {
+ VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON);
+ VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO);
+ }
+ }
+ }
+
+done:
+ VR_UNLOCK(sc);
+
+}
+#endif /* DEVICE_POLLING */
+
static void
vr_intr(arg)
void *arg;
@@ -1297,6 +1340,16 @@ vr_intr(arg)
VR_LOCK(sc);
ifp = &sc->arpcom.ac_if;
+#ifdef DEVICE_POLLING
+ if (ifp->if_flags & IFF_POLLING)
+ goto done;
+ if (ether_poll_register(vr_poll, ifp)) { /* ok, disable interrupts */
+ CSR_WRITE_2(sc, VR_IMR, 0x0000);
+ vr_poll(ifp, 0, 1);
+ goto done;
+ }
+#endif /* DEVICE_POLLING */
+
/* Supress unwanted interrupts. */
if (!(ifp->if_flags & IFF_UP)) {
vr_stop(sc);
@@ -1351,12 +1404,11 @@ vr_intr(arg)
(status & VR_ISR_TX_ABRT2) ||
(status & VR_ISR_TX_ABRT)) {
ifp->if_oerrors++;
- if (sc->vr_cdata.vr_tx_head != NULL) {
+ if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) {
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON);
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO);
}
- } else
- vr_txeoc(sc);
+ }
}
}
@@ -1368,6 +1420,9 @@ vr_intr(arg)
vr_start(ifp);
}
+#ifdef DEVICE_POLLING
+done:
+#endif /* DEVICE_POLLING */
VR_UNLOCK(sc);
return;
@@ -1383,49 +1438,38 @@ vr_encap(sc, c, m_head)
struct vr_chain *c;
struct mbuf *m_head;
{
- int frag = 0;
struct vr_desc *f = NULL;
- int total_len;
struct mbuf *m;
- m = m_head;
- total_len = 0;
-
/*
* The VIA Rhine wants packet buffers to be longword
* aligned, but very often our mbufs aren't. Rather than
* waste time trying to decide when to copy and when not
* to copy, just do it all the time.
*/
- if (m != NULL) {
- struct mbuf *m_new = NULL;
-
- m_new = m_defrag(m_head, M_DONTWAIT);
- if (m_new == NULL) {
- return(1);
- }
+ m = m_defrag(m_head, M_DONTWAIT);
+ if (m == NULL) {
+ return(1);
+ }
- m_head = m_new;
- /*
- * The Rhine chip doesn't auto-pad, so we have to make
- * sure to pad short frames out to the minimum frame length
- * ourselves.
- */
- if (m_head->m_len < VR_MIN_FRAMELEN) {
- m_new->m_pkthdr.len += VR_MIN_FRAMELEN - m_new->m_len;
- m_new->m_len = m_new->m_pkthdr.len;
- }
- f = c->vr_ptr;
- f->vr_data = vtophys(mtod(m_new, caddr_t));
- f->vr_ctl = total_len = m_new->m_len;
- f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG;
- f->vr_status = 0;
- frag = 1;
+ /*
+ * The Rhine chip doesn't auto-pad, so we have to make
+ * sure to pad short frames out to the minimum frame length
+ * ourselves.
+ */
+ if (m->m_len < VR_MIN_FRAMELEN) {
+ m->m_pkthdr.len += VR_MIN_FRAMELEN - m->m_len;
+ m->m_len = m->m_pkthdr.len;
}
- c->vr_mbuf = m_head;
- c->vr_ptr->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT;
- c->vr_ptr->vr_next = vtophys(c->vr_nextdesc->vr_ptr);
+ c->vr_mbuf = m;
+ f = c->vr_ptr;
+ f->vr_data = vtophys(mtod(m, caddr_t));
+ f->vr_ctl = m->m_len;
+ f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG;
+ f->vr_status = 0;
+ f->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT;
+ f->vr_next = vtophys(c->vr_nextdesc->vr_ptr);
return(0);
}
@@ -1442,45 +1486,30 @@ vr_start(ifp)
struct ifnet *ifp;
{
struct vr_softc *sc;
- struct mbuf *m_head = NULL;
- struct vr_chain *cur_tx = NULL, *start_tx, *prev_tx;
+ struct mbuf *m_head;
+ struct vr_chain *cur_tx;
+
+ if (ifp->if_flags & IFF_OACTIVE)
+ return;
sc = ifp->if_softc;
VR_LOCK(sc);
- /*
- * Check for an available queue slot. If there are none,
- * punt.
- */
- if (sc->vr_cdata.vr_tx_free->vr_mbuf != NULL) {
- VR_UNLOCK(sc);
- return;
- }
-
- start_tx = sc->vr_cdata.vr_tx_free;
-
- while(sc->vr_cdata.vr_tx_free->vr_mbuf == NULL) {
+ cur_tx = sc->vr_cdata.vr_tx_prod;
+ while (cur_tx->vr_mbuf == NULL) {
IF_DEQUEUE(&ifp->if_snd, m_head);
if (m_head == NULL)
break;
- /* Pick a descriptor off the free list. */
- prev_tx = cur_tx;
- cur_tx = sc->vr_cdata.vr_tx_free;
- sc->vr_cdata.vr_tx_free = cur_tx->vr_nextdesc;
-
/* Pack the data into the descriptor. */
if (vr_encap(sc, cur_tx, m_head)) {
/* Rollback, send what we were able to encap. */
IF_PREPEND(&ifp->if_snd, m_head);
- sc->vr_cdata.vr_tx_free = cur_tx;
- cur_tx = prev_tx;
break;
}
- if (cur_tx != start_tx)
- VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
+ VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
/*
* If there's a BPF listener, bounce a copy of this frame
@@ -1488,29 +1517,21 @@ vr_start(ifp)
*/
BPF_MTAP(ifp, cur_tx->vr_mbuf);
- VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
+ cur_tx = cur_tx->vr_nextdesc;
}
+ if (cur_tx != sc->vr_cdata.vr_tx_prod || cur_tx->vr_mbuf != NULL) {
+ sc->vr_cdata.vr_tx_prod = cur_tx;
- /*
- * If there are no frames queued, bail.
- */
- if (cur_tx == NULL) {
- VR_UNLOCK(sc);
- return;
- }
+ /* Tell the chip to start transmitting. */
+ VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO);
- sc->vr_cdata.vr_tx_tail = cur_tx;
+ /* Set a timeout in case the chip goes out to lunch. */
+ ifp->if_timer = 5;
- if (sc->vr_cdata.vr_tx_head == NULL)
- sc->vr_cdata.vr_tx_head = start_tx;
-
- /* Tell the chip to start transmitting. */
- VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO);
+ if (cur_tx->vr_mbuf != NULL)
+ ifp->if_flags |= IFF_OACTIVE;
+ }
- /*
- * Set a timeout in case the chip goes out to lunch.
- */
- ifp->if_timer = 5;
VR_UNLOCK(sc);
return;
@@ -1604,10 +1625,18 @@ vr_init(xsc)
CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0]));
+ CSR_WRITE_2(sc, VR_ISR, 0xFFFF);
+#ifdef DEVICE_POLLING
+ /*
+ * Disable interrupts if we are polling.
+ */
+ if (ifp->if_flags & IFF_POLLING)
+ CSR_WRITE_2(sc, VR_IMR, 0);
+ else
+#endif /* DEVICE_POLLING */
/*
* Enable interrupts.
*/
- CSR_WRITE_2(sc, VR_ISR, 0xFFFF);
CSR_WRITE_2(sc, VR_IMR, VR_INTRS);
mii_mediachg(mii);
@@ -1743,6 +1772,10 @@ vr_stop(sc)
ifp->if_timer = 0;
untimeout(vr_tick, sc, sc->vr_stat_ch);
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+#ifdef DEVICE_POLLING
+ ether_poll_deregister(ifp);
+#endif /* DEVICE_POLLING */
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP);
VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON));
@@ -1775,7 +1808,6 @@ vr_stop(sc)
bzero((char *)&sc->vr_ldata->vr_tx_list,
sizeof(sc->vr_ldata->vr_tx_list));
- ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
VR_UNLOCK(sc);
return;
diff --git a/sys/dev/vr/if_vrreg.h b/sys/dev/vr/if_vrreg.h
index 2f8e810..5cf3bb2 100644
--- a/sys/dev/vr/if_vrreg.h
+++ b/sys/dev/vr/if_vrreg.h
@@ -420,9 +420,8 @@ struct vr_chain_data {
struct vr_chain_onefrag *vr_rx_head;
- struct vr_chain *vr_tx_head;
- struct vr_chain *vr_tx_tail;
- struct vr_chain *vr_tx_free;
+ struct vr_chain *vr_tx_cons;
+ struct vr_chain *vr_tx_prod;
};
struct vr_type {
@@ -469,6 +468,9 @@ struct vr_softc {
struct vr_chain_data vr_cdata;
struct callout_handle vr_stat_ch;
struct mtx vr_mtx;
+#ifdef DEVICE_POLLING
+ int rxcycles;
+#endif
};
#define VR_F_RESTART 0x01 /* Restart unit on next tick */
OpenPOWER on IntegriCloud