From owner-svn-src-stable-7@FreeBSD.ORG Fri Jan 8 21:55:44 2010 Return-Path: Delivered-To: svn-src-stable-7@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 67CDD106566B; Fri, 8 Jan 2010 21:55:44 +0000 (UTC) (envelope-from yongari@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 553D98FC18; Fri, 8 Jan 2010 21:55:44 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o08LtijM002741; Fri, 8 Jan 2010 21:55:44 GMT (envelope-from yongari@svn.freebsd.org) Received: (from yongari@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o08LtiuX002738; Fri, 8 Jan 2010 21:55:44 GMT (envelope-from yongari@svn.freebsd.org) Message-Id: <201001082155.o08LtiuX002738@svn.freebsd.org> From: Pyun YongHyeon Date: Fri, 8 Jan 2010 21:55:44 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org X-SVN-Group: stable-7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r201834 - stable/7/sys/dev/vge X-BeenThere: svn-src-stable-7@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for only the 7-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 08 Jan 2010 21:55:44 -0000 Author: yongari Date: Fri Jan 8 21:55:44 2010 New Revision: 201834 URL: http://svn.freebsd.org/changeset/base/201834 Log: MFC r200525: Overhaul bus_dma(9) usage and fix various things. o Separate TX/RX buffer DMA tag from TX/RX descriptor ring DMA tag. o Separate RX buffer DMA tag from common buffer DMA tag. RX DMA tag has different restriction compared to TX DMA tag. o Add 40bit DMA address support. o Adjust TX/RX descriptor ring alignment to 64 bytes from 256 bytes as documented in datasheet. o Added check to ensure TX/RX ring reside within a 4GB boundary. Since TX/RX ring shares the same high address register they should have the same high address. o TX/RX side bus_dmamap_load_mbuf_sg(9) support. o Add lock assertion to vge_setmulti(). o Add RX spare DMA map to recover from DMA map load failure. o Add optimized RX buffer handler, vge_discard_rxbuf which is activated when vge(4) sees bad frames. o Don't blindly update VGE_RXDESC_RESIDUECNT register. Datasheet says the register should be updated only when number of available RX descriptors are multiple of 4. o Use __NO_STRICT_ALIGNMENT instead of defining VGE_FIXUP_RX which is only set for i386 architecture. Previously vge(4) also performed expensive copy operation to align IP header on amd64. This change should give RX performance boost on amd64 architecture. o Don't reinitialize controller if driver is already running. This should reduce number of link state flipping. o Since vge(4) drops a driver lock before passing received frame to upper layer, make sure vge(4) is still running after re-acquiring driver lock. o Add second argument count to vge_rxeof(). The argument will limit number of packets could be processed in RX handler. o Rearrange vge_rxeof() not to allocate RX buffer if received frame was bad packet. o Removed if_printf that prints DMA map failure. This type of message shouldn't be used in fast path of driver. o Reduce number of allowed TX buffer fragments to 6 from 7. A TX descriptor allows 7 fragments of a frame. However the CMZ field of descriptor has just 3bits and the controller wants to see fragment + 1 in the field. So if we have 7 fragments the field value would be 0 which seems to cause unexpected results under certain conditions. This change should fix occasional TX hang observed on vge(4). o Simplify vge_stat_locked() and add number of available TX descriptor check. o vge(4) controllers lack padding short frames. Make sure to fill zero for the padded bytes. This closes unintended information disclosure. o Don't set VGE_TDCTL_JUMBO flag. Datasheet is not clear whether this bit should be set by driver or write-back status bit after transmission. At least vendor's driver does not set this bit so remove it. Without this bit vge(4) still can send jumbo frames. o Don't start driver when vge(4) know there are not enough RX buffers. o Remove volatile keyword in RX descriptor structure. This should be handled by bus_dma(9). o Collapse two 16bits member of TX/RX descriptor into single 32bits member. o Reduce number of RX descriptors to 252 from 256. The VGE_RXDESCNUM is 16bits register but only lower 8bits are valid. So the maximum number of RX descriptors would be 255. However the number of should be multiple of 4 as controller wants to update 4 RX descriptors at a time. This limits the maximum number of RX descriptor to be 252. PR: kern/141276, kern/141414 Modified: stable/7/sys/dev/vge/if_vge.c stable/7/sys/dev/vge/if_vgereg.h stable/7/sys/dev/vge/if_vgevar.h Directory Properties: stable/7/sys/ (props changed) stable/7/sys/cddl/contrib/opensolaris/ (props changed) stable/7/sys/contrib/dev/acpica/ (props changed) stable/7/sys/contrib/pf/ (props changed) Modified: stable/7/sys/dev/vge/if_vge.c ============================================================================== --- stable/7/sys/dev/vge/if_vge.c Fri Jan 8 21:42:00 2010 (r201833) +++ stable/7/sys/dev/vge/if_vge.c Fri Jan 8 21:55:44 2010 (r201834) @@ -140,22 +140,21 @@ static int vge_probe (device_t); static int vge_attach (device_t); static int vge_detach (device_t); -static int vge_encap (struct vge_softc *, struct mbuf *, int); +static int vge_encap (struct vge_softc *, struct mbuf **); -static void vge_dma_map_addr (void *, bus_dma_segment_t *, int, int); -static void vge_dma_map_rx_desc (void *, bus_dma_segment_t *, int, - bus_size_t, int); -static void vge_dma_map_tx_desc (void *, bus_dma_segment_t *, int, - bus_size_t, int); -static int vge_allocmem (device_t, struct vge_softc *); -static int vge_newbuf (struct vge_softc *, int, struct mbuf *); +static void vge_dmamap_cb (void *, bus_dma_segment_t *, int, int); +static int vge_dma_alloc (struct vge_softc *); +static void vge_dma_free (struct vge_softc *); +static void vge_discard_rxbuf (struct vge_softc *, int); +static int vge_newbuf (struct vge_softc *, int); static int vge_rx_list_init (struct vge_softc *); static int vge_tx_list_init (struct vge_softc *); -#ifdef VGE_FIXUP_RX +static void vge_freebufs (struct vge_softc *); +#ifndef __NO_STRICT_ALIGNMENT static __inline void vge_fixup_rx (struct mbuf *); #endif -static void vge_rxeof (struct vge_softc *); +static int vge_rxeof (struct vge_softc *, int); static void vge_txeof (struct vge_softc *); static void vge_intr (void *); static void vge_tick (void *); @@ -548,6 +547,8 @@ vge_setmulti(sc) struct ifmultiaddr *ifma; u_int32_t h, hashes[2] = { 0, 0 }; + VGE_LOCK_ASSERT(sc); + ifp = sc->vge_ifp; /* First, zot all the multicast entries. */ @@ -665,249 +666,326 @@ vge_probe(dev) return (ENXIO); } -static void -vge_dma_map_rx_desc(arg, segs, nseg, mapsize, error) - void *arg; - bus_dma_segment_t *segs; - int nseg; - bus_size_t mapsize; - int error; -{ - - struct vge_dmaload_arg *ctx; - struct vge_rx_desc *d = NULL; - - if (error) - return; - - ctx = arg; - - /* Signal error to caller if there's too many segments */ - if (nseg > ctx->vge_maxsegs) { - ctx->vge_maxsegs = 0; - return; - } - - /* - * Map the segment array into descriptors. - */ - - d = &ctx->sc->vge_ldata.vge_rx_list[ctx->vge_idx]; - - /* If this descriptor is still owned by the chip, bail. */ - - if (le32toh(d->vge_sts) & VGE_RDSTS_OWN) { - device_printf(ctx->sc->vge_dev, - "tried to map busy descriptor\n"); - ctx->vge_maxsegs = 0; - return; - } - - d->vge_buflen = htole16(VGE_BUFLEN(segs[0].ds_len) | VGE_RXDESC_I); - d->vge_addrlo = htole32(VGE_ADDR_LO(segs[0].ds_addr)); - d->vge_addrhi = htole16(VGE_ADDR_HI(segs[0].ds_addr) & 0xFFFF); - d->vge_sts = 0; - d->vge_ctl = 0; - - ctx->vge_maxsegs = 1; - - return; -} - -static void -vge_dma_map_tx_desc(arg, segs, nseg, mapsize, error) - void *arg; - bus_dma_segment_t *segs; - int nseg; - bus_size_t mapsize; - int error; -{ - struct vge_dmaload_arg *ctx; - struct vge_tx_desc *d = NULL; - struct vge_tx_frag *f; - int i = 0; - - if (error) - return; - - ctx = arg; - - /* Signal error to caller if there's too many segments */ - if (nseg > ctx->vge_maxsegs) { - ctx->vge_maxsegs = 0; - return; - } - - /* Map the segment array into descriptors. */ - - d = &ctx->sc->vge_ldata.vge_tx_list[ctx->vge_idx]; - - /* If this descriptor is still owned by the chip, bail. */ - - if (le32toh(d->vge_sts) & VGE_TDSTS_OWN) { - ctx->vge_maxsegs = 0; - return; - } - - for (i = 0; i < nseg; i++) { - f = &d->vge_frag[i]; - f->vge_buflen = htole16(VGE_BUFLEN(segs[i].ds_len)); - f->vge_addrlo = htole32(VGE_ADDR_LO(segs[i].ds_addr)); - f->vge_addrhi = htole16(VGE_ADDR_HI(segs[i].ds_addr) & 0xFFFF); - } - - /* Argh. This chip does not autopad short frames */ - - if (ctx->vge_m0->m_pkthdr.len < VGE_MIN_FRAMELEN) { - f = &d->vge_frag[i]; - f->vge_buflen = htole16(VGE_BUFLEN(VGE_MIN_FRAMELEN - - ctx->vge_m0->m_pkthdr.len)); - f->vge_addrlo = htole32(VGE_ADDR_LO(segs[0].ds_addr)); - f->vge_addrhi = htole16(VGE_ADDR_HI(segs[0].ds_addr) & 0xFFFF); - ctx->vge_m0->m_pkthdr.len = VGE_MIN_FRAMELEN; - i++; - } - - /* - * When telling the chip how many segments there are, we - * must use nsegs + 1 instead of just nsegs. Darned if I - * know why. - */ - i++; - - d->vge_sts = ctx->vge_m0->m_pkthdr.len << 16; - d->vge_ctl = ctx->vge_flags|(i << 28)|VGE_TD_LS_NORM; - - if (ctx->vge_m0->m_pkthdr.len > ETHERMTU + ETHER_HDR_LEN) - d->vge_ctl |= VGE_TDCTL_JUMBO; - - ctx->vge_maxsegs = nseg; - - return; -} - /* * Map a single buffer address. */ +struct vge_dmamap_arg { + bus_addr_t vge_busaddr; +}; + static void -vge_dma_map_addr(arg, segs, nseg, error) +vge_dmamap_cb(arg, segs, nsegs, error) void *arg; bus_dma_segment_t *segs; - int nseg; + int nsegs; int error; { - bus_addr_t *addr; + struct vge_dmamap_arg *ctx; - if (error) + if (error != 0) return; - KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); - addr = arg; - *addr = segs->ds_addr; + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); - return; + ctx = (struct vge_dmamap_arg *)arg; + ctx->vge_busaddr = segs[0].ds_addr; } static int -vge_allocmem(dev, sc) - device_t dev; - struct vge_softc *sc; +vge_dma_alloc(sc) + struct vge_softc *sc; { - int error; - int nseg; - int i; - - /* - * Allocate map for RX mbufs. - */ - nseg = 32; - error = bus_dma_tag_create(sc->vge_parent_tag, ETHER_ALIGN, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, - NULL, MCLBYTES * nseg, nseg, MCLBYTES, BUS_DMA_ALLOCNOW, - NULL, NULL, &sc->vge_ldata.vge_mtag); - if (error) { - device_printf(dev, "could not allocate dma tag\n"); - return (ENOMEM); + struct vge_dmamap_arg ctx; + struct vge_txdesc *txd; + struct vge_rxdesc *rxd; + bus_addr_t lowaddr, tx_ring_end, rx_ring_end; + int error, i; + + lowaddr = BUS_SPACE_MAXADDR; + +again: + /* Create parent ring tag. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->vge_dev),/* parent */ + 1, 0, /* algnmnt, boundary */ + lowaddr, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ + 0, /* nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->vge_cdata.vge_ring_tag); + if (error != 0) { + device_printf(sc->vge_dev, + "could not create parent DMA tag.\n"); + goto fail; } - /* - * Allocate map for TX descriptor list. - */ - error = bus_dma_tag_create(sc->vge_parent_tag, VGE_RING_ALIGN, - 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, - NULL, VGE_TX_LIST_SZ, 1, VGE_TX_LIST_SZ, BUS_DMA_ALLOCNOW, - NULL, NULL, &sc->vge_ldata.vge_tx_list_tag); - if (error) { - device_printf(dev, "could not allocate dma tag\n"); - return (ENOMEM); + /* Create tag for Tx ring. */ + error = bus_dma_tag_create(sc->vge_cdata.vge_ring_tag,/* parent */ + VGE_TX_RING_ALIGN, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + VGE_TX_LIST_SZ, /* maxsize */ + 1, /* nsegments */ + VGE_TX_LIST_SZ, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->vge_cdata.vge_tx_ring_tag); + if (error != 0) { + device_printf(sc->vge_dev, + "could not allocate Tx ring DMA tag.\n"); + goto fail; } - /* Allocate DMA'able memory for the TX ring */ - - error = bus_dmamem_alloc(sc->vge_ldata.vge_tx_list_tag, - (void **)&sc->vge_ldata.vge_tx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, - &sc->vge_ldata.vge_tx_list_map); - if (error) - return (ENOMEM); + /* Create tag for Rx ring. */ + error = bus_dma_tag_create(sc->vge_cdata.vge_ring_tag,/* parent */ + VGE_RX_RING_ALIGN, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + VGE_RX_LIST_SZ, /* maxsize */ + 1, /* nsegments */ + VGE_RX_LIST_SZ, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->vge_cdata.vge_rx_ring_tag); + if (error != 0) { + device_printf(sc->vge_dev, + "could not allocate Rx ring DMA tag.\n"); + goto fail; + } - /* Load the map for the TX ring. */ + /* Allocate DMA'able memory and load the DMA map for Tx ring. */ + error = bus_dmamem_alloc(sc->vge_cdata.vge_tx_ring_tag, + (void **)&sc->vge_rdata.vge_tx_ring, + BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, + &sc->vge_cdata.vge_tx_ring_map); + if (error != 0) { + device_printf(sc->vge_dev, + "could not allocate DMA'able memory for Tx ring.\n"); + goto fail; + } - error = bus_dmamap_load(sc->vge_ldata.vge_tx_list_tag, - sc->vge_ldata.vge_tx_list_map, sc->vge_ldata.vge_tx_list, - VGE_TX_LIST_SZ, vge_dma_map_addr, - &sc->vge_ldata.vge_tx_list_addr, BUS_DMA_NOWAIT); + ctx.vge_busaddr = 0; + error = bus_dmamap_load(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_cdata.vge_tx_ring_map, sc->vge_rdata.vge_tx_ring, + VGE_TX_LIST_SZ, vge_dmamap_cb, &ctx, BUS_DMA_NOWAIT); + if (error != 0 || ctx.vge_busaddr == 0) { + device_printf(sc->vge_dev, + "could not load DMA'able memory for Tx ring.\n"); + goto fail; + } + sc->vge_rdata.vge_tx_ring_paddr = ctx.vge_busaddr; - /* Create DMA maps for TX buffers */ + /* Allocate DMA'able memory and load the DMA map for Rx ring. */ + error = bus_dmamem_alloc(sc->vge_cdata.vge_rx_ring_tag, + (void **)&sc->vge_rdata.vge_rx_ring, + BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, + &sc->vge_cdata.vge_rx_ring_map); + if (error != 0) { + device_printf(sc->vge_dev, + "could not allocate DMA'able memory for Rx ring.\n"); + goto fail; + } - for (i = 0; i < VGE_TX_DESC_CNT; i++) { - error = bus_dmamap_create(sc->vge_ldata.vge_mtag, 0, - &sc->vge_ldata.vge_tx_dmamap[i]); - if (error) { - device_printf(dev, "can't create DMA map for TX\n"); - return (ENOMEM); - } + ctx.vge_busaddr = 0; + error = bus_dmamap_load(sc->vge_cdata.vge_rx_ring_tag, + sc->vge_cdata.vge_rx_ring_map, sc->vge_rdata.vge_rx_ring, + VGE_RX_LIST_SZ, vge_dmamap_cb, &ctx, BUS_DMA_NOWAIT); + if (error != 0 || ctx.vge_busaddr == 0) { + device_printf(sc->vge_dev, + "could not load DMA'able memory for Rx ring.\n"); + goto fail; } + sc->vge_rdata.vge_rx_ring_paddr = ctx.vge_busaddr; - /* - * Allocate map for RX descriptor list. - */ - error = bus_dma_tag_create(sc->vge_parent_tag, VGE_RING_ALIGN, - 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, - NULL, VGE_TX_LIST_SZ, 1, VGE_TX_LIST_SZ, BUS_DMA_ALLOCNOW, - NULL, NULL, &sc->vge_ldata.vge_rx_list_tag); - if (error) { - device_printf(dev, "could not allocate dma tag\n"); - return (ENOMEM); + /* Tx/Rx descriptor queue should reside within 4GB boundary. */ + tx_ring_end = sc->vge_rdata.vge_tx_ring_paddr + VGE_TX_LIST_SZ; + rx_ring_end = sc->vge_rdata.vge_rx_ring_paddr + VGE_RX_LIST_SZ; + if ((VGE_ADDR_HI(tx_ring_end) != + VGE_ADDR_HI(sc->vge_rdata.vge_tx_ring_paddr)) || + (VGE_ADDR_HI(rx_ring_end) != + VGE_ADDR_HI(sc->vge_rdata.vge_rx_ring_paddr)) || + VGE_ADDR_HI(tx_ring_end) != VGE_ADDR_HI(rx_ring_end)) { + device_printf(sc->vge_dev, "4GB boundary crossed, " + "switching to 32bit DMA address mode.\n"); + vge_dma_free(sc); + /* Limit DMA address space to 32bit and try again. */ + lowaddr = BUS_SPACE_MAXADDR_32BIT; + goto again; + } + + /* Create parent buffer tag. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->vge_dev),/* parent */ + 1, 0, /* algnmnt, boundary */ + VGE_BUF_DMA_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ + 0, /* nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->vge_cdata.vge_buffer_tag); + if (error != 0) { + device_printf(sc->vge_dev, + "could not create parent buffer DMA tag.\n"); + goto fail; } - /* Allocate DMA'able memory for the RX ring */ + /* Create tag for Tx buffers. */ + error = bus_dma_tag_create(sc->vge_cdata.vge_buffer_tag,/* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES * VGE_MAXTXSEGS, /* maxsize */ + VGE_MAXTXSEGS, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->vge_cdata.vge_tx_tag); + if (error != 0) { + device_printf(sc->vge_dev, "could not create Tx DMA tag.\n"); + goto fail; + } - error = bus_dmamem_alloc(sc->vge_ldata.vge_rx_list_tag, - (void **)&sc->vge_ldata.vge_rx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, - &sc->vge_ldata.vge_rx_list_map); - if (error) - return (ENOMEM); + /* Create tag for Rx buffers. */ + error = bus_dma_tag_create(sc->vge_cdata.vge_buffer_tag,/* parent */ + VGE_RX_BUF_ALIGN, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, /* maxsize */ + 1, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->vge_cdata.vge_rx_tag); + if (error != 0) { + device_printf(sc->vge_dev, "could not create Rx DMA tag.\n"); + goto fail; + } - /* Load the map for the RX ring. */ + /* Create DMA maps for Tx buffers. */ + for (i = 0; i < VGE_TX_DESC_CNT; i++) { + txd = &sc->vge_cdata.vge_txdesc[i]; + txd->tx_m = NULL; + txd->tx_dmamap = NULL; + error = bus_dmamap_create(sc->vge_cdata.vge_tx_tag, 0, + &txd->tx_dmamap); + if (error != 0) { + device_printf(sc->vge_dev, + "could not create Tx dmamap.\n"); + goto fail; + } + } + /* Create DMA maps for Rx buffers. */ + if ((error = bus_dmamap_create(sc->vge_cdata.vge_rx_tag, 0, + &sc->vge_cdata.vge_rx_sparemap)) != 0) { + device_printf(sc->vge_dev, + "could not create spare Rx dmamap.\n"); + goto fail; + } + for (i = 0; i < VGE_RX_DESC_CNT; i++) { + rxd = &sc->vge_cdata.vge_rxdesc[i]; + rxd->rx_m = NULL; + rxd->rx_dmamap = NULL; + error = bus_dmamap_create(sc->vge_cdata.vge_rx_tag, 0, + &rxd->rx_dmamap); + if (error != 0) { + device_printf(sc->vge_dev, + "could not create Rx dmamap.\n"); + goto fail; + } + } - error = bus_dmamap_load(sc->vge_ldata.vge_rx_list_tag, - sc->vge_ldata.vge_rx_list_map, sc->vge_ldata.vge_rx_list, - VGE_TX_LIST_SZ, vge_dma_map_addr, - &sc->vge_ldata.vge_rx_list_addr, BUS_DMA_NOWAIT); +fail: + return (error); +} - /* Create DMA maps for RX buffers */ +static void +vge_dma_free(sc) + struct vge_softc *sc; +{ + struct vge_txdesc *txd; + struct vge_rxdesc *rxd; + int i; - for (i = 0; i < VGE_RX_DESC_CNT; i++) { - error = bus_dmamap_create(sc->vge_ldata.vge_mtag, 0, - &sc->vge_ldata.vge_rx_dmamap[i]); - if (error) { - device_printf(dev, "can't create DMA map for RX\n"); - return (ENOMEM); + /* Tx ring. */ + if (sc->vge_cdata.vge_tx_ring_tag != NULL) { + if (sc->vge_cdata.vge_tx_ring_map) + bus_dmamap_unload(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_cdata.vge_tx_ring_map); + if (sc->vge_cdata.vge_tx_ring_map && + sc->vge_rdata.vge_tx_ring) + bus_dmamem_free(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_rdata.vge_tx_ring, + sc->vge_cdata.vge_tx_ring_map); + sc->vge_rdata.vge_tx_ring = NULL; + sc->vge_cdata.vge_tx_ring_map = NULL; + bus_dma_tag_destroy(sc->vge_cdata.vge_tx_ring_tag); + sc->vge_cdata.vge_tx_ring_tag = NULL; + } + /* Rx ring. */ + if (sc->vge_cdata.vge_rx_ring_tag != NULL) { + if (sc->vge_cdata.vge_rx_ring_map) + bus_dmamap_unload(sc->vge_cdata.vge_rx_ring_tag, + sc->vge_cdata.vge_rx_ring_map); + if (sc->vge_cdata.vge_rx_ring_map && + sc->vge_rdata.vge_rx_ring) + bus_dmamem_free(sc->vge_cdata.vge_rx_ring_tag, + sc->vge_rdata.vge_rx_ring, + sc->vge_cdata.vge_rx_ring_map); + sc->vge_rdata.vge_rx_ring = NULL; + sc->vge_cdata.vge_rx_ring_map = NULL; + bus_dma_tag_destroy(sc->vge_cdata.vge_rx_ring_tag); + sc->vge_cdata.vge_rx_ring_tag = NULL; + } + /* Tx buffers. */ + if (sc->vge_cdata.vge_tx_tag != NULL) { + for (i = 0; i < VGE_TX_DESC_CNT; i++) { + txd = &sc->vge_cdata.vge_txdesc[i]; + if (txd->tx_dmamap != NULL) { + bus_dmamap_destroy(sc->vge_cdata.vge_tx_tag, + txd->tx_dmamap); + txd->tx_dmamap = NULL; + } } + bus_dma_tag_destroy(sc->vge_cdata.vge_tx_tag); + sc->vge_cdata.vge_tx_tag = NULL; + } + /* Rx buffers. */ + if (sc->vge_cdata.vge_rx_tag != NULL) { + for (i = 0; i < VGE_RX_DESC_CNT; i++) { + rxd = &sc->vge_cdata.vge_rxdesc[i]; + if (rxd->rx_dmamap != NULL) { + bus_dmamap_destroy(sc->vge_cdata.vge_rx_tag, + rxd->rx_dmamap); + rxd->rx_dmamap = NULL; + } + } + if (sc->vge_cdata.vge_rx_sparemap != NULL) { + bus_dmamap_destroy(sc->vge_cdata.vge_rx_tag, + sc->vge_cdata.vge_rx_sparemap); + sc->vge_cdata.vge_rx_sparemap = NULL; + } + bus_dma_tag_destroy(sc->vge_cdata.vge_rx_tag); + sc->vge_cdata.vge_rx_tag = NULL; + } + + if (sc->vge_cdata.vge_buffer_tag != NULL) { + bus_dma_tag_destroy(sc->vge_cdata.vge_buffer_tag); + sc->vge_cdata.vge_buffer_tag = NULL; + } + if (sc->vge_cdata.vge_ring_tag != NULL) { + bus_dma_tag_destroy(sc->vge_cdata.vge_ring_tag); + sc->vge_cdata.vge_ring_tag = NULL; } - - return (0); } /* @@ -964,25 +1042,7 @@ vge_attach(dev) */ vge_read_eeprom(sc, (caddr_t)eaddr, VGE_EE_EADDR, 3, 0); - /* - * Allocate the parent bus DMA tag appropriate for PCI. - */ -#define VGE_NSEG_NEW 32 - error = bus_dma_tag_create(NULL, /* parent */ - 1, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - MAXBSIZE, VGE_NSEG_NEW, /* maxsize, nsegments */ - BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ - BUS_DMA_ALLOCNOW, /* flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->vge_parent_tag); - if (error) - goto fail; - - error = vge_allocmem(dev, sc); - + error = vge_dma_alloc(sc); if (error) goto fail; @@ -1054,7 +1114,6 @@ vge_detach(dev) { struct vge_softc *sc; struct ifnet *ifp; - int i; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->vge_mtx), ("vge mutex not initialized")); @@ -1087,97 +1146,93 @@ vge_detach(dev) if (ifp) if_free(ifp); - /* Unload and free the RX DMA ring memory and map */ + vge_dma_free(sc); + mtx_destroy(&sc->vge_mtx); - if (sc->vge_ldata.vge_rx_list_tag) { - bus_dmamap_unload(sc->vge_ldata.vge_rx_list_tag, - sc->vge_ldata.vge_rx_list_map); - bus_dmamem_free(sc->vge_ldata.vge_rx_list_tag, - sc->vge_ldata.vge_rx_list, - sc->vge_ldata.vge_rx_list_map); - bus_dma_tag_destroy(sc->vge_ldata.vge_rx_list_tag); - } - - /* Unload and free the TX DMA ring memory and map */ - - if (sc->vge_ldata.vge_tx_list_tag) { - bus_dmamap_unload(sc->vge_ldata.vge_tx_list_tag, - sc->vge_ldata.vge_tx_list_map); - bus_dmamem_free(sc->vge_ldata.vge_tx_list_tag, - sc->vge_ldata.vge_tx_list, - sc->vge_ldata.vge_tx_list_map); - bus_dma_tag_destroy(sc->vge_ldata.vge_tx_list_tag); - } - - /* Destroy all the RX and TX buffer maps */ - - if (sc->vge_ldata.vge_mtag) { - for (i = 0; i < VGE_TX_DESC_CNT; i++) - bus_dmamap_destroy(sc->vge_ldata.vge_mtag, - sc->vge_ldata.vge_tx_dmamap[i]); - for (i = 0; i < VGE_RX_DESC_CNT; i++) - bus_dmamap_destroy(sc->vge_ldata.vge_mtag, - sc->vge_ldata.vge_rx_dmamap[i]); - bus_dma_tag_destroy(sc->vge_ldata.vge_mtag); - } + return (0); +} - if (sc->vge_parent_tag) - bus_dma_tag_destroy(sc->vge_parent_tag); +static void +vge_discard_rxbuf(sc, prod) + struct vge_softc *sc; + int prod; +{ + struct vge_rxdesc *rxd; + int i; - mtx_destroy(&sc->vge_mtx); + rxd = &sc->vge_cdata.vge_rxdesc[prod]; + rxd->rx_desc->vge_sts = 0; + rxd->rx_desc->vge_ctl = 0; - return (0); + /* + * Note: the manual fails to document the fact that for + * proper opration, the driver needs to replentish the RX + * DMA ring 4 descriptors at a time (rather than one at a + * time, like most chips). We can allocate the new buffers + * but we should not set the OWN bits until we're ready + * to hand back 4 of them in one shot. + */ + if ((prod % VGE_RXCHUNK) == (VGE_RXCHUNK - 1)) { + for (i = VGE_RXCHUNK; i > 0; i--) { + rxd->rx_desc->vge_sts = htole32(VGE_RDSTS_OWN); + rxd = rxd->rxd_prev; + } + sc->vge_cdata.vge_rx_commit += VGE_RXCHUNK; + } } static int -vge_newbuf(sc, idx, m) +vge_newbuf(sc, prod) struct vge_softc *sc; - int idx; - struct mbuf *m; + int prod; { - struct vge_dmaload_arg arg; - struct mbuf *n = NULL; - int i, error; - - if (m == NULL) { - n = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); - if (n == NULL) - return (ENOBUFS); - m = n; - } else - m->m_data = m->m_ext.ext_buf; - - -#ifdef VGE_FIXUP_RX - /* - * This is part of an evil trick to deal with non-x86 platforms. - * The VIA chip requires RX buffers to be aligned on 32-bit - * boundaries, but that will hose non-x86 machines. To get around - * this, we leave some empty space at the start of each buffer - * and for non-x86 hosts, we copy the buffer back two bytes - * to achieve word alignment. This is slightly more efficient - * than allocating a new buffer, copying the contents, and - * discarding the old buffer. + struct vge_rxdesc *rxd; + struct mbuf *m; + bus_dma_segment_t segs[1]; + bus_dmamap_t map; + int i, nsegs; + + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + return (ENOBUFS); + /* + * This is part of an evil trick to deal with strict-alignment + * architectures. The VIA chip requires RX buffers to be aligned + * on 32-bit boundaries, but that will hose strict-alignment + * architectures. To get around this, we leave some empty space + * at the start of each buffer and for non-strict-alignment hosts, + * we copy the buffer back two bytes to achieve word alignment. + * This is slightly more efficient than allocating a new buffer, + * copying the contents, and discarding the old buffer. */ - m->m_len = m->m_pkthdr.len = MCLBYTES - VGE_ETHER_ALIGN; - m_adj(m, VGE_ETHER_ALIGN); -#else m->m_len = m->m_pkthdr.len = MCLBYTES; -#endif + m_adj(m, VGE_RX_BUF_ALIGN); - arg.sc = sc; - arg.vge_idx = idx; - arg.vge_maxsegs = 1; - arg.vge_flags = 0; - - error = bus_dmamap_load_mbuf(sc->vge_ldata.vge_mtag, - sc->vge_ldata.vge_rx_dmamap[idx], m, vge_dma_map_rx_desc, - &arg, BUS_DMA_NOWAIT); - if (error || arg.vge_maxsegs != 1) { - if (n != NULL) - m_freem(n); - return (ENOMEM); + if (bus_dmamap_load_mbuf_sg(sc->vge_cdata.vge_rx_tag, + sc->vge_cdata.vge_rx_sparemap, m, segs, &nsegs, 0) != 0) { + m_freem(m); + return (ENOBUFS); } + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); + + rxd = &sc->vge_cdata.vge_rxdesc[prod]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc->vge_cdata.vge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->vge_cdata.vge_rx_tag, rxd->rx_dmamap); + } + map = rxd->rx_dmamap; + rxd->rx_dmamap = sc->vge_cdata.vge_rx_sparemap; + sc->vge_cdata.vge_rx_sparemap = map; + bus_dmamap_sync(sc->vge_cdata.vge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + rxd->rx_m = m; + + rxd->rx_desc->vge_sts = 0; + rxd->rx_desc->vge_ctl = 0; + rxd->rx_desc->vge_addrlo = htole32(VGE_ADDR_LO(segs[0].ds_addr)); + rxd->rx_desc->vge_addrhi = htole32(VGE_ADDR_HI(segs[0].ds_addr) | + (VGE_BUFLEN(segs[0].ds_len) << 16) | VGE_RXDESC_I); /* * Note: the manual fails to document the fact that for @@ -1187,73 +1242,127 @@ vge_newbuf(sc, idx, m) * but we should not set the OWN bits until we're ready * to hand back 4 of them in one shot. */ - -#define VGE_RXCHUNK 4 - sc->vge_rx_consumed++; - if (sc->vge_rx_consumed == VGE_RXCHUNK) { - for (i = idx; i != idx - sc->vge_rx_consumed; i--) - sc->vge_ldata.vge_rx_list[i].vge_sts |= - htole32(VGE_RDSTS_OWN); - sc->vge_rx_consumed = 0; + if ((prod % VGE_RXCHUNK) == (VGE_RXCHUNK - 1)) { + for (i = VGE_RXCHUNK; i > 0; i--) { + rxd->rx_desc->vge_sts = htole32(VGE_RDSTS_OWN); + rxd = rxd->rxd_prev; + } + sc->vge_cdata.vge_rx_commit += VGE_RXCHUNK; } - sc->vge_ldata.vge_rx_mbuf[idx] = m; - - bus_dmamap_sync(sc->vge_ldata.vge_mtag, - sc->vge_ldata.vge_rx_dmamap[idx], - BUS_DMASYNC_PREREAD); - return (0); } static int vge_tx_list_init(sc) - struct vge_softc *sc; + struct vge_softc *sc; { - bzero ((char *)sc->vge_ldata.vge_tx_list, VGE_TX_LIST_SZ); - bzero ((char *)&sc->vge_ldata.vge_tx_mbuf, - (VGE_TX_DESC_CNT * sizeof(struct mbuf *))); - - bus_dmamap_sync(sc->vge_ldata.vge_tx_list_tag, - sc->vge_ldata.vge_tx_list_map, BUS_DMASYNC_PREWRITE); - sc->vge_ldata.vge_tx_prodidx = 0; - sc->vge_ldata.vge_tx_considx = 0; - sc->vge_ldata.vge_tx_free = VGE_TX_DESC_CNT; + struct vge_ring_data *rd; + struct vge_txdesc *txd; + int i; + + VGE_LOCK_ASSERT(sc); + + sc->vge_cdata.vge_tx_prodidx = 0; + sc->vge_cdata.vge_tx_considx = 0; + sc->vge_cdata.vge_tx_cnt = 0; + + rd = &sc->vge_rdata; + bzero(rd->vge_tx_ring, VGE_TX_LIST_SZ); + for (i = 0; i < VGE_TX_DESC_CNT; i++) { + txd = &sc->vge_cdata.vge_txdesc[i]; + txd->tx_m = NULL; + txd->tx_desc = &rd->vge_tx_ring[i]; + } + + bus_dmamap_sync(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_cdata.vge_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } static int vge_rx_list_init(sc) - struct vge_softc *sc; + struct vge_softc *sc; { + struct vge_ring_data *rd; + struct vge_rxdesc *rxd; int i; - bzero ((char *)sc->vge_ldata.vge_rx_list, VGE_RX_LIST_SZ); - bzero ((char *)&sc->vge_ldata.vge_rx_mbuf, - (VGE_RX_DESC_CNT * sizeof(struct mbuf *))); + VGE_LOCK_ASSERT(sc); - sc->vge_rx_consumed = 0; + sc->vge_cdata.vge_rx_prodidx = 0; + sc->vge_cdata.vge_head = NULL; + sc->vge_cdata.vge_tail = NULL; + sc->vge_cdata.vge_rx_commit = 0; + rd = &sc->vge_rdata; + bzero(rd->vge_rx_ring, VGE_RX_LIST_SZ); for (i = 0; i < VGE_RX_DESC_CNT; i++) { - if (vge_newbuf(sc, i, NULL) == ENOBUFS) + rxd = &sc->vge_cdata.vge_rxdesc[i]; + rxd->rx_m = NULL; + rxd->rx_desc = &rd->vge_rx_ring[i]; + if (i == 0) + rxd->rxd_prev = + &sc->vge_cdata.vge_rxdesc[VGE_RX_DESC_CNT - 1]; + else + rxd->rxd_prev = &sc->vge_cdata.vge_rxdesc[i - 1]; + if (vge_newbuf(sc, i) != 0) return (ENOBUFS); } - /* Flush the RX descriptors */ + bus_dmamap_sync(sc->vge_cdata.vge_rx_ring_tag, + sc->vge_cdata.vge_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(sc->vge_ldata.vge_rx_list_tag, - sc->vge_ldata.vge_rx_list_map, - BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); - - sc->vge_ldata.vge_rx_prodidx = 0; - sc->vge_rx_consumed = 0; - sc->vge_head = sc->vge_tail = NULL; + sc->vge_cdata.vge_rx_commit = 0; return (0); } -#ifdef VGE_FIXUP_RX +static void +vge_freebufs(sc) + struct vge_softc *sc; +{ + struct vge_txdesc *txd; + struct vge_rxdesc *rxd; + struct ifnet *ifp; + int i; + + VGE_LOCK_ASSERT(sc); + + ifp = sc->vge_ifp; + /* + * Free RX and TX mbufs still in the queues. + */ + for (i = 0; i < VGE_RX_DESC_CNT; i++) { + rxd = &sc->vge_cdata.vge_rxdesc[i]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc->vge_cdata.vge_rx_tag, + rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->vge_cdata.vge_rx_tag, + rxd->rx_dmamap); + m_freem(rxd->rx_m); + rxd->rx_m = NULL; + } + } + + for (i = 0; i < VGE_TX_DESC_CNT; i++) { + txd = &sc->vge_cdata.vge_txdesc[i]; + if (txd->tx_m != NULL) { + bus_dmamap_sync(sc->vge_cdata.vge_tx_tag, + txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->vge_cdata.vge_tx_tag, + txd->tx_dmamap); + m_freem(txd->tx_m); + txd->tx_m = NULL; + ifp->if_oerrors++; + } + } +} + +#ifndef __NO_STRICT_ALIGNMENT static __inline void vge_fixup_rx(m) struct mbuf *m; @@ -1268,8 +1377,6 @@ vge_fixup_rx(m) *dst++ = *src++; m->m_data -= ETHER_ALIGN; - - return; } #endif @@ -1277,50 +1384,40 @@ vge_fixup_rx(m) * RX handler. We support the reception of jumbo frames that have * been fragmented across multiple 2K mbuf cluster buffers. */ -static void -vge_rxeof(sc) +static int +vge_rxeof(sc, count) struct vge_softc *sc; + int count; { struct mbuf *m; struct ifnet *ifp; - int i, total_len; - int lim = 0; + int prod, prog, total_len; + struct vge_rxdesc *rxd; struct vge_rx_desc *cur_rx; - u_int32_t rxstat, rxctl; + uint32_t rxstat, rxctl; VGE_LOCK_ASSERT(sc); - ifp = sc->vge_ifp; - i = sc->vge_ldata.vge_rx_prodidx; - - /* Invalidate the descriptor memory */ - - bus_dmamap_sync(sc->vge_ldata.vge_rx_list_tag, - sc->vge_ldata.vge_rx_list_map, - BUS_DMASYNC_POSTREAD); - - while (!VGE_OWN(&sc->vge_ldata.vge_rx_list[i])) { -#ifdef DEVICE_POLLING - if (ifp->if_capenable & IFCAP_POLLING) { - if (sc->rxcycles <= 0) - break; - sc->rxcycles--; - } -#endif *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***