diff options
author | yongari <yongari@FreeBSD.org> | 2006-01-03 06:14:07 +0000 |
---|---|---|
committer | yongari <yongari@FreeBSD.org> | 2006-01-03 06:14:07 +0000 |
commit | d2cea5305fb9934620b0b2ddaccb11e6d59f8462 (patch) | |
tree | c77b81746e8a610fb9c8fb2379a3a69489f2371c /sys/dev/ti | |
parent | be8db3dd12d8def6ec9dd7b61f9888340e2322ce (diff) | |
download | FreeBSD-src-d2cea5305fb9934620b0b2ddaccb11e6d59f8462.zip FreeBSD-src-d2cea5305fb9934620b0b2ddaccb11e6d59f8462.tar.gz |
- Tx side bus_dmamap_load_mbuf_sg(9) support. This reduces bookkeeping
requiried to keep consistent softc state before/after callback function
invocation and supposed to be sligntly faster than previous one as it
wouldn't incur callback overhead. With this change callback function
was gone.
- Decrease TI_MAXTXSEGS to 32 from 128. It seems that most mbuf chain
length is less than 32 and it would be re-packed with m_defrag(9) if
its chain length is larger than TI_MAXTXSEGS. This would protect ti(4)
against possible kernel stack overflow when txsegs[] is put on stack.
Alternatively, we can embed the txsegs[] into softc. However, that
would waste memory and make Tx/Rx speration hard when we want to
sperate Tx/Rx handlers to optimize locking.
- Fix dma map tracking used in Tx path. Previously it used the dma map
of the last mbuf chain in ti_txeof() which was incorrect as ti(4)
used dma map of the first mbuf chain when it loads a mbuf chain with
bus_dmamap_load_mbuf(9). Correct the bug by introducing queues that
keep track of active/inactive dma maps/mbuf chain.
- Use ti_txcnt to check whether driver need to set watchdog timer instead
of blidnly clearing the timer in ti_txeof().
- Remove the 3rd arg. of ti_encap(). Since ti(4) now caches the last
descriptor index(ti_tx_saved_prodidx) used in Tx there is no need to
pass it as a fuction arg.
- Change data type of producer/consumer index to int from u_int16_t in
order to remove implicit type conversions in Tx/Rx handlers.
- Check interface queue before getting a mbuf chain to reduce locking
overhead.
- Check number of available Tx descriptores to be 16 or higher in
ti_start(). This wouldn't protect Tx descriptor shortage but it would
reduce number of bus_dmamap_unload(9) calls in ti_encap() when we are
about to running out of Tx descriptors.
- Command NIC to send packets ony when the driver really has packets
enqueued. Previously it always set TI_MB_SENDPROD_IDX which would
command NIC to DMA Tx descriptors into NIC local memory regardless
of Tx descriptor changes.
Reviewed by: scottl
Diffstat (limited to 'sys/dev/ti')
-rw-r--r-- | sys/dev/ti/if_ti.c | 294 | ||||
-rw-r--r-- | sys/dev/ti/if_tireg.h | 31 |
2 files changed, 157 insertions, 168 deletions
diff --git a/sys/dev/ti/if_ti.c b/sys/dev/ti/if_ti.c index 9fbe805..b603103 100644 --- a/sys/dev/ti/if_ti.c +++ b/sys/dev/ti/if_ti.c @@ -184,9 +184,7 @@ static void ti_txeof(struct ti_softc *); static void ti_rxeof(struct ti_softc *); static void ti_stats_update(struct ti_softc *); -static int ti_encap(struct ti_softc *, struct mbuf *, u_int32_t *); -static void ti_encap_cb(void *arg, bus_dma_segment_t *segs, int nseg, - bus_size_t mapsize, int error); +static int ti_encap(struct ti_softc *, struct mbuf **); static void ti_intr(void *); static void ti_start(struct ifnet *); @@ -908,7 +906,7 @@ ti_cmd(sc, cmd) struct ti_softc *sc; struct ti_cmd_desc *cmd; { - u_int32_t index; + int index; index = sc->ti_cmd_saved_prodidx; CSR_WRITE_4(sc, TI_GCR_CMDRING + (index * 4), *(u_int32_t *)(cmd)); @@ -928,7 +926,7 @@ ti_cmd_ext(sc, cmd, arg, len) caddr_t arg; int len; { - u_int32_t index; + int index; int i; index = sc->ti_cmd_saved_prodidx; @@ -1002,8 +1000,10 @@ ti_alloc_dmamaps(struct ti_softc *sc) int i; for (i = 0; i < TI_TX_RING_CNT; i++) { + sc->ti_cdata.ti_txdesc[i].tx_m = NULL; + sc->ti_cdata.ti_txdesc[i].tx_dmamap = 0; if (bus_dmamap_create(sc->ti_mbuftx_dmat, 0, - &sc->ti_cdata.ti_tx_maps[i])) + &sc->ti_cdata.ti_txdesc[i].tx_dmamap)) return (ENOBUFS); } for (i = 0; i < TI_STD_RX_RING_CNT; i++) { @@ -1033,10 +1033,10 @@ ti_free_dmamaps(struct ti_softc *sc) if (sc->ti_mbuftx_dmat) for (i = 0; i < TI_TX_RING_CNT; i++) - if (sc->ti_cdata.ti_tx_maps[i]) { + if (sc->ti_cdata.ti_txdesc[i].tx_dmamap) { bus_dmamap_destroy(sc->ti_mbuftx_dmat, - sc->ti_cdata.ti_tx_maps[i]); - sc->ti_cdata.ti_tx_maps[i] = 0; + sc->ti_cdata.ti_txdesc[i].tx_dmamap); + sc->ti_cdata.ti_txdesc[i].tx_dmamap = 0; } if (sc->ti_mbufrx_dmat) @@ -1701,20 +1701,20 @@ static void ti_free_tx_ring(sc) struct ti_softc *sc; { - bus_dmamap_t map; + struct ti_txdesc *txd; int i; if (sc->ti_rdata->ti_tx_ring == NULL) return; for (i = 0; i < TI_TX_RING_CNT; i++) { - if (sc->ti_cdata.ti_tx_chain[i] != NULL) { - map = sc->ti_cdata.ti_tx_maps[i]; - bus_dmamap_sync(sc->ti_mbuftx_dmat, map, + txd = &sc->ti_cdata.ti_txdesc[i]; + if (txd->tx_m != NULL) { + bus_dmamap_sync(sc->ti_mbuftx_dmat, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->ti_mbuftx_dmat, map); - m_freem(sc->ti_cdata.ti_tx_chain[i]); - sc->ti_cdata.ti_tx_chain[i] = NULL; + bus_dmamap_unload(sc->ti_mbuftx_dmat, txd->tx_dmamap); + m_freem(txd->tx_m); + txd->tx_m = NULL; } bzero((char *)&sc->ti_rdata->ti_tx_ring[i], sizeof(struct ti_tx_desc)); @@ -1725,6 +1725,15 @@ static int ti_init_tx_ring(sc) struct ti_softc *sc; { + struct ti_txdesc *txd; + int i; + + STAILQ_INIT(&sc->ti_cdata.ti_txfreeq); + STAILQ_INIT(&sc->ti_cdata.ti_txbusyq); + for (i = 0; i < TI_TX_RING_CNT; i++) { + txd = &sc->ti_cdata.ti_txdesc[i]; + STAILQ_INSERT_TAIL(&sc->ti_cdata.ti_txfreeq, txd, tx_q); + } sc->ti_txcnt = 0; sc->ti_tx_saved_considx = 0; sc->ti_tx_saved_prodidx = 0; @@ -2826,44 +2835,47 @@ static void ti_txeof(sc) struct ti_softc *sc; { + struct ti_txdesc *txd; + struct ti_tx_desc txdesc; struct ti_tx_desc *cur_tx = NULL; struct ifnet *ifp; - bus_dmamap_t map; + int idx; ifp = sc->ti_ifp; + txd = STAILQ_FIRST(&sc->ti_cdata.ti_txbusyq); + if (txd == NULL) + return; /* * Go through our tx ring and free mbufs for those * frames that have been sent. */ - while (sc->ti_tx_saved_considx != sc->ti_tx_considx.ti_idx) { - u_int32_t idx = 0; - struct ti_tx_desc txdesc; - - idx = sc->ti_tx_saved_considx; + for (idx = sc->ti_tx_saved_considx; idx != sc->ti_tx_considx.ti_idx; + TI_INC(idx, TI_TX_RING_CNT)) { if (sc->ti_hwrev == TI_HWREV_TIGON) { ti_mem_read(sc, TI_TX_RING_BASE + idx * sizeof(txdesc), sizeof(txdesc), &txdesc); cur_tx = &txdesc; } else cur_tx = &sc->ti_rdata->ti_tx_ring[idx]; - if (cur_tx->ti_flags & TI_BDFLAG_END) - ifp->if_opackets++; - if (sc->ti_cdata.ti_tx_chain[idx] != NULL) { - m_freem(sc->ti_cdata.ti_tx_chain[idx]); - sc->ti_cdata.ti_tx_chain[idx] = NULL; - map = sc->ti_cdata.ti_tx_maps[idx]; - bus_dmamap_sync(sc->ti_mbuftx_dmat, map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->ti_mbuftx_dmat, map); - } sc->ti_txcnt--; - TI_INC(sc->ti_tx_saved_considx, TI_TX_RING_CNT); - ifp->if_timer = 0; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + if ((cur_tx->ti_flags & TI_BDFLAG_END) == 0) + continue; + bus_dmamap_sync(sc->ti_mbuftx_dmat, txd->tx_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->ti_mbuftx_dmat, txd->tx_dmamap); + + ifp->if_opackets++; + m_freem(txd->tx_m); + txd->tx_m = NULL; + STAILQ_REMOVE_HEAD(&sc->ti_cdata.ti_txbusyq, tx_q); + STAILQ_INSERT_TAIL(&sc->ti_cdata.ti_txfreeq, txd, tx_q); + txd = STAILQ_FIRST(&sc->ti_cdata.ti_txbusyq); } + sc->ti_tx_saved_considx = idx; - if (cur_tx != NULL) - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_timer = sc->ti_txcnt > 0 ? 5 : 0; } static void @@ -2931,68 +2943,86 @@ ti_stats_update(sc) BUS_DMASYNC_PREREAD); } -struct ti_dmamap_arg { - struct ti_softc *sc; - struct m_tag *mtag; - struct mbuf *m_head; - u_int16_t csum_flags; - int idx; - int error; -}; - -static void -ti_encap_cb(arg, segs, nseg, mapsize, error) - void *arg; - bus_dma_segment_t *segs; - int nseg; - bus_size_t mapsize; - int error; -{ +/* + * Encapsulate an mbuf chain in the tx ring by coupling the mbuf data + * pointers to descriptors. + */ +static int +ti_encap(sc, m_head) struct ti_softc *sc; - struct ti_dmamap_arg *ctx; - struct ti_tx_desc *f = NULL; + struct mbuf **m_head; +{ + struct ti_txdesc *txd; + struct ti_tx_desc *f; struct ti_tx_desc txdesc; + struct mbuf *m, *n; struct m_tag *mtag; - u_int32_t frag, cur, cnt = 0; + bus_dma_segment_t txsegs[TI_MAXTXSEGS]; u_int16_t csum_flags; + int error, frag, i, nseg; - if (error) - return; + if ((txd = STAILQ_FIRST(&sc->ti_cdata.ti_txfreeq)) == NULL) + return (ENOBUFS); - ctx = (struct ti_dmamap_arg *)arg; - sc = ctx->sc; - cur = frag = ctx->idx; - mtag = ctx->mtag; - csum_flags = ctx->csum_flags; + m = *m_head; + csum_flags = 0; + if (m->m_pkthdr.csum_flags) { + if (m->m_pkthdr.csum_flags & CSUM_IP) + csum_flags |= TI_BDFLAG_IP_CKSUM; + if (m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) + csum_flags |= TI_BDFLAG_TCP_UDP_CKSUM; + if (m->m_flags & M_LASTFRAG) + csum_flags |= TI_BDFLAG_IP_FRAG_END; + else if (m->m_flags & M_FRAG) + csum_flags |= TI_BDFLAG_IP_FRAG; + } - /* - * Sanity check: avoid coming within 16 descriptors - * of the end of the ring. - */ - if ((TI_TX_RING_CNT - (sc->ti_txcnt + nseg)) < 16) { - ctx->error = ENOBUFS; - return; + error = bus_dmamap_load_mbuf_sg(sc->ti_mbuftx_dmat, txd->tx_dmamap, + m, txsegs, &nseg, 0); + if (error == EFBIG) { + n = m_defrag(m, M_DONTWAIT); + if (n == NULL) { + m_freem(m); + m = NULL; + return (ENOMEM); + } + m = n; + error = bus_dmamap_load_mbuf_sg(sc->ti_mbuftx_dmat, + txd->tx_dmamap, m, txsegs, &nseg, 0); + if (error) { + m_freem(m); + m = NULL; + return (error); + } + } else if (error != 0) + return (error); + if (nseg == 0) { + m_freem(m); + m = NULL; + return (EIO); } - /* - * Start packing the mbufs in this chain into - * the fragment pointers. Stop when we run out - * of fragments or hit the end of the mbuf chain. - */ - while (nseg-- > 0) { + if (sc->ti_txcnt + nseg >= TI_TX_RING_CNT) { + bus_dmamap_unload(sc->ti_mbuftx_dmat, txd->tx_dmamap); + return (ENOBUFS); + } + + bus_dmamap_sync(sc->ti_mbuftx_dmat, txd->tx_dmamap, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->ti_rdata_dmat, sc->ti_rdata_dmamap, + BUS_DMASYNC_PREWRITE); + + mtag = VLAN_OUTPUT_TAG(sc->ti_ifp, m); + frag = sc->ti_tx_saved_prodidx; + for (i = 0; i < nseg; i++) { if (sc->ti_hwrev == TI_HWREV_TIGON) { bzero(&txdesc, sizeof(txdesc)); f = &txdesc; } else f = &sc->ti_rdata->ti_tx_ring[frag]; - if (sc->ti_cdata.ti_tx_chain[frag] != NULL) { - ctx->error = ENOBUFS; - return; - } - ti_hostaddr64(&f->ti_addr, segs[cnt].ds_addr); - f->ti_len = segs[cnt].ds_len; + ti_hostaddr64(&f->ti_addr, txsegs[i].ds_addr); + f->ti_len = txsegs[i].ds_len; f->ti_flags = csum_flags; - if (mtag != NULL) { f->ti_flags |= TI_BDFLAG_VLAN_TAG; f->ti_vlan_tag = VLAN_TAG_VALUE(mtag) & 0xfff; @@ -3003,77 +3033,24 @@ ti_encap_cb(arg, segs, nseg, mapsize, error) if (sc->ti_hwrev == TI_HWREV_TIGON) ti_mem_write(sc, TI_TX_RING_BASE + frag * sizeof(txdesc), sizeof(txdesc), &txdesc); - cur = frag; TI_INC(frag, TI_TX_RING_CNT); - cnt++; } + sc->ti_tx_saved_prodidx = frag; + /* set TI_BDFLAG_END on the last descriptor */ + frag = (frag + TI_TX_RING_CNT - 1) % TI_TX_RING_CNT; if (sc->ti_hwrev == TI_HWREV_TIGON) { txdesc.ti_flags |= TI_BDFLAG_END; - ti_mem_write(sc, TI_TX_RING_BASE + cur * sizeof(txdesc), + ti_mem_write(sc, TI_TX_RING_BASE + frag * sizeof(txdesc), sizeof(txdesc), &txdesc); } else - sc->ti_rdata->ti_tx_ring[cur].ti_flags |= TI_BDFLAG_END; - sc->ti_cdata.ti_tx_chain[cur] = ctx->m_head; - sc->ti_txcnt += cnt; - - ctx->idx = frag; - ctx->error = 0; -} + sc->ti_rdata->ti_tx_ring[frag].ti_flags |= TI_BDFLAG_END; -/* - * Encapsulate an mbuf chain in the tx ring by coupling the mbuf data - * pointers to descriptors. - */ -static int -ti_encap(sc, m_head, txidx) - struct ti_softc *sc; - struct mbuf *m_head; - u_int32_t *txidx; -{ - bus_dmamap_t map; - struct ti_dmamap_arg ctx; - u_int32_t frag, cnt; - u_int16_t csum_flags = 0; - int error; - - frag = *txidx; + STAILQ_REMOVE_HEAD(&sc->ti_cdata.ti_txfreeq, tx_q); + STAILQ_INSERT_TAIL(&sc->ti_cdata.ti_txbusyq, txd, tx_q); + txd->tx_m = m; + sc->ti_txcnt += nseg; - if (m_head->m_pkthdr.csum_flags) { - if (m_head->m_pkthdr.csum_flags & CSUM_IP) - csum_flags |= TI_BDFLAG_IP_CKSUM; - if (m_head->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) - csum_flags |= TI_BDFLAG_TCP_UDP_CKSUM; - if (m_head->m_flags & M_LASTFRAG) - csum_flags |= TI_BDFLAG_IP_FRAG_END; - else if (m_head->m_flags & M_FRAG) - csum_flags |= TI_BDFLAG_IP_FRAG; - } - - ctx.sc = sc; - ctx.idx = frag; - ctx.csum_flags = csum_flags; - ctx.mtag = VLAN_OUTPUT_TAG(sc->ti_ifp, m_head); - ctx.m_head = m_head; - - map = sc->ti_cdata.ti_tx_maps[frag]; - error = bus_dmamap_load_mbuf(sc->ti_mbuftx_dmat, map, m_head, - ti_encap_cb, &ctx, 0); - if (error) - return (ENOBUFS); - - cnt = ctx.idx - frag; - frag = ctx.idx; - - if ((ctx.error != 0) || (frag == sc->ti_tx_saved_considx)) { - bus_dmamap_unload(sc->ti_mbuftx_dmat, map); - return (ENOBUFS); - } - - bus_dmamap_sync(sc->ti_mbuftx_dmat, map, BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(sc->ti_rdata_dmat, sc->ti_rdata_dmamap, - BUS_DMASYNC_PREWRITE); - *txidx = frag; return (0); } @@ -3099,13 +3076,12 @@ ti_start_locked(ifp) { struct ti_softc *sc; struct mbuf *m_head = NULL; - u_int32_t prodidx = 0; + int enq = 0; sc = ifp->if_softc; - prodidx = sc->ti_tx_saved_prodidx; - - while (sc->ti_cdata.ti_tx_chain[prodidx] == NULL) { + for (; ifp->if_snd.ifq_head != NULL && + sc->ti_txcnt < (TI_TX_RING_CNT - 16);) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; @@ -3133,12 +3109,15 @@ ti_start_locked(ifp) * don't have room, set the OACTIVE flag and wait * for the NIC to drain the ring. */ - if (ti_encap(sc, m_head, &prodidx)) { + if (ti_encap(sc, &m_head)) { + if (m_head == NULL) + break; IF_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } + enq++; /* * If there's a BPF listener, bounce a copy of this frame * to him. @@ -3146,14 +3125,15 @@ ti_start_locked(ifp) BPF_MTAP(ifp, m_head); } - /* Transmit */ - sc->ti_tx_saved_prodidx = prodidx; - CSR_WRITE_4(sc, TI_MB_SENDPROD_IDX, prodidx); + if (enq > 0) { + /* Transmit */ + CSR_WRITE_4(sc, TI_MB_SENDPROD_IDX, sc->ti_tx_saved_prodidx); - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + } } static void diff --git a/sys/dev/ti/if_tireg.h b/sys/dev/ti/if_tireg.h index 97f7077..5f0bda9 100644 --- a/sys/dev/ti/if_tireg.h +++ b/sys/dev/ti/if_tireg.h @@ -399,7 +399,7 @@ #define TI_MINI_RX_RING_CNT 1024 #define TI_RETURN_RING_CNT 2048 -#define TI_MAXTXSEGS 128 +#define TI_MAXTXSEGS 32 /* * Possible TX ring sizes. @@ -904,6 +904,14 @@ struct ti_event_desc { #define TI_RESID (TI_JPAGESZ - (TI_JLEN * TI_JSLOTS) % TI_JPAGESZ) #define TI_JMEM ((TI_JLEN * TI_JSLOTS) + TI_RESID) +struct ti_txdesc { + struct mbuf *tx_m; + bus_dmamap_t tx_dmamap; + STAILQ_ENTRY(ti_txdesc) tx_q; +}; + +STAILQ_HEAD(ti_txdq, ti_txdesc); + /* * Ring structures. Most of these reside in host memory and we tell * the NIC where they are via the ring control blocks. The exceptions @@ -942,8 +950,9 @@ struct ti_ring_data { * not the other way around. */ struct ti_chain_data { - struct mbuf *ti_tx_chain[TI_TX_RING_CNT]; - bus_dmamap_t ti_tx_maps[TI_TX_RING_CNT]; + struct ti_txdesc ti_txdesc[TI_TX_RING_CNT]; + struct ti_txdq ti_txfreeq; + struct ti_txdq ti_txbusyq; struct mbuf *ti_rx_std_chain[TI_STD_RX_RING_CNT]; bus_dmamap_t ti_rx_std_maps[TI_STD_RX_RING_CNT]; struct mbuf *ti_rx_jumbo_chain[TI_JUMBO_RX_RING_CNT]; @@ -1009,14 +1018,14 @@ struct ti_softc { #define ti_ev_prodidx ti_rdata->ti_ev_prodidx_r #define ti_return_prodidx ti_rdata->ti_return_prodidx_r #define ti_tx_considx ti_rdata->ti_tx_considx_r - u_int16_t ti_tx_saved_prodidx; - u_int16_t ti_tx_saved_considx; - u_int16_t ti_rx_saved_considx; - u_int16_t ti_ev_saved_considx; - u_int16_t ti_cmd_saved_prodidx; - u_int16_t ti_std; /* current std ring head */ - u_int16_t ti_mini; /* current mini ring head */ - u_int16_t ti_jumbo; /* current jumo ring head */ + int ti_tx_saved_prodidx; + int ti_tx_saved_considx; + int ti_rx_saved_considx; + int ti_ev_saved_considx; + int ti_cmd_saved_prodidx; + int ti_std; /* current std ring head */ + int ti_mini; /* current mini ring head */ + int ti_jumbo; /* current jumo ring head */ SLIST_HEAD(__ti_mchead, ti_mc_entry) ti_mc_listhead; SLIST_HEAD(__ti_jfreehead, ti_jpool_entry) ti_jfree_listhead; SLIST_HEAD(__ti_jinusehead, ti_jpool_entry) ti_jinuse_listhead; |