i started this to make it avoid IFQ_POLL, but i threw some other
changes in to make it easier.
firstly, it checks if there's space on the ring before it proceeds
to try and dequeue a packet for transmission.
rather than advertise 32 dma segments in a transmit slots dma map,
it advertises the 7 that are actually there. if the mbuf has more
segments than that, it calls m_defrag immediately, rather than
trying to fill it and handling the overflow late.
it avoids setting IFF_OACTIVE for another except ring full conditions.
this should make it more robust if the first packet on the tx ring
fails to be encapsulated for some reason.
i do not have any vge(4) hardware, so i need someone to test this.
otherwise ill put it in as is and we may end up with a nasty surprise
in the future.
Index: if_vge.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_vge.c,v
retrieving revision 1.65
diff -u -p -r1.65 if_vge.c
--- if_vge.c 25 Oct 2015 13:04:28 -0000 1.65
+++ if_vge.c 12 Nov 2015 03:19:44 -0000
@@ -620,8 +620,8 @@ vge_allocmem(struct vge_softc *sc)
/* Create DMA maps for TX buffers */
for (i = 0; i < VGE_TX_DESC_CNT; i++) {
- error = bus_dmamap_create(sc->sc_dmat, MCLBYTES * nseg, nseg,
- MCLBYTES, 0, BUS_DMA_ALLOCNOW,
+ error = bus_dmamap_create(sc->sc_dmat, MCLBYTES * nseg,
+ VGE_TX_FRAGS, MCLBYTES, 0, BUS_DMA_ALLOCNOW,
&sc->vge_ldata.vge_tx_dmamap[i]);
if (error) {
printf("%s: can't create DMA map for TX\n",
@@ -1321,13 +1321,12 @@ vge_intr(void *arg)
int
vge_encap(struct vge_softc *sc, struct mbuf *m_head, int idx)
{
- struct ifnet *ifp = &sc->arpcom.ac_if;
bus_dmamap_t txmap;
struct vge_tx_desc *d = NULL;
struct vge_tx_frag *f;
- struct mbuf *mnew = NULL;
int error, frag;
u_int32_t vge_flags;
+ unsigned int len;
vge_flags = 0;
@@ -1339,14 +1338,19 @@ vge_encap(struct vge_softc *sc, struct m
vge_flags |= VGE_TDCTL_UDPCSUM;
txmap = sc->vge_ldata.vge_tx_dmamap[idx];
-repack:
error = bus_dmamap_load_mbuf(sc->sc_dmat, txmap,
m_head, BUS_DMA_NOWAIT);
- if (error) {
- printf("%s: can't map mbuf (error %d)\n",
- sc->vge_dev.dv_xname, error);
- return (ENOBUFS);
- }
+ switch (error) {
+ case 0:
+ break;
+ case EFBIG: /* mbuf chain is too fragmented */
+ if ((error = m_defrag(m_head, M_DONTWAIT)) == 0 &&
+ (error = bus_dmamap_load_mbuf(sc->sc_dmat, txmap, m_head,
+ BUS_DMA_NOWAIT)) == 0)
+ break;
+ default:
+ return (error);
+ }
d = &sc->vge_ldata.vge_tx_list[idx];
/* If owned by chip, fail */
@@ -1354,40 +1358,12 @@ repack:
return (ENOBUFS);
for (frag = 0; frag < txmap->dm_nsegs; frag++) {
- /* Check if we have used all 7 fragments. */
- if (frag == VGE_TX_FRAGS)
- break;
f = &d->vge_frag[frag];
f->vge_buflen =
htole16(VGE_BUFLEN(txmap->dm_segs[frag].ds_len));
f->vge_addrlo =
htole32(VGE_ADDR_LO(txmap->dm_segs[frag].ds_addr));
f->vge_addrhi =
htole16(VGE_ADDR_HI(txmap->dm_segs[frag].ds_addr) & 0xFFFF);
}
- /*
- * We used up all 7 fragments! Now what we have to do is
- * copy the data into a mbuf cluster and map that.
- */
- if (frag == VGE_TX_FRAGS) {
- MGETHDR(mnew, M_DONTWAIT, MT_DATA);
- if (mnew == NULL)
- return (ENOBUFS);
-
- if (m_head->m_pkthdr.len > MHLEN) {
- MCLGET(mnew, M_DONTWAIT);
- if (!(mnew->m_flags & M_EXT)) {
- m_freem(mnew);
- return (ENOBUFS);
- }
- }
- m_copydata(m_head, 0, m_head->m_pkthdr.len,
- mtod(mnew, caddr_t));
- mnew->m_pkthdr.len = mnew->m_len = m_head->m_pkthdr.len;
- IFQ_DEQUEUE(&ifp->if_snd, m_head);
- m_freem(m_head);
- m_head = mnew;
- goto repack;
- }
-
/* This chip does not do auto-padding */
if (m_head->m_pkthdr.len < VGE_MIN_FRAMELEN) {
f = &d->vge_frag[frag];
@@ -1396,19 +1372,21 @@ repack:
m_head->m_pkthdr.len));
f->vge_addrlo = htole32(VGE_ADDR_LO(txmap->dm_segs[0].ds_addr));
f->vge_addrhi = htole16(VGE_ADDR_HI(txmap->dm_segs[0].ds_addr)
& 0xFFFF);
- m_head->m_pkthdr.len = VGE_MIN_FRAMELEN;
+ len = VGE_MIN_FRAMELEN;
frag++;
- }
+ } else
+ len = m_head->m_pkthdr.len;
+
/* For some reason, we need to tell the card fragment + 1 */
frag++;
bus_dmamap_sync(sc->sc_dmat, txmap, 0, txmap->dm_mapsize,
BUS_DMASYNC_PREWRITE);
- d->vge_sts = htole32(m_head->m_pkthdr.len << 16);
+ d->vge_sts = htole32(len << 16);
d->vge_ctl = htole32(vge_flags|(frag << 28) | VGE_TD_LS_NORM);
- if (m_head->m_pkthdr.len > ETHERMTU + ETHER_HDR_LEN)
+ if (len > ETHERMTU + ETHER_HDR_LEN)
d->vge_ctl |= htole32(VGE_TDCTL_JUMBO);
#if NVLAN > 0
@@ -1425,10 +1403,6 @@ repack:
sc->vge_ldata.vge_tx_list[idx].vge_sts |= htole32(VGE_TDSTS_OWN);
idx++;
- if (mnew == NULL) {
- /* if mbuf is coalesced, it is already dequeued */
- IFQ_DEQUEUE(&ifp->if_snd, m_head);
- }
return (0);
}
@@ -1456,11 +1430,21 @@ vge_start(struct ifnet *ifp)
if (pidx < 0)
pidx = VGE_TX_DESC_CNT - 1;
- while (sc->vge_ldata.vge_tx_mbuf[idx] == NULL) {
- IFQ_POLL(&ifp->if_snd, m_head);
+ for (;;) {
+ if (sc->vge_ldata.vge_tx_mbuf[idx] != NULL) {
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+
+ IFQ_DEQUEUE(&ifp->if_snd, m_head);
if (m_head == NULL)
break;
+ if (vge_encap(sc, m_head, idx)) {
+ m_freem(m_head);
+ continue;
+ }
+
/*
* If there's a BPF listener, bounce a copy of this frame
* to him.
@@ -1469,11 +1453,6 @@ vge_start(struct ifnet *ifp)
if (ifp->if_bpf)
bpf_mtap_ether(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
#endif
-
- if (vge_encap(sc, m_head, idx)) {
- ifp->if_flags |= IFF_OACTIVE;
- break;
- }
sc->vge_ldata.vge_tx_list[pidx].vge_frag[0].vge_buflen |=
htole16(VGE_TXDESC_Q);