Date: Sat, 18 Nov 2017 20:42:48 +0000 (UTC) From: Emmanuel Vadot <manu@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r325978 - head/sys/arm/allwinner Message-ID: <201711182042.vAIKgmhS021575@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
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 <guyyur@gmail.com> 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 */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201711182042.vAIKgmhS021575>