From takao@milkcup.jp  Sun Dec 13 01:08:14 2009
Return-Path: <takao@milkcup.jp>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id C7DF21065670
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 13 Dec 2009 01:08:14 +0000 (UTC)
	(envelope-from takao@milkcup.jp)
Received: from brian.milkcup.jp (ns.milkcup.jp [IPv6:2001:240:503:2::1])
	by mx1.freebsd.org (Postfix) with ESMTP id 994D08FC0C
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 13 Dec 2009 01:08:14 +0000 (UTC)
Received: by brian.milkcup.jp (Postfix, from userid 500)
	id A97802E06C; Sun, 13 Dec 2009 10:08:12 +0900 (JST)
Message-Id: <20091213010812.A97802E06C@brian.milkcup.jp>
Date: Sun, 13 Dec 2009 10:08:12 +0900 (JST)
From: Takao TAGAMI <tahkun-send-pr@milkcup.jp>
Reply-To: Takao TAGAMI <tahkun-send-pr@milkcup.jp>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: vge(4) problem on 8.0-RELEASE
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         141414
>Category:       kern
>Synopsis:       [vge] vge(4) problem on 8.0-RELEASE
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    yongari
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Dec 13 01:10:02 UTC 2009
>Closed-Date:    Sat Jan 09 01:37:09 UTC 2010
>Last-Modified:  Sat Jan 09 01:37:09 UTC 2010
>Originator:     Takao TAGAMI
>Release:        FreeBSD 7.2-RELEASE-p5 i386
>Organization:
milkcup
>Environment:
System: FreeBSD  8.0-RELEASE FreeBSD 8.0-RELEASE #19: Thu Dec  3 11:42:38 JST 2009     root@brian.milkcup.jp:/usr/obj/usr/src/sys/BRIAN  i386


	
>Description:
FreeBSD was upgraded from 7.2-RELEASE to 8.0-RELEASE.
Trouble is caused in vge(4) that is the network interface.
There is usually no problem in the operation about login in ssh etc.
However, the browse in Apache and the update by cvsup cannot be normally communicated. 

It is unquestionable for Apache in a light page. 
The display of a browser falls into disorder irregularly when heavy pages of PHP, Style Sheet, and the many images, etc. are opened. 

It is not possible to update it by display the following being output for cvsup. 
Even if time and the server that does cvsup are changed, it is the same. 

# cvsup /usr/local/etc/cvsup/standard-supfile
Connected to cvsup2.jp.freebsd.org
Updating collection src-all/cvs
TreeList failed: Network write failure: Connection closed
Will retry at 07:23:30

There is the change though media and mediaopt were variously changed from autoselect with ifconfig. 

The myriad plan is exhausted though trouble occurs when upgrading to 8.0-RELEASE though there was no problem in 7.2-RELEASE, and it tried variously. 

vge0@pci0:0:14:0:       class=0x020000 card=0x01101106 chip=0x31191106 rev=0x11 hdr=0x00
    vendor     = 'VIA Technologies Inc'
    device     = 'VT6120/VT6121/VT6122 'Velocity' Gigabit Ethernet Controllers'
    class      = network
    subclass   = ethernet

vge0: <VIA Networking Gigabit Ethernet> port 0xf800-0xf8ff mem 0xfdffe000-0xfdffe0ff irq 18 at device 14.0 on pci0
miibus0: <MII bus> on vge0
ciphy0: <Cicada CS8201 10/100/1000TX PHY> PHY 1 on miibus0
ciphy0:  10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, 1000baseT, 1000baseT-FDX, auto
vge0: WARNING: using obsoleted if_watchdog interface
vge0: Ethernet address: 00:40:63:XX:XX:XX
vge0: [ITHREAD]

vge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=1b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING>
        ether 00:40:63:XX:XX:XX
        inet 192.168.12.100 netmask 0xffffff00 broadcast 192.168.12.255
        media: Ethernet autoselect (1000baseTX <full-duplex>)
        status: active

>How-To-Repeat:

>Fix:



>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->freebsd-net 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Sun Dec 13 02:04:19 UTC 2009 
Responsible-Changed-Why:  
Over to maintainer(s). 

http://www.freebsd.org/cgi/query-pr.cgi?pr=141414 
State-Changed-From-To: open->feedback 
State-Changed-By: yongari 
State-Changed-When: Mon Dec 14 22:40:55 UTC 2009 
State-Changed-Why:  
I think I fixed the issue in HEAD. Would you try vge(4) in HEAD? 
You can download the following latest vge(4) files from HEAD and it 
should build on 8.0-RELEASE. 
http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/dev/vge/if_vge.c 
http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/dev/vge/if_vgereg.h 
http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/dev/vge/if_vgevar.h 


Responsible-Changed-From-To: freebsd-net->yongari 
Responsible-Changed-By: yongari 
Responsible-Changed-When: Mon Dec 14 22:40:55 UTC 2009 
Responsible-Changed-Why:  
Grab. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=141414 
State-Changed-From-To: feedback->patched 
State-Changed-By: yongari 
State-Changed-When: Thu Dec 17 19:02:48 UTC 2009 
State-Changed-Why:  
Submitter confirms vge(4) in HEAD fixed the issue. 
Will MFC after more testing. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=141414 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/141414: commit references a PR
Date: Fri,  8 Jan 2010 21:38:03 +0000 (UTC)

 Author: yongari
 Date: Fri Jan  8 21:37:16 2010
 New Revision: 201832
 URL: http://svn.freebsd.org/changeset/base/201832
 
 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/8/sys/dev/vge/if_vge.c
   stable/8/sys/dev/vge/if_vgereg.h
   stable/8/sys/dev/vge/if_vgevar.h
 Directory Properties:
   stable/8/sys/   (props changed)
   stable/8/sys/amd64/include/xen/   (props changed)
   stable/8/sys/cddl/contrib/opensolaris/   (props changed)
   stable/8/sys/contrib/dev/acpica/   (props changed)
   stable/8/sys/contrib/pf/   (props changed)
   stable/8/sys/dev/xen/xenpci/   (props changed)
 
 Modified: stable/8/sys/dev/vge/if_vge.c
 ==============================================================================
 --- stable/8/sys/dev/vge/if_vge.c	Fri Jan  8 21:35:03 2010	(r201831)
 +++ stable/8/sys/dev/vge/if_vge.c	Fri Jan  8 21:37:16 2010	(r201832)
 @@ -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 int 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 *);
 @@ -547,6 +546,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. */
 @@ -662,249 +663,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);
  }
  
  /*
 @@ -961,25 +1039,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;
  
 @@ -1051,7 +1111,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"));
 @@ -1084,97 +1143,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
 @@ -1184,73 +1239,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;
 @@ -1265,8 +1374,6 @@ vge_fixup_rx(m)
  		*dst++ = *src++;
  
  	m->m_data -= ETHER_ALIGN;
 -
 -	return;
  }
  #endif
  
 @@ -1275,49 +1382,39 @@ vge_fixup_rx(m)
   * been fragmented across multiple 2K mbuf cluster buffers.
   */
  static int
 -vge_rxeof(sc)
 +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
 +	ifp = sc->vge_ifp;
  
 
 *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/141414: commit references a PR
Date: Fri,  8 Jan 2010 21:55:54 +0000 (UTC)

 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 ***
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: patched->closed 
State-Changed-By: yongari 
State-Changed-When: Sat Jan 9 01:36:48 UTC 2010 
State-Changed-Why:  
Close. Fix MFCed to stable/8 and stable/7. 
Thanks for reporting. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=141414 
>Unformatted:
