Author: manu
Date: Sat Nov 18 20:42:48 2017
New Revision: 325978
URL: https://svnweb.freebsd.org/changeset/base/325978

Log:
  if_awg: mark the first tx descriptor as ready only after all the other tx 
descriptors are set up
  
  In a multi segment frame, if the first tx descriptor is marked with 
TX_DESC_CTL
  but not all tx descriptors for the other segments in the frame are set up,
  the TX DMA may transmit an incomplete frame.
  To prevent this, set TX_DESC_CTL for the first tx descriptor only when done
  with all the other segments.
  
  Also, don't bother cleaning transmitted tx descriptors since TX_DESC_CTL
  is cleared for them by the hardware and they will be reprogrammed before
  TX_DESC_CTL is reenabled for them.
  
  Submitted by: Guy Yur <[email protected]>
  Differential Revision:        https://reviews.freebsd.org/D13030

Modified:
  head/sys/arm/allwinner/if_awg.c

Modified: head/sys/arm/allwinner/if_awg.c
==============================================================================
--- head/sys/arm/allwinner/if_awg.c     Sat Nov 18 20:38:05 2017        
(r325977)
+++ head/sys/arm/allwinner/if_awg.c     Sat Nov 18 20:42:48 2017        
(r325978)
@@ -387,35 +387,17 @@ awg_media_change(if_t ifp)
        return (error);
 }
 
-static void
-awg_setup_txdesc(struct awg_softc *sc, int index, int flags, bus_addr_t paddr,
-    u_int len)
-{
-       uint32_t status, size;
-
-       if (paddr == 0 || len == 0) {
-               status = 0;
-               size = 0;
-               --sc->tx.queued;
-       } else {
-               status = TX_DESC_CTL;
-               size = flags | len;
-               ++sc->tx.queued;
-       }
-
-       sc->tx.desc_ring[index].addr = htole32((uint32_t)paddr);
-       sc->tx.desc_ring[index].size = htole32(size);
-       sc->tx.desc_ring[index].status = htole32(status);
-}
-
 static int
 awg_setup_txbuf(struct awg_softc *sc, int index, struct mbuf **mp)
 {
        bus_dma_segment_t segs[TX_MAX_SEGS];
-       int error, nsegs, cur, i, flags;
+       int error, nsegs, cur, first, i;
        u_int csum_flags;
+       uint32_t flags, status;
        struct mbuf *m;
 
+       cur = first = index;
+
        m = *mp;
        error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag,
            sc->tx.buf_map[index].map, m, segs, &nsegs, BUS_DMA_NOWAIT);
@@ -438,6 +420,7 @@ awg_setup_txbuf(struct awg_softc *sc, int index, struc
            BUS_DMASYNC_PREWRITE);
 
        flags = TX_FIR_DESC;
+       status = 0;
        if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) {
                if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0)
                        csum_flags = TX_CHECKSUM_CTL_FULL;
@@ -446,8 +429,7 @@ awg_setup_txbuf(struct awg_softc *sc, int index, struc
                flags |= (csum_flags << TX_CHECKSUM_CTL_SHIFT);
        }
 
-       for (cur = index, i = 0; i < nsegs; i++) {
-               sc->tx.buf_map[cur].mbuf = (i == 0 ? m : NULL);
+       for (i = 0; i < nsegs; i++) {
                sc->tx.segs++;
                if (i == nsegs - 1) {
                        flags |= TX_LAST_DESC;
@@ -460,16 +442,51 @@ awg_setup_txbuf(struct awg_softc *sc, int index, struc
                                flags |= TX_INT_CTL;
                        }
                }
-               awg_setup_txdesc(sc, cur, flags, segs[i].ds_addr,
-                   segs[i].ds_len);
+
+               sc->tx.desc_ring[cur].addr = htole32((uint32_t)segs[i].ds_addr);
+               sc->tx.desc_ring[cur].size = htole32(flags | segs[i].ds_len);
+               sc->tx.desc_ring[cur].status = htole32(status);
+
                flags &= ~TX_FIR_DESC;
+               /*
+                * Setting of the valid bit in the first descriptor is
+                * deferred until the whole chain is fully set up.
+                */
+               status = TX_DESC_CTL;
+
+               ++sc->tx.queued;
                cur = TX_NEXT(cur);
        }
 
+       sc->tx.buf_map[first].mbuf = m;
+
+       /*
+        * The whole mbuf chain has been DMA mapped,
+        * fix the first descriptor.
+        */
+       sc->tx.desc_ring[first].status = htole32(TX_DESC_CTL);
+
        return (nsegs);
 }
 
 static void
+awg_clean_txbuf(struct awg_softc *sc, int index)
+{
+       struct awg_bufmap *bmap;
+
+       --sc->tx.queued;
+
+       bmap = &sc->tx.buf_map[index];
+       if (bmap->mbuf != NULL) {
+               bus_dmamap_sync(sc->tx.buf_tag, bmap->map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(sc->tx.buf_tag, bmap->map);
+               m_freem(bmap->mbuf);
+               bmap->mbuf = NULL;
+       }
+}
+
+static void
 awg_setup_rxdesc(struct awg_softc *sc, int index, bus_addr_t paddr)
 {
        uint32_t status, size;
@@ -891,7 +908,6 @@ awg_rxintr(struct awg_softc *sc)
 static void
 awg_txintr(struct awg_softc *sc)
 {
-       struct awg_bufmap *bmap;
        struct emac_desc *desc;
        uint32_t status;
        if_t ifp;
@@ -908,23 +924,12 @@ awg_txintr(struct awg_softc *sc)
                status = le32toh(desc->status);
                if ((status & TX_DESC_CTL) != 0)
                        break;
-               bmap = &sc->tx.buf_map[i];
-               if (bmap->mbuf != NULL) {
-                       bus_dmamap_sync(sc->tx.buf_tag, bmap->map,
-                           BUS_DMASYNC_POSTWRITE);
-                       bus_dmamap_unload(sc->tx.buf_tag, bmap->map);
-                       m_freem(bmap->mbuf);
-                       bmap->mbuf = NULL;
-               }
-               awg_setup_txdesc(sc, i, 0, 0, 0);
+               awg_clean_txbuf(sc, i);
                if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
                if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
        }
 
        sc->tx.next = i;
-
-       bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map,
-           BUS_DMASYNC_PREWRITE);
 }
 
 static void
@@ -1519,7 +1524,7 @@ awg_setup_dma(device_t dev)
                return (error);
        }
 
-       sc->tx.queued = TX_DESC_COUNT;
+       sc->tx.queued = 0;
        for (i = 0; i < TX_DESC_COUNT; i++) {
                error = bus_dmamap_create(sc->tx.buf_tag, 0,
                    &sc->tx.buf_map[i].map);
@@ -1527,7 +1532,6 @@ awg_setup_dma(device_t dev)
                        device_printf(dev, "cannot create TX buffer map\n");
                        return (error);
                }
-               awg_setup_txdesc(sc, i, 0, 0, 0);
        }
 
        /* Setup RX ring */
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to