Darren Tucker's vlan tagging for vr motivated me. Here is a diff that
implements transmit DMA segments, instead of copying fragmented mbufs every
time. This should be a win for userland traffic, and NFS. It also implements a
FreeBSD feature to only ask for TX completion interrupts every 8 packets,
instead of every packet, which is another win for weak CPUs. FreeBSD has been
doing DMA tx segments and 1/8 completion interrupts for 4 years across the same
chips. Annoyingly, on first glance, the rhine chip still seems to send the same
number completion interrupts. But it's clear that bus_dmamap_load_mbuf no
longer fails at the top of vr_encap on packets with 8 or less mbuf fragments,
avoiding the whole new mbuf and m_copydata dance for the majority of situations
now. The next win would be to copy reyk's method from if_myx to create a new
DMA segment for padding packets < VR_MINFRAMELEN instead of create a whole new
mbuf and copying. Micro-optimizations for micro-platforms.
This is heavily influenced by yongari@FreeBSD's work 4 years ago. (In fact,
maybe too much so. As far as I can tell, allowing for DMA transfers of MCLBYTES
* VR_MAXFRAGS is overkill since a packet over the size of MCLBYTES isn't even
possible with this chip. Also returns from vr_encap are now ENOFBUFS but the
error gets ignored upstream at this point.)
Index: if_vr.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_vr.c,v
retrieving revision 1.115
diff -u -r1.115 if_vr.c
--- if_vr.c 18 Sep 2012 14:49:44 -0000 1.115
+++ if_vr.c 4 Oct 2012 17:12:08 -0000
@@ -113,7 +113,7 @@
NULL, "vr", DV_IFNET
};
-int vr_encap(struct vr_softc *, struct vr_chain *, struct mbuf *);
+int vr_encap(struct vr_softc *, struct vr_chain **, struct mbuf *);
void vr_rxeof(struct vr_softc *);
void vr_rxeoc(struct vr_softc *);
void vr_txeof(struct vr_softc *);
@@ -720,13 +720,17 @@
cd = &sc->vr_cdata;
ld = sc->vr_ldata;
+
+ cd->vr_tx_pkts = 0;
+ cd->vr_tx_cnt = 0;
+
for (i = 0; i < VR_TX_LIST_CNT; i++) {
cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i];
cd->vr_tx_chain[i].vr_paddr =
sc->sc_listmap->dm_segs[0].ds_addr +
offsetof(struct vr_list_data, vr_tx_list[i]);
- if (bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
+ if (bus_dmamap_create(sc->sc_dmat, MCLBYTES * VR_MAXFRAGS,
VR_MAXFRAGS,
MCLBYTES, 0, BUS_DMA_NOWAIT, &cd->vr_tx_chain[i].vr_map))
return (ENOBUFS);
@@ -984,11 +988,13 @@
* frames that have been transmitted.
*/
cur_tx = sc->vr_cdata.vr_tx_cons;
- while(cur_tx->vr_mbuf != NULL) {
- u_int32_t txstat;
+ while (cur_tx != sc->vr_cdata.vr_tx_prod) {
+
+ u_int32_t txstat, txctl;
int i;
txstat = letoh32(cur_tx->vr_ptr->vr_status);
+ txctl = letoh32(cur_tx->vr_ptr->vr_ctl);
if ((txstat & VR_TXSTAT_ABRT) ||
(txstat & VR_TXSTAT_UDF)) {
@@ -1002,7 +1008,7 @@
sc->vr_flags |= VR_F_RESTART;
break;
}
- VR_TXOWN(cur_tx) = htole32(VR_TXSTAT_OWN);
+ cur_tx->vr_ptr->vr_status = htole32(VR_TXSTAT_OWN);
CSR_WRITE_4(sc, VR_TXADDR, cur_tx->vr_paddr);
break;
}
@@ -1010,6 +1016,11 @@
if (txstat & VR_TXSTAT_OWN)
break;
+ sc->vr_cdata.vr_tx_cnt--;
+ /* Only the first descriptor in the chain is valid. */
+ if ((txctl & VR_TXCTL_FIRSTFRAG) == 0)
+ goto next;
+
if (txstat & VR_TXSTAT_ERRSUM) {
ifp->if_oerrors++;
if (txstat & VR_TXSTAT_DEFER)
@@ -1028,11 +1039,12 @@
cur_tx->vr_mbuf = NULL;
ifp->if_flags &= ~IFF_OACTIVE;
+next:
cur_tx = cur_tx->vr_nextdesc;
}
sc->vr_cdata.vr_tx_cons = cur_tx;
- if (cur_tx->vr_mbuf == NULL)
+ if (sc->vr_cdata.vr_tx_cnt == 0)
ifp->if_timer = 0;
}
@@ -1164,19 +1176,22 @@
* pointers to the fragment pointers.
*/
int
-vr_encap(struct vr_softc *sc, struct vr_chain *c, struct mbuf *m_head)
+vr_encap(struct vr_softc *sc, struct vr_chain **cp, struct mbuf *m_head)
{
+ struct vr_chain *c = *cp;
struct vr_desc *f = NULL;
struct mbuf *m_new = NULL;
- u_int32_t vr_flags = 0, vr_status = 0;
+ u_int32_t vr_ctl = 0, vr_status = 0;
+ bus_dmamap_t txmap;
+ int i;
if (sc->vr_quirks & VR_Q_CSUM) {
if (m_head->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT)
- vr_flags |= VR_TXCTL_IPCSUM;
+ vr_ctl |= VR_TXCTL_IPCSUM;
if (m_head->m_pkthdr.csum_flags & M_TCP_CSUM_OUT)
- vr_flags |= VR_TXCTL_TCPCSUM;
+ vr_ctl |= VR_TXCTL_TCPCSUM;
if (m_head->m_pkthdr.csum_flags & M_UDP_CSUM_OUT)
- vr_flags |= VR_TXCTL_UDPCSUM;
+ vr_ctl |= VR_TXCTL_UDPCSUM;
}
if (sc->vr_quirks & VR_Q_NEEDALIGN ||
@@ -1213,28 +1228,51 @@
if (bus_dmamap_load_mbuf(sc->sc_dmat, c->vr_map, m_new,
BUS_DMA_NOWAIT | BUS_DMA_WRITE)) {
m_freem(m_new);
- return (1);
+ return(ENOBUFS);
}
+ c->vr_mbuf = m_new;
+ } else {
+ c->vr_mbuf = m_head;
}
bus_dmamap_sync(sc->sc_dmat, c->vr_map, 0, c->vr_map->dm_mapsize,
BUS_DMASYNC_PREWRITE);
- if (m_new != NULL) {
+ /* Check number of available descriptors. */
+ if (sc->vr_cdata.vr_tx_cnt + c->vr_map->dm_nsegs >= (VR_TX_LIST_CNT -
1)) {
+ printf("vr_tx_cnt %i dm_nsegs %i\n", sc->vr_cdata.vr_tx_cnt,
c->vr_map->dm_nsegs);
+ bus_dmamap_unload(sc->sc_dmat, c->vr_map);
+ if (m_new)
+ m_freem(m_new);
+ return(ENOBUFS);
+ }
+ if (m_new)
m_freem(m_head);
- c->vr_mbuf = m_new;
- } else
- c->vr_mbuf = m_head;
-
- f = c->vr_ptr;
- f->vr_data = htole32(c->vr_map->dm_segs[0].ds_addr);
- f->vr_ctl = htole32(c->vr_map->dm_mapsize);
- f->vr_ctl |= htole32(vr_flags|VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG);
- f->vr_status = htole32(vr_status);
+ txmap = c->vr_map;
+ for (i = 0; i < txmap->dm_nsegs; i++) {
+ if (i != 0)
+ *cp = c = c->vr_nextdesc;
+ f = c->vr_ptr;
+ f->vr_ctl = htole32(txmap->dm_segs[i].ds_len | VR_TXCTL_TLINK |
+ vr_ctl);
+ if (i == 0)
+ f->vr_ctl |= htole32(VR_TXCTL_FIRSTFRAG);
+ f->vr_status = htole32(vr_status);
+ f->vr_data = htole32(txmap->dm_segs[i].ds_addr);
+ f->vr_next = htole32(c->vr_nextdesc->vr_paddr);
+ sc->vr_cdata.vr_tx_cnt++;
+ }
- f->vr_ctl |= htole32(VR_TXCTL_LASTFRAG|VR_TXCTL_FINT);
- f->vr_next = htole32(c->vr_nextdesc->vr_paddr);
+ /*
+ * Set EOP on the last desciptor and request Tx completion
+ * interrupt for every VR_TX_INTR_THRESH-th frames.
+ */
+ VR_INC(sc->vr_cdata.vr_tx_pkts, VR_TX_INTR_THRESH);
+ if (sc->vr_cdata.vr_tx_pkts == 0)
+ f->vr_ctl |= htole32(VR_TXCTL_LASTFRAG | VR_TXCTL_FINT);
+ else
+ f->vr_ctl |= htole32(VR_TXCTL_LASTFRAG);
return (0);
}
@@ -1251,7 +1289,7 @@
{
struct vr_softc *sc;
struct mbuf *m_head;
- struct vr_chain *cur_tx;
+ struct vr_chain *cur_tx, *head_tx;
sc = ifp->if_softc;
@@ -1265,7 +1303,8 @@
break;
/* Pack the data into the descriptor. */
- if (vr_encap(sc, cur_tx, m_head)) {
+ head_tx = cur_tx;
+ if (vr_encap(sc, &cur_tx, m_head)) {
/* Rollback, send what we were able to encap. */
if (ALTQ_IS_ENABLED(&ifp->if_snd))
m_freem(m_head);
@@ -1274,7 +1313,8 @@
break;
}
- VR_TXOWN(cur_tx) = htole32(VR_TXSTAT_OWN);
+ /* Only set ownership bit on first descriptor */
+ head_tx->vr_ptr->vr_status |= htole32(VR_TXSTAT_OWN);
#if NBPFILTER > 0
/*
@@ -1282,12 +1322,12 @@
* to him.
*/
if (ifp->if_bpf)
- bpf_mtap_ether(ifp->if_bpf, cur_tx->vr_mbuf,
+ bpf_mtap_ether(ifp->if_bpf, head_tx->vr_mbuf,
BPF_DIRECTION_OUT);
#endif
cur_tx = cur_tx->vr_nextdesc;
}
- if (cur_tx != sc->vr_cdata.vr_tx_prod || cur_tx->vr_mbuf != NULL) {
+ if (sc->vr_cdata.vr_tx_cnt != 0) {
sc->vr_cdata.vr_tx_prod = cur_tx;
bus_dmamap_sync(sc->sc_dmat, sc->sc_listmap, 0,
@@ -1513,6 +1553,15 @@
struct vr_softc *sc;
sc = ifp->if_softc;
+
+ /*
+ * Reclaim first as we don't request interrupt for every packet.
+ */
+ printf("vr_watchdog vr_tx_cnt %i\n",sc->vr_cdata.vr_tx_cnt);
+ vr_txeof(sc);
+ printf("vr_watchdog vr_tx_cnt %i (after
txeof)\n",sc->vr_cdata.vr_tx_cnt);
+ if (sc->vr_cdata.vr_tx_cnt == 0)
+ return;
ifp->if_oerrors++;
printf("%s: watchdog timeout\n", sc->sc_dev.dv_xname);
Index: if_vrreg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_vrreg.h,v
retrieving revision 1.30
diff -u -r1.30 if_vrreg.h
--- if_vrreg.h 5 Jan 2012 19:08:25 -0000 1.30
+++ if_vrreg.h 4 Oct 2012 17:12:08 -0000
@@ -417,13 +417,14 @@
#define VR_TXCTL_LASTFRAG 0x00400000
#define VR_TXCTL_FINT 0x00800000
-#define VR_MAXFRAGS 16
-#define VR_RX_LIST_CNT 64
+#define VR_MAXFRAGS 8
+#define VR_TX_INTR_THRESH 8
+#define VR_RX_LIST_CNT 128
#define VR_TX_LIST_CNT 128
#define VR_MIN_FRAMELEN 60
#define VR_RXLEN 1524
-#define VR_TXOWN(x) x->vr_ptr->vr_status
+#define VR_INC(x,y) (x) = (((x) + 1) % y)
struct vr_list_data {
struct vr_desc vr_rx_list[VR_RX_LIST_CNT];
@@ -456,6 +457,8 @@
struct vr_chain *vr_tx_cons;
struct vr_chain *vr_tx_prod;
+ int vr_tx_cnt;
+ int vr_tx_pkts;
};
struct vr_mii_frame {