Hi all, I've begun merging NetBSD's changes to vr(4) for bus_dma(9)-ification of the driver. However I have run into a few problems.
I have extracted diffs against HEAD (as of my last commit rev 1.89) of if_vr.c and if_vrreg.h of the merging work I've done so far which has been mostly by hand from NetBSD's rev 1.18, when bus_dma-ification was first introduced to vr(4) in their tree. The main problem here seems to be that NetBSD's version makes extensive use of their bus_dmamap_sync() API which accepts region (offset, length) arguments to be synchronized within the map. I'd appreciate any help from someone more familiar with the bus_dma(9) API than I in finishing off this work as I'm in a position to test (my local CVS mirror / main development machine uses vr(4) at the moment). There are some further whitespace/style changes in there which can probably be weeded out easily before the merge proper would happen in CVS. Faithfully, BMS
Index: if_vr.c =================================================================== RCS file: /home/ncvs/src/sys/pci/if_vr.c,v retrieving revision 1.89 diff -u -r1.89 if_vr.c --- if_vr.c 3 Jul 2004 02:59:02 -0000 1.89 +++ if_vr.c 3 Jul 2004 05:59:28 -0000 @@ -77,8 +77,8 @@ #include <net/bpf.h> -#include <vm/vm.h> /* for vtophys */ -#include <vm/pmap.h> /* for vtophys */ +#include <vm/vm.h> +#include <vm/pmap.h> #include <machine/bus_pio.h> #include <machine/bus_memio.h> #include <machine/bus.h> @@ -130,12 +130,7 @@ static int vr_attach (device_t); static int vr_detach (device_t); -static int vr_newbuf (struct vr_softc *, - struct vr_chain_onefrag *, - struct mbuf *); -static int vr_encap (struct vr_softc *, struct vr_chain *, - struct mbuf * ); - +static int vr_add_rxbuf (struct vr_softc *, int); static void vr_rxeof (struct vr_softc *); static void vr_rxeoc (struct vr_softc *); static void vr_txeof (struct vr_softc *); @@ -163,8 +158,6 @@ static void vr_setcfg (struct vr_softc *, int); static void vr_setmulti (struct vr_softc *); static void vr_reset (struct vr_softc *); -static int vr_list_rx_init (struct vr_softc *); -static int vr_list_tx_init (struct vr_softc *); #ifdef VR_USEIOSPACE #define VR_RES SYS_RES_IOPORT @@ -640,11 +633,11 @@ vr_attach(dev) device_t dev; { - int i; - u_char eaddr[ETHER_ADDR_LEN]; + u_char eaddr[ETHER_ADDR_LEN]; + bus_dma_segment_t seg; struct vr_softc *sc; struct ifnet *ifp; - int unit, error = 0, rid; + int error = 0, i, rid, rseg, unit; sc = device_get_softc(dev); unit = device_get_unit(dev); @@ -715,17 +708,6 @@ sc->vr_unit = unit; bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); - sc->vr_ldata = contigmalloc(sizeof(struct vr_list_data), M_DEVBUF, - M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); - - if (sc->vr_ldata == NULL) { - printf("vr%d: no memory for list buffers!\n", unit); - error = ENXIO; - goto fail; - } - - bzero(sc->vr_ldata, sizeof(struct vr_list_data)); - ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); @@ -742,6 +724,85 @@ #endif ifp->if_capenable = ifp->if_capabilities; + /* XXX needs bus_dmamap_create() */ + sc->vr_dmat = NULL; + /* goto fail_0; */ + + /* + * Allocate the control data structures, and create and load + * the DMA map for it. + */ + if ((error = bus_dmamem_alloc(sc->vr_dmat, + sizeof(struct vr_control_data), PAGE_SIZE, 0, &seg, 1, &rseg, + 0)) != 0) { + if_printf(ifp, "unable to allocate control data, error = %d\n", + error); + error = ENXIO; + goto fail_1; + } + + if ((error = bus_dmamem_map(sc->vr_dmat, &seg, rseg, + sizeof(struct vr_control_data), (caddr_t *)&sc->vr_control_data, + BUS_DMA_COHERENT)) != 0) { + if_printf(ifp, "unable to map control data, error = %d\n", + error); + goto fail_2; + } + + if ((error = bus_dmamap_create(sc->vr_dmat, + sizeof(struct vr_control_data), 1, + sizeof(struct vr_control_data), 0, 0, + &sc->vr_cddmamap)) != 0) { + if_printf(ifp, +"unable to create control data DMA map, error = %d\n", error); + goto fail_3; + } + + if ((error = bus_dmamap_load(sc->vr_dmat, sc->vr_cddmamap, + sc->vr_control_data, sizeof(struct vr_control_data), NULL, + 0)) != 0) { + if_printf(ifp, +"unable to load control data DMA map, error = %d\n", error); + goto fail_4; + } + + /* + * Create the transmit buffer DMA maps. + */ + for (i = 0; i < VR_NTXDESC; i++) { + if ((error = bus_dmamap_create(sc->vr_dmat, MCLBYTES, + 1, MCLBYTES, 0, 0, + &VR_DSTX(sc, i)->ds_dmamap)) != 0) { + if_printf(ifp, +"unable to create tx DMA map %d, error = %d\n", i, error); + goto fail_5; + } + } + + /* + * Create the receive buffer DMA maps. + */ + for (i = 0; i < VR_NRXDESC; i++) { + if ((error = bus_dmamap_create(sc->vr_dmat, MCLBYTES, 1, + MCLBYTES, 0, 0, + &VR_DSRX(sc, i)->ds_dmamap)) != 0) { + if_printf(ifp, +"unable to create rx DMA map %d, error = %d\n", i, error); + goto fail_6; + } + } + + /* + * Pre-allocate the receive buffers. + */ + for (i = 0; i < VR_NRXDESC; i++) { + if ((error = vr_add_rxbuf(sc, i)) != 0) { + if_printf(ifp, +"unable to allocate or map rx buffer %d, error = %d\n", i, error); + goto fail_7; + } + } + /* Do MII setup. */ if (mii_phy_probe(dev, &sc->vr_miibus, vr_ifmedia_upd, vr_ifmedia_sts)) { @@ -765,6 +826,39 @@ goto fail; } +fail_7: + for (i = 0; i < VR_NRXDESC; i++) { + if (sc->vr_rxsoft[i].ds_mbuf != NULL) { + bus_dmamap_unload(sc->vr_dmat, + sc->vr_rxsoft[i].ds_dmamap); + (void)m_freem(sc->vr_rxsoft[i].ds_mbuf); + } + } +fail_6: + for (i = 0; i < VR_NRXDESC; i++) { + if (sc->vr_rxsoft[i].ds_dmamap != NULL) + bus_dmamap_destroy(sc->vr_dmat, + sc->vr_rxsoft[i].ds_dmamap); + } +fail_5: + for (i = 0; i < VR_NTXDESC; i++) { + if (sc->vr_txsoft[i].ds_dmamap != NULL) + bus_dmamap_destroy(sc->vr_dmat, + sc->vr_txsoft[i].ds_dmamap); + } + bus_dmamap_unload(sc->vr_dmat, sc->vr_cddmamap); +fail_4: + bus_dmamap_destroy(sc->vr_dmat, sc->vr_cddmamap); +fail_3: + bus_dmamem_unmap(sc->vr_dmat, (caddr_t)sc->vr_control_data, + sizeof(struct vr_control_data)); +fail_2: + bus_dmamem_free(sc->vr_dmat, &seg, rseg); +fail_1: + /* XXX */ +fail_0: + /* XXX */ + fail: if (error) vr_detach(dev); @@ -815,73 +909,6 @@ } /* - * Initialize the transmit descriptors. - */ -static int -vr_list_tx_init(struct vr_softc *sc) -{ - struct vr_chain_data *cd; - struct vr_list_data *ld; - int i; - - cd = &sc->vr_cdata; - ld = sc->vr_ldata; - for (i = 0; i < VR_TX_LIST_CNT; i++) { - cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; - if (i == (VR_TX_LIST_CNT - 1)) - cd->vr_tx_chain[i].vr_nextdesc = - &cd->vr_tx_chain[0]; - else - cd->vr_tx_chain[i].vr_nextdesc = - &cd->vr_tx_chain[i + 1]; - } - cd->vr_tx_cons = cd->vr_tx_prod = &cd->vr_tx_chain[0]; - - return (0); -} - - -/* - * Initialize the RX descriptors and allocate mbufs for them. Note that - * we arrange the descriptors in a closed ring, so that the last descriptor - * points back to the first. - */ -static int -vr_list_rx_init(struct vr_softc *sc) -{ - struct vr_chain_data *cd; - struct vr_list_data *ld; - int i; - - VR_LOCK_ASSERT(sc); - - cd = &sc->vr_cdata; - ld = sc->vr_ldata; - - for (i = 0; i < VR_RX_LIST_CNT; i++) { - cd->vr_rx_chain[i].vr_ptr = - (struct vr_desc *)&ld->vr_rx_list[i]; - if (vr_newbuf(sc, &cd->vr_rx_chain[i], NULL) == ENOBUFS) - return (ENOBUFS); - if (i == (VR_RX_LIST_CNT - 1)) { - cd->vr_rx_chain[i].vr_nextdesc = - &cd->vr_rx_chain[0]; - ld->vr_rx_list[i].vr_next = - vtophys(&ld->vr_rx_list[0]); - } else { - cd->vr_rx_chain[i].vr_nextdesc = - &cd->vr_rx_chain[i + 1]; - ld->vr_rx_list[i].vr_next = - vtophys(&ld->vr_rx_list[i + 1]); - } - } - - cd->vr_rx_head = &cd->vr_rx_chain[0]; - - return (0); -} - -/* * Initialize an RX descriptor and attach an MBUF cluster. * Note: the length fields are only 11 bits wide, which means the * largest size we can specify is 2047. This is important because @@ -889,33 +916,36 @@ * overflow the field and make a mess. */ static int -vr_newbuf(struct vr_softc *sc, struct vr_chain_onefrag *c, struct mbuf *m) +vr_add_rxbuf(struct vr_softc *sc, int i) { - struct mbuf *m_new = NULL; + struct vr_descsoft *ds = VR_DSRX(sc, i); + struct mbuf *m_new; + int error; - if (m == NULL) { - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) - return (ENOBUFS); + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return (ENOBUFS); - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - return (ENOBUFS); - } - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - } else { - m_new = m; - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - m_new->m_data = m_new->m_ext.ext_buf; + MCLGET(m_new, M_DONTWAIT); + if ((m_new->m_flags & M_EXT) == 0) { + m_freem(m_new); + return (ENOBUFS); } - m_adj(m_new, sizeof(uint64_t)); + if (ds->ds_mbuf != NULL) + bus_dmamap_unload(sc->vr_dmat, ds->ds_dmamap); + + ds->ds_mbuf = m_new; + + error = bus_dmamap_load(sc->vr_dmat, ds->ds_dmamap, + m_new->m_ext.ext_buf, m_new->m_ext.ext_size, NULL, BUS_DMA_NOWAIT); + if (error) + if_printf(&sc->arpcom.ac_if, +"unable to load rx DMA map %d, error = %d\n", i, error); + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); - c->vr_mbuf = m_new; - c->vr_ptr->vr_status = VR_RXSTAT; - c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); - c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; + VR_INIT_RXDESC(sc, i); return (0); } @@ -929,26 +959,35 @@ { struct mbuf *m, *m0; struct ifnet *ifp; - struct vr_chain_onefrag *cur_rx; - int total_len = 0; + struct vr_desc *d; + struct vr_descsoft *ds; + int i, total_len; uint32_t rxstat; VR_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; - while (!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & - VR_RXSTAT_OWN)) { + for (i = sc->vr_rxptr; ; i = VR_NEXTRX(i)) { #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) { if (sc->rxcycles <= 0) break; sc->rxcycles--; } -#endif /* DEVICE_POLLING */ - m0 = NULL; - cur_rx = sc->vr_cdata.vr_rx_head; - sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; - m = cur_rx->vr_mbuf; +#endif + d = VR_CDRX(sc, i); + ds = VR_DSRX(sc, i); + + VR_CDRXSYNC(sc, i, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + + rxstat = d->vr_status; + + if (rxstat & VR_RXSTAT_OWN) { + /* + * We have processed all of the receive buffers. + */ + break; + } /* * If an error occurs, update stats, clear the @@ -958,7 +997,7 @@ */ if (rxstat & VR_RXSTAT_RXERR) { ifp->if_ierrors++; - printf("vr%d: rx error (%02x):", sc->vr_unit, + if_printf(ifp, "rx error (%02x):", rxstat & 0x000000ff); if (rxstat & VR_RXSTAT_CRCERR) printf(" crc error"); @@ -975,12 +1014,15 @@ if (rxstat & VR_RXSTAT_BUFFERR) printf("rx buffer error"); printf("\n"); - vr_newbuf(sc, cur_rx, m); + VR_INIT_RXDESC(sc, i); continue; } + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD); + /* No errors; receive the packet. */ - total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); + total_len = VR_RXBYTES(d->vr_status); /* * XXX The VIA Rhine chip includes the CRC with every @@ -991,20 +1033,52 @@ */ total_len -= ETHER_CRC_LEN; - m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, - NULL); - vr_newbuf(sc, cur_rx, m); - if (m0 == NULL) { + /* + * The Rhine's packet buffers must be 4-byte aligned. + * But this means that the data after the Ethernet header + * is misaligned. We must allocate a new buffer and + * copy the data, shifted forward 2 bytes. + */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { +dropit: ifp->if_ierrors++; + VR_INIT_RXDESC(sc, i); + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); continue; } - m = m0; + if (total_len > (MHLEN - 2)) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + goto dropit; + } + } + m->m_data += 2; + + /* + * Note that we use clusters for incoming frames, so the + * buffer is virtually contiguous. + */ + bcopy(mtod(ds->ds_mbuf, char *), mtod(m, char *), total_len); + + /* Allow the receive descriptor to continue using its mbuf. */ + VR_INIT_RXDESC(sc, i); + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = total_len; + VR_UNLOCK(sc); (*ifp->if_input)(ifp, m); VR_LOCK(sc); } + + /* Update the receive pointer. */ + sc->vr_rxptr = i; } static void @@ -1035,7 +1109,7 @@ vr_rxeof(sc); - CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); + CSR_WRITE_4(sc, VR_RXADDR, VR_CDRXADDR(sc, sc->vr_rxptr)); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); } @@ -1047,8 +1121,11 @@ static void vr_txeof(struct vr_softc *sc) { - struct vr_chain *cur_tx; struct ifnet *ifp = &sc->arpcom.ac_if; + struct vr_desc *d; + struct vr_descsoft *ds; + u_int32_t txstat; + int i; VR_LOCK_ASSERT(sc); @@ -1056,12 +1133,14 @@ * Go through our tx list and free mbufs for those * frames that have been transmitted. */ - cur_tx = sc->vr_cdata.vr_tx_cons; - while (cur_tx->vr_mbuf != NULL) { - uint32_t txstat; - int i; + for (i = sc->vr_txdirty; sc->vr_txpending != 0; + i = VR_NEXTTX(i), sc->vr_txpending--) { + d = VR_CDTX(sc, i); + ds = VR_DSTX(sc, i); + + VR_CDTXSYNC(sc, i, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); - txstat = cur_tx->vr_ptr->vr_status; + txstat = d->vr_status; if ((txstat & VR_TXSTAT_ABRT) || (txstat & VR_TXSTAT_UDF)) { @@ -1083,6 +1162,12 @@ if (txstat & VR_TXSTAT_OWN) break; + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, + 0, ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->vr_dmat, ds->ds_dmamap); + m_freem(ds->ds_mbuf); + ds->ds_mbuf = NULL; + if (txstat & VR_TXSTAT_ERRSUM) { ifp->if_oerrors++; if (txstat & VR_TXSTAT_DEFER) @@ -1091,17 +1176,19 @@ ifp->if_collisions++; } - ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3; - + ifp->if_collisions += (txstat & VR_TXSTAT_COLLCNT) >> 3; ifp->if_opackets++; - m_freem(cur_tx->vr_mbuf); - cur_tx->vr_mbuf = NULL; ifp->if_flags &= ~IFF_OACTIVE; - - cur_tx = cur_tx->vr_nextdesc; } - sc->vr_cdata.vr_tx_cons = cur_tx; - if (cur_tx->vr_mbuf == NULL) + + /* Update the dirty transmit buffer pointer. */ + sc->vr_txdirty = i; + + /* + * Cancel the watchdog timer if there are no pending + * transmissions. + */ + if (sc->vr_txpending == 0) ifp->if_timer = 0; } @@ -1217,6 +1304,7 @@ struct vr_softc *sc = arg; struct ifnet *ifp = &sc->arpcom.ac_if; uint16_t status; + int handled = 0, dotx = 0; VR_LOCK(sc); #ifdef DEVICE_POLLING @@ -1234,7 +1322,7 @@ } #endif /* DEVICE_POLLING */ - /* Supress unwanted interrupts. */ + /* Suppress unwanted interrupts. */ if (!(ifp->if_flags & IFF_UP)) { vr_stop(sc); VR_UNLOCK(sc); @@ -1256,14 +1344,13 @@ vr_rxeof(sc); if (status & VR_ISR_RX_DROPPED) { - printf("vr%d: rx packet lost\n", sc->vr_unit); + if_printf(ifp, "rx packet lost\n"); ifp->if_ierrors++; } if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW)) { - printf("vr%d: receive error (%04x)", - sc->vr_unit, status); + if_printf(ifp, "receive error (%04x)", status); if (status & VR_ISR_RX_NOBUF) printf(" no buffers"); if (status & VR_ISR_RX_OFLOW) @@ -1274,135 +1361,197 @@ vr_rxeoc(sc); } - if ((status & VR_ISR_BUSERR) || (status & VR_ISR_TX_UNDERRUN)) { + if (status & VR_ISR_TX_OK) { + dotx = 1; + vr_txeof(sc); + } + + if (status & (VR_ISR_TX_ABRT | VR_ISR_TX_ABRT2 | VR_ISR_UDFI)) { + ifp->if_oerrors++; + dotx = 1; + vr_txeof(sc); + if (sc->vr_txpending) { + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); + } + } + + if (status & (VR_ISR_BUSERR | VR_ISR_TX_UNDERRUN)) { + dotx = 0; vr_reset(sc); VR_UNLOCK(sc); vr_init(sc); VR_LOCK(sc); break; } - - if ((status & VR_ISR_TX_OK) || (status & VR_ISR_TX_ABRT) || - (status & VR_ISR_TX_ABRT2) || (status & VR_ISR_UDFI)) { - vr_txeof(sc); - if ((status & VR_ISR_UDFI) || - (status & VR_ISR_TX_ABRT2) || - (status & VR_ISR_TX_ABRT)) { - ifp->if_oerrors++; - if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) { - VR_SETBIT16(sc, VR_COMMAND, - VR_CMD_TX_ON); - VR_SETBIT16(sc, VR_COMMAND, - VR_CMD_TX_GO); - } - } - } } /* Re-enable interrupts. */ CSR_WRITE_2(sc, VR_IMR, VR_INTRS); VR_UNLOCK(sc); - if (_IF_QLEN(&ifp->if_snd) != 0) + if (dotx || (_IF_QLEN(&ifp->if_snd) != 0)) vr_start(ifp); } /* - * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data - * pointers to the fragment pointers. - */ -static int -vr_encap(struct vr_softc *sc, struct vr_chain *c, struct mbuf *m_head) -{ - struct vr_desc *f = NULL; - struct mbuf *m; - - VR_LOCK_ASSERT(sc); - /* - * The VIA Rhine wants packet buffers to be longword - * aligned, but very often our mbufs aren't. Rather than - * waste time trying to decide when to copy and when not - * to copy, just do it all the time. - */ - m = m_defrag(m_head, M_DONTWAIT); - if (m == NULL) - return (1); - - /* - * The Rhine chip doesn't auto-pad, so we have to make - * sure to pad short frames out to the minimum frame length - * ourselves. - */ - if (m->m_len < VR_MIN_FRAMELEN) { - m->m_pkthdr.len += VR_MIN_FRAMELEN - m->m_len; - m->m_len = m->m_pkthdr.len; - } - - c->vr_mbuf = m; - f = c->vr_ptr; - f->vr_data = vtophys(mtod(m, caddr_t)); - f->vr_ctl = m->m_len; - f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG; - f->vr_status = 0; - f->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT; - f->vr_next = vtophys(c->vr_nextdesc->vr_ptr); - - return (0); -} - -/* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ - static void vr_start(struct ifnet *ifp) { struct vr_softc *sc = ifp->if_softc; - struct mbuf *m_head; - struct vr_chain *cur_tx; + struct mbuf *m0, *m; + struct vr_desc *d; + struct vr_descsoft *ds; + int error, firsttx, nexttx, opending; if (ifp->if_flags & IFF_OACTIVE) return; VR_LOCK(sc); - cur_tx = sc->vr_cdata.vr_tx_prod; - while (cur_tx->vr_mbuf == NULL) { - IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) - break; + opending = sc->vr_txpending; + firsttx = VR_NEXTTX(sc->vr_txlast); - /* Pack the data into the descriptor. */ - if (vr_encap(sc, cur_tx, m_head)) { - /* Rollback, send what we were able to encap. */ - IF_PREPEND(&ifp->if_snd, m_head); + /* + * Loop through the send queue, setting up transmit descriptors + * until we drain the queue, or use up all available transmit + * descriptors. + */ + while (sc->vr_txpending < VR_NTXDESC) { + /* + * Grab a packet off the queue. + */ + IF_DEQUEUE(&ifp->if_snd, m0); + if (m0 == NULL) break; + + /* + * Get the next available transmit descriptor. + */ + nexttx = VR_NEXTTX(sc->vr_txlast); + d = VR_CDTX(sc, nexttx); + ds = VR_DSTX(sc, nexttx); + + /* + * Load the DMA map. If this fails, the packet didn't + * fit in one DMA segment, and we need to copy. Note, + * the packet must also be aligned. + */ + if ((mtod(m0, bus_addr_t) & 3) != 0 || + bus_dmamap_load_mbuf(sc->vr_dmat, ds->ds_dmamap, m0, + BUS_DMA_NOWAIT) != 0) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + if_printf(ifp, "unable to allocate tx mbuf\n"); + IF_PREPEND(&ifp->if_snd, m0); + break; + } + if (m0->m_pkthdr.len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + if_printf(ifp, +"unable to allocate tx cluster\n"); + m_freem(m); + IF_PREPEND(&ifp->if_snd, m0); + break; + } + } + m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, char *)); + m->m_pkthdr.len = m->m_len = m0->m_pkthdr.len; + m_freem(m0); + m0 = m; + error = bus_dmamap_load_mbuf(sc->vr_dmat, + ds->ds_dmamap, m0, BUS_DMA_NOWAIT); + if (error) { + if_printf(ifp, +"unable to load tx buffer, error = %d\n", error); + IF_PREPEND(&ifp->if_snd, m0); + break; + } } - VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; + /* Sync the DMA map. */ + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE); + + /* + * Store a pointer to the packet so we can free it later. + */ + ds->ds_mbuf = m0; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ - BPF_MTAP(ifp, cur_tx->vr_mbuf); + BPF_MTAP(ifp, m0); + + /* + * Fill in the transmit descriptor. The Rhine + * doesn't auto-pad, so we have to do this ourselves. + */ + d->vr_data = ds->ds_dmamap->dm_segs[0].ds_addr; + d->vr_ctl = m0->m_pkthdr.len < VR_MIN_FRAMELEN ? + VR_MIN_FRAMELEN : m0->m_pkthdr.len; + d->vr_ctl |= + VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG|VR_TXCTL_LASTFRAG; + + /* + * If this is the first descriptor we're enqueuing, + * don't give it to the Rhine yet. That could cause + * a race condition. We'll do it below. + */ + if (nexttx == firsttx) + d->vr_status = 0; + else + d->vr_status = VR_TXSTAT_OWN; + + VR_CDTXSYNC(sc, nexttx, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); - cur_tx = cur_tx->vr_nextdesc; + /* Advance the tx pointer. */ + sc->vr_txpending++; + sc->vr_txlast = nexttx; } - if (cur_tx != sc->vr_cdata.vr_tx_prod || cur_tx->vr_mbuf != NULL) { - sc->vr_cdata.vr_tx_prod = cur_tx; - /* Tell the chip to start transmitting. */ - VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/ VR_CMD_TX_GO); + if (sc->vr_txpending == VR_NTXDESC) { + /* No more slots left; notify upper layer. */ + ifp->if_flags |= IFF_OACTIVE; + } - /* Set a timeout in case the chip goes out to lunch. */ - ifp->if_timer = 5; + if (sc->vr_txpending != opending) { + /* + * We enqueued packets. If the transmitter was idle, + * reset the txdirty pointer. + */ + if (opending == 0) + sc->vr_txdirty = firsttx; + + /* + * Cause a transmit interrupt to happen on the + * last packet we enqueued. + */ + VR_CDTX(sc, sc->vr_txlast)->vr_ctl |= VR_TXCTL_FINT; + VR_CDTXSYNC(sc, sc->vr_txlast, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); - if (cur_tx->vr_mbuf != NULL) - ifp->if_flags |= IFF_OACTIVE; + /* + * The entire packet chain is set up. Give the + * first descriptor to the Rhine now. + */ + VR_CDTX(sc, firsttx)->vr_status = VR_TXSTAT_OWN; + VR_CDTXSYNC(sc, firsttx, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + /* Start the transmitter. */ + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_TX_GO); + + /* Set the watchdog timer in case the chip flakes out. */ + ifp->if_timer = 5; } VR_UNLOCK(sc); @@ -1414,6 +1563,7 @@ struct vr_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; struct mii_data *mii; + struct vr_desc *d; int i; VR_LOCK(sc); @@ -1448,17 +1598,28 @@ VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); - /* Init circular RX list. */ - if (vr_list_rx_init(sc) == ENOBUFS) { - printf( -"vr%d: initialization failed: no memory for rx buffers\n", sc->vr_unit); - vr_stop(sc); - VR_UNLOCK(sc); - return; - } + /* + * Initialize the transmit desciptor ring. txlast is initialized + * to the end of the list so that it will wrap around to the first + * descriptor when the first packet is transmitted. + */ + for (i = 0; i < VR_NTXDESC; i++) { + d = VR_CDTX(sc, i); + bzero(d, sizeof(struct vr_desc)); + d->vr_next = VR_CDTXADDR(sc, VR_NEXTTX(i)); + VR_CDTXSYNC(sc, i, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + } + sc->vr_txpending = 0; + sc->vr_txdirty = 0; + sc->vr_txlast = VR_NTXDESC - 1; - /* Init tx descriptors. */ - vr_list_tx_init(sc); + /* + * Initialize the receive descriptor ring. The buffers are + * already allocated. + */ + for (i = 0; i < VR_NRXDESC; i++) + VR_INIT_RXDESC(sc, i); + sc->vr_rxptr = 0; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) @@ -1477,18 +1638,15 @@ */ vr_setmulti(sc); - /* - * Load the address of the RX list. - */ - CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); + /* Give the transmit and recieve rings to the Rhine. */ + CSR_WRITE_4(sc, VR_RXADDR, VR_CDRXADDR(sc, sc->vr_rxptr)); + CSR_WRITE_4(sc, VR_TXADDR, VR_CDTXADDR(sc, VR_NEXTTX(sc->vr_txlast))); /* Enable receiver and transmitter. */ CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| VR_CMD_TX_ON|VR_CMD_RX_ON| VR_CMD_RX_GO); - CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); - CSR_WRITE_2(sc, VR_ISR, 0xFFFF); #ifdef DEVICE_POLLING /* @@ -1498,9 +1656,7 @@ CSR_WRITE_2(sc, VR_IMR, 0); else #endif /* DEVICE_POLLING */ - /* - * Enable interrupts. - */ + /* Enable interrupts. */ CSR_WRITE_2(sc, VR_IMR, VR_INTRS); mii_mediachg(mii); @@ -1552,17 +1708,31 @@ switch (command) { case SIOCSIFFLAGS: - if (ifp->if_flags & IFF_UP) { + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* + * If interface is marked down and it is running, then + * stop it. + */ + VR_LOCK(sc); + vr_stop(sc); + VR_UNLOCK(sc); + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + vr_init(sc); + } else if ((ifp->if_flags & IFF_UP) != 0) { + /* + * Reset the interface to pick up changes in any other + * flags that affect the hardware state. + */ vr_init(sc); - } else { - if (ifp->if_flags & IFF_RUNNING) { - VR_LOCK(sc); - vr_stop(sc); - VR_UNLOCK(sc); - } } - error = 0; break; + case SIOCADDMULTI: case SIOCDELMULTI: VR_LOCK(sc); @@ -1570,14 +1740,17 @@ VR_UNLOCK(sc); error = 0; break; + case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->vr_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; + case SIOCSIFCAP: ifp->if_capenable = ifr->ifr_reqcap; break; + default: error = ether_ioctl(ifp, command, data); break; @@ -1591,36 +1764,28 @@ { struct vr_softc *sc = ifp->if_softc; - VR_LOCK(sc); + if_printf(ifp, "watchdog timeout\n"); ifp->if_oerrors++; - printf("vr%d: watchdog timeout\n", sc->vr_unit); - - vr_stop(sc); - vr_reset(sc); - VR_UNLOCK(sc); - vr_init(sc); - if (ifp->if_snd.ifq_head != NULL) - vr_start(ifp); } /* - * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. + * Stop the adapter and free any mbufs allocated to the transmit lists. */ static void vr_stop(struct vr_softc *sc) { - register int i; - struct ifnet *ifp; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct vr_descsoft *ds; + int i; VR_LOCK_ASSERT(sc); - ifp = &sc->arpcom.ac_if; + /* Mark the interface down and cancel the watchdog timer. */ ifp->if_timer = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); untimeout(vr_tick, sc, sc->vr_stat_ch); - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); #ifdef DEVICE_POLLING ether_poll_deregister(ifp); #endif /* DEVICE_POLLING */ @@ -1632,28 +1797,16 @@ CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); /* - * Free data in the RX lists. - */ - for (i = 0; i < VR_RX_LIST_CNT; i++) { - if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { - m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); - sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; - } - } - bzero((char *)&sc->vr_ldata->vr_rx_list, - sizeof(sc->vr_ldata->vr_rx_list)); - - /* - * Free the TX list buffers. + * Release any queued transmit buffers. */ - for (i = 0; i < VR_TX_LIST_CNT; i++) { - if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) { - m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf); - sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL; + for (i = 0; i < VR_NTXDESC; i++) { + ds = VR_DSTX(sc, i); + if (ds->ds_mbuf != NULL) { + bus_dmamap_unload(sc->vr_dmat, ds->ds_dmamap); + m_freem(ds->ds_mbuf); + ds->ds_mbuf = NULL; } } - bzero((char *)&sc->vr_ldata->vr_tx_list, - sizeof(sc->vr_ldata->vr_tx_list)); } /*
Index: if_vrreg.h =================================================================== RCS file: /home/ncvs/src/sys/pci/if_vrreg.h,v retrieving revision 1.19 diff -u -r1.19 if_vrreg.h --- if_vrreg.h 5 Apr 2004 17:39:57 -0000 1.19 +++ if_vrreg.h 3 Jul 2004 05:59:32 -0000 @@ -397,31 +397,42 @@ #define VR_TXOWN(x) x->vr_ptr->vr_status -struct vr_list_data { - struct vr_desc vr_rx_list[VR_RX_LIST_CNT]; - struct vr_desc vr_tx_list[VR_TX_LIST_CNT]; -}; +/* + * Transmit descriptor list size. + */ +#define VR_NTXDESC 64 +#define VR_NTXDESC_MASK (VR_NTXDESC - 1) +#define VR_NEXTTX(x) (((x) + 1) & VR_NTXDESC_MASK) -struct vr_chain { - struct vr_desc *vr_ptr; - struct mbuf *vr_mbuf; - struct vr_chain *vr_nextdesc; -}; +/* + * Receive descriptor list size. + */ +#define VR_NRXDESC 64 +#define VR_NRXDESC_MASK (VR_NRXDESC - 1) +#define VR_NEXTRX(x) (((x) + 1) & VR_NRXDESC_MASK) -struct vr_chain_onefrag { - struct vr_desc *vr_ptr; - struct mbuf *vr_mbuf; - struct vr_chain_onefrag *vr_nextdesc; +/* + * Control data structres that are DMA'd to the Rhine chip. We allocate + * them in a single clump that maps to a single DMA segment to make several + * things easier. + * + * Note that since we always copy outgoing packets to aligned transmit + * buffers, we can reduce the transmit descriptors to one per packet. + */ +struct vr_control_data { + struct vr_desc vr_txdescs[VR_NTXDESC]; + struct vr_desc vr_rxdescs[VR_NRXDESC]; }; +#define VR_CDOFF(x) offsetof(struct vr_control_data, x) +#define VR_CDTXOFF(x) VR_CDOFF(vr_txdescs[(x)]) +#define VR_CDRXOFF(x) VR_CDOFF(vr_rxdescs[(x)]) -struct vr_chain_data { - struct vr_chain_onefrag vr_rx_chain[VR_RX_LIST_CNT]; - struct vr_chain vr_tx_chain[VR_TX_LIST_CNT]; - - struct vr_chain_onefrag *vr_rx_head; - - struct vr_chain *vr_tx_cons; - struct vr_chain *vr_tx_prod; +/* + * Software state of transmit and receive descriptors. + */ +struct vr_descsoft { + struct mbuf *ds_mbuf; /* head of mbuf chain */ + bus_dmamap_t ds_dmamap; /* our DMA map */ }; struct vr_type { @@ -449,29 +460,46 @@ #define VR_FLAG_FORCEDELAY 1 #define VR_FLAG_SCHEDDELAY 2 -#define VR_FLAG_DELAYTIMEO 3 +#define VR_FLAG_DELAYTIMEO 3 struct vr_softc { - struct arpcom arpcom; /* interface info */ - bus_space_handle_t vr_bhandle; /* bus space handle */ - bus_space_tag_t vr_btag; /* bus space tag */ + struct arpcom arpcom; /* interface info */ + bus_space_handle_t vr_bhandle; /* bus space handle */ + bus_space_tag_t vr_btag; /* bus space tag */ + bus_dma_tag_t vr_dmat; /* bus DMA tag */ struct resource *vr_res; struct resource *vr_irq; void *vr_intrhand; - device_t vr_miibus; + device_t vr_miibus; struct vr_type *vr_info; /* Rhine adapter info */ - u_int8_t vr_unit; /* interface number */ - u_int8_t vr_type; - u_int8_t vr_revid; /* Rhine chip revision */ - u_int8_t vr_flags; /* See VR_F_* below */ - struct vr_list_data *vr_ldata; - struct vr_chain_data vr_cdata; - struct callout_handle vr_stat_ch; - struct mtx vr_mtx; + u_int8_t vr_unit; /* interface number */ + u_int8_t vr_type; + u_int8_t vr_revid; /* Rhine chip revision */ + u_int8_t vr_flags; /* See VR_F_* below */ + struct callout_handle vr_stat_ch; + struct mtx vr_mtx; + bus_dmamap_t vr_cddmamap; /* control data DMA map */ + + /* + * Software state for transmit and receive descriptors. + */ + struct vr_descsoft vr_txsoft[VR_NTXDESC]; + struct vr_descsoft vr_rxsoft[VR_NRXDESC]; + + /* + * Control data structures. + */ + struct vr_control_data *vr_control_data; + int vr_txpending; /* # of TX requests pending */ + int vr_txdirty; /* first dirty TX descriptor */ + int vr_txlast; /* last used TX descriptor */ + int vr_rxptr; /* next ready RX descriptor */ + #ifdef DEVICE_POLLING - int rxcycles; + int rxcycles; #endif }; +#define vr_cddma vr_cddmamap->dm_segs[0].ds_addr #define VR_F_RESTART 0x01 /* Restart unit on next tick */ @@ -479,6 +507,42 @@ #define VR_UNLOCK(_sc) mtx_unlock(&(_sc)->vr_mtx) #define VR_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->vr_mtx, MA_OWNED) +#define VR_CDTXADDR(sc, x) ((sc)->vr_cddma + VR_CDTXOFF((x))) +#define VR_CDRXADDR(sc, x) ((sc)->vr_cddma + VR_CDRXOFF((x))) + +#define VR_CDTX(sc, x) (&(sc)->vr_control_data->vr_txdescs[(x)]) +#define VR_CDRX(sc, x) (&(sc)->vr_control_data->vr_rxdescs[(x)]) + +#define VR_DSTX(sc, x) (&(sc)->vr_txsoft[(x)]) +#define VR_DSRX(sc, x) (&(sc)->vr_rxsoft[(x)]) + +#define VR_CDTXSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->vr_dmat, (sc)->vr_cddmamap, \ + VR_CDTXOFF((x)), sizeof(struct vr_desc), (ops)) + +#define VR_CDRXSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->vr_dmat, (sc)->vr_cddmamap, \ + VR_CDRXOFF((x)), sizeof(struct vr_desc), (ops)) + +/* + * Note we rely on MCLBYTES being a power of two below. + */ +#define VR_INIT_RXDESC(sc, i) \ + do {\ + struct vr_desc *__d = VR_CDRX((sc), (i)); \ + struct vr_descsoft *__ds = VR_DSRX((sc), (i)); \ + \ + __d->vr_next = VR_CDRXADDR((sc), VR_NEXTRX((i))); \ + __d->vr_status = VR_RXSTAT_FIRSTFRAG | VR_RXSTAT_LASTFRAG | \ + VR_RXSTAT_OWN; \ + __d->vr_data = __ds->ds_dmamap->dm_segs[0].ds_addr; \ + __d->vr_ctl = VR_RXCTL_CHAIN | VR_RXCTL_RX_INTR | \ + ((MCLBYTES - 1) & VR_RXCTL_BUFLEN); \ + VR_CDRXSYNC((sc), (i), \ + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); \ + } while (0) + + /* * register space access macros */ @@ -590,8 +654,4 @@ #define VR_PME_EN 0x0010 #define VR_PME_STATUS 0x8000 - -#ifdef __alpha__ -#undef vtophys -#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) -#endif +#define AC2IFP(acp) &((acp)->ac_if)
pgpcFPvNjG8GO.pgp
Description: PGP signature