Index: sys/dev/dwc/if_dwc.c =================================================================== --- sys/dev/dwc/if_dwc.c +++ sys/dev/dwc/if_dwc.c @@ -627,7 +627,7 @@ inline static void dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, - uint32_t len, uint32_t flags) + uint32_t len, uint32_t flags, bool first, bool last) { uint32_t desc0, desc1; @@ -635,56 +635,73 @@ if (paddr == 0 || len == 0) { desc0 = 0; desc1 = 0; - --sc->txcount; + --sc->tx_desccount; } else { if (sc->mactype != DWC_GMAC_EXT_DESC) { desc0 = 0; - desc1 = NTDESC1_TCH | NTDESC1_FS | NTDESC1_LS | - NTDESC1_IC | len | flags; + desc1 = NTDESC1_TCH | len | flags; + if (first) + desc1 |= NTDESC1_FS; + if (last) + desc1 |= NTDESC1_LS | NTDESC1_IC; } else { - desc0 = ETDESC0_TCH | ETDESC0_FS | ETDESC0_LS | - ETDESC0_IC | flags; + desc0 = ETDESC0_TCH | flags; + if (first) + desc0 |= ETDESC0_FS; + if (last) + desc0 |= ETDESC0_LS | ETDESC0_IC; desc1 = len; } - ++sc->txcount; + ++sc->tx_desccount; } sc->txdesc_ring[idx].addr1 = (uint32_t)(paddr); sc->txdesc_ring[idx].desc0 = desc0; sc->txdesc_ring[idx].desc1 = desc1; +} - if (paddr && len) { - wmb(); - sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; - wmb(); - } +inline static void +dwc_set_owner(struct dwc_softc *sc, int idx) +{ + wmb(); + sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; + wmb(); } static int dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) { - struct bus_dma_segment seg; + struct bus_dma_segment segs[TX_MAP_MAX_SEGS]; int error, nsegs; struct mbuf * m; uint32_t flags = 0; + int i; + int first, last; - if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) + error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, + *mp, segs, &nsegs, 0); + if (error == EFBIG) { + /* + * The map may be partially mapped from the first call. + * Make sure to reset it. + */ + bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); + if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) + return (ENOMEM); + *mp = m; + error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, + *mp, segs, &nsegs, 0); + } + if (error != 0) return (ENOMEM); - *mp = m; - error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, - m, &seg, &nsegs, 0); - if (error != 0) { + if (sc->tx_desccount + nsegs > TX_DESC_COUNT) { + bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); return (ENOMEM); } - KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); + m = *mp; - bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, - BUS_DMASYNC_PREWRITE); - - sc->txbuf_map[idx].mbuf = m; - if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) { if (sc->mactype != DWC_GMAC_EXT_DESC) @@ -699,8 +716,28 @@ } } - dwc_setup_txdesc(sc, idx, seg.ds_addr, seg.ds_len, flags); + bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, + BUS_DMASYNC_PREWRITE); + sc->txbuf_map[idx].mbuf = m; + + first = sc->tx_desc_head; + for (i = 0; i < nsegs; i++) { + dwc_setup_txdesc(sc, sc->tx_desc_head, + segs[i].ds_addr, segs[i].ds_len, + (i == 0) ? flags : 0, /* only first desc needs flags */ + (i == 0), + (i == nsegs - 1)); + if (i > 0) + dwc_set_owner(sc, sc->tx_desc_head); + last = sc->tx_desc_head; + sc->tx_desc_head = next_txidx(sc, sc->tx_desc_head); + } + + sc->txbuf_map[idx].last_desc_idx = last; + + dwc_set_owner(sc, first); + return (0); } @@ -900,7 +937,8 @@ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ - MCLBYTES, 1, /* maxsize, nsegments */ + MCLBYTES*TX_MAP_MAX_SEGS, /* maxsize */ + TX_MAP_MAX_SEGS, /* nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ @@ -911,7 +949,7 @@ goto out; } - for (idx = 0; idx < TX_DESC_COUNT; idx++) { + for (idx = 0; idx < TX_MAP_COUNT; idx++) { error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, &sc->txbuf_map[idx].map); if (error != 0) { @@ -919,9 +957,11 @@ "could not create TX buffer DMA map.\n"); goto out; } - dwc_setup_txdesc(sc, idx, 0, 0, 0); } + for (idx = 0; idx < TX_DESC_COUNT; idx++) + dwc_setup_txdesc(sc, idx, 0, 0, 0, false, false); + /* * Set up RX descriptor ring, descriptors, dma maps, and mbufs. */ @@ -1029,20 +1069,27 @@ enqueued = 0; for (;;) { - if (sc->txcount == (TX_DESC_COUNT - 1)) { + if (sc->tx_desccount > (TX_DESC_COUNT - TX_MAP_MAX_SEGS + 1)) { if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); break; } + if (sc->tx_mapcount == (TX_MAP_COUNT - 1)) { + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); + break; + } + m = if_dequeue(ifp); if (m == NULL) break; - if (dwc_setup_txbuf(sc, sc->tx_idx_head, &m) != 0) { + if (dwc_setup_txbuf(sc, sc->tx_map_head, &m) != 0) { if_sendq_prepend(ifp, m); + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); break; } if_bpfmtap(ifp, m); - sc->tx_idx_head = next_txidx(sc, sc->tx_idx_head); + sc->tx_map_head = next_txidx(sc, sc->tx_map_head); + sc->tx_mapcount++; ++enqueued; } @@ -1193,28 +1240,46 @@ struct dwc_bufmap *bmap; struct dwc_hwdesc *desc; struct ifnet *ifp; + int idx, last_idx; + bool map_finished; DWC_ASSERT_LOCKED(sc); ifp = sc->ifp; - while (sc->tx_idx_tail != sc->tx_idx_head) { - desc = &sc->txdesc_ring[sc->tx_idx_tail]; - if ((desc->desc0 & TDESC0_OWN) != 0) + /* check if all descriptors of the map are done */ + while (sc->tx_map_tail != sc->tx_map_head) { + map_finished = true; + bmap = &sc->txbuf_map[sc->tx_map_tail]; + idx = sc->tx_desc_tail; + last_idx = next_txidx(sc, bmap->last_desc_idx); + while (idx != last_idx) { + desc = &sc->txdesc_ring[idx]; + if ((desc->desc0 & TDESC0_OWN) != 0) { + map_finished = false; + break; + } + idx = next_txidx(sc, idx); + } + + if (!map_finished) break; - bmap = &sc->txbuf_map[sc->tx_idx_tail]; bus_dmamap_sync(sc->txbuf_tag, bmap->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->txbuf_tag, bmap->map); m_freem(bmap->mbuf); bmap->mbuf = NULL; - dwc_setup_txdesc(sc, sc->tx_idx_tail, 0, 0, 0); - sc->tx_idx_tail = next_txidx(sc, sc->tx_idx_tail); + sc->tx_mapcount--; + while (sc->tx_desc_tail != last_idx) { + dwc_setup_txdesc(sc, sc->tx_desc_tail, 0, 0, 0, false, false); + sc->tx_desc_tail = next_txidx(sc, sc->tx_desc_tail); + } + sc->tx_map_tail = next_txidx(sc, sc->tx_map_tail); if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } /* If there are no buffers outstanding, muzzle the watchdog. */ - if (sc->tx_idx_tail == sc->tx_idx_head) { + if (sc->tx_desc_tail == sc->tx_desc_head) { sc->tx_watchdog_count = 0; } } @@ -1503,7 +1568,8 @@ sc = device_get_softc(dev); sc->dev = dev; sc->rx_idx = 0; - sc->txcount = TX_DESC_COUNT; + sc->tx_desccount = TX_DESC_COUNT; + sc->tx_mapcount = 0; sc->mii_clk = IF_DWC_MII_CLK(dev); sc->mactype = IF_DWC_MAC_TYPE(dev); @@ -1610,7 +1676,7 @@ if_setstartfn(ifp, dwc_txstart); if_setioctlfn(ifp, dwc_ioctl); if_setinitfn(ifp, dwc_init); - if_setsendqlen(ifp, TX_DESC_COUNT - 1); + if_setsendqlen(ifp, TX_MAP_COUNT - 1); if_setsendqready(sc->ifp); if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); Index: sys/dev/dwc/if_dwcvar.h =================================================================== --- sys/dev/dwc/if_dwcvar.h +++ sys/dev/dwc/if_dwcvar.h @@ -47,11 +47,15 @@ #define RX_DESC_COUNT 1024 #define RX_DESC_SIZE (sizeof(struct dwc_hwdesc) * RX_DESC_COUNT) #define TX_DESC_COUNT 1024 +#define TX_MAP_COUNT TX_DESC_COUNT #define TX_DESC_SIZE (sizeof(struct dwc_hwdesc) * TX_DESC_COUNT) +#define TX_MAP_MAX_SEGS 32 struct dwc_bufmap { bus_dmamap_t map; struct mbuf *mbuf; + /* Only used for TX descirptors */ + int last_desc_idx; }; struct dwc_softc { @@ -89,9 +93,12 @@ bus_addr_t txdesc_ring_paddr; bus_dma_tag_t txbuf_tag; struct dwc_bufmap txbuf_map[TX_DESC_COUNT]; - uint32_t tx_idx_head; - uint32_t tx_idx_tail; - int txcount; + uint32_t tx_desc_head; + uint32_t tx_desc_tail; + uint32_t tx_map_head; + uint32_t tx_map_tail; + int tx_desccount; + int tx_mapcount; }; #endif /* __IF_DWCVAR_H__ */