Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 21 Aug 2012 00:03:04 +0000 (UTC)
From:      Ed Maste <emaste@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r239472 - in stable/9/sys: dev/virtio dev/virtio/balloon dev/virtio/block dev/virtio/network dev/virtio/pci modules modules/virtio
Message-ID:  <201208210003.q7L0342r018598@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: emaste
Date: Tue Aug 21 00:03:04 2012
New Revision: 239472
URL: http://svn.freebsd.org/changeset/base/239472

Log:
  MFC r227652, r228301, r234270, r234349
  
  Sync Bryan Venteicher's virtio base, PCI front-end, and net/block/balloon
  drivers from HEAD.

Added:
  stable/9/sys/dev/virtio/
     - copied from r227652, head/sys/dev/virtio/
  stable/9/sys/modules/virtio/
     - copied from r227652, head/sys/modules/virtio/
Modified:
  stable/9/sys/dev/virtio/balloon/virtio_balloon.c
  stable/9/sys/dev/virtio/balloon/virtio_balloon.h
  stable/9/sys/dev/virtio/block/virtio_blk.c
  stable/9/sys/dev/virtio/block/virtio_blk.h
  stable/9/sys/dev/virtio/network/if_vtnet.c
  stable/9/sys/dev/virtio/network/if_vtnetvar.h
  stable/9/sys/dev/virtio/network/virtio_net.h
  stable/9/sys/dev/virtio/pci/virtio_pci.c
  stable/9/sys/dev/virtio/pci/virtio_pci.h
  stable/9/sys/dev/virtio/virtio.h
  stable/9/sys/dev/virtio/virtio_ring.h
  stable/9/sys/dev/virtio/virtqueue.c
  stable/9/sys/dev/virtio/virtqueue.h
  stable/9/sys/modules/Makefile
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/dev/   (props changed)
  stable/9/sys/modules/   (props changed)

Modified: stable/9/sys/dev/virtio/balloon/virtio_balloon.c
==============================================================================
--- head/sys/dev/virtio/balloon/virtio_balloon.c	Fri Nov 18 05:43:43 2011	(r227652)
+++ stable/9/sys/dev/virtio/balloon/virtio_balloon.c	Tue Aug 21 00:03:04 2012	(r239472)
@@ -122,6 +122,9 @@ static void	vtballoon_add_sysctl(struct 
  */
 #define VTBALLOON_PAGES_PER_REQUEST	256
 
+/* Must be able to fix all pages frames in one page (segment). */
+CTASSERT(VTBALLOON_PAGES_PER_REQUEST * sizeof(uint32_t) <= PAGE_SIZE);
+
 #define VTBALLOON_MTX(_sc)		&(_sc)->vtballoon_mtx
 #define VTBALLOON_LOCK_INIT(_sc, _name)	mtx_init(VTBALLOON_MTX((_sc)), _name, \
 					    "VirtIO Balloon Lock", MTX_SPIN)
@@ -138,7 +141,7 @@ static device_method_t vtballoon_methods
 	/* VirtIO methods. */
 	DEVMETHOD(virtio_config_change, vtballoon_config_change),
 
-	{ 0, 0 }
+	DEVMETHOD_END
 };
 
 static driver_t vtballoon_driver = {
@@ -402,13 +405,13 @@ vtballoon_send_page_frames(struct vtball
 
 	error = virtqueue_enqueue(vq, vq, &sg, 1, 0);
 	KASSERT(error == 0, ("error enqueuing page frames to virtqueue"));
+	virtqueue_notify(vq);
 
 	/*
 	 * Inflate and deflate operations are done synchronously. The
 	 * interrupt handler will wake us up.
 	 */
 	VTBALLOON_LOCK(sc);
-	virtqueue_notify(vq);
 
 	while ((c = virtqueue_dequeue(vq, NULL)) == NULL)
 		msleep_spin(sc, VTBALLOON_MTX(sc), "vtbspf", 0);
@@ -475,7 +478,6 @@ vtballoon_update_size(struct vtballoon_s
 	virtio_write_dev_config_4(sc->vtballoon_dev,
 	    offsetof(struct virtio_balloon_config, actual),
 	    htole32(sc->vtballoon_current_npages));
-
 }
 
 static int

Modified: stable/9/sys/dev/virtio/balloon/virtio_balloon.h
==============================================================================
--- head/sys/dev/virtio/balloon/virtio_balloon.h	Fri Nov 18 05:43:43 2011	(r227652)
+++ stable/9/sys/dev/virtio/balloon/virtio_balloon.h	Tue Aug 21 00:03:04 2012	(r239472)
@@ -1,7 +1,30 @@
-/*
+/*-
  * This header is BSD licensed so anyone can use the definitions to implement
  * compatible drivers/servers.
  *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
  * $FreeBSD$
  */
 

Modified: stable/9/sys/dev/virtio/block/virtio_blk.c
==============================================================================
--- head/sys/dev/virtio/block/virtio_blk.c	Fri Nov 18 05:43:43 2011	(r227652)
+++ stable/9/sys/dev/virtio/block/virtio_blk.c	Tue Aug 21 00:03:04 2012	(r239472)
@@ -70,8 +70,8 @@ struct vtblk_softc {
 	uint32_t		 vtblk_flags;
 #define VTBLK_FLAG_INDIRECT	0x0001
 #define VTBLK_FLAG_READONLY	0x0002
-#define VTBLK_FLAG_DETACHING	0x0004
-#define VTBLK_FLAG_SUSPENDED	0x0008
+#define VTBLK_FLAG_DETACH	0x0004
+#define VTBLK_FLAG_SUSPEND	0x0008
 #define VTBLK_FLAG_DUMPING	0x0010
 
 	struct virtqueue	*vtblk_vq;
@@ -82,14 +82,12 @@ struct vtblk_softc {
 	TAILQ_HEAD(, vtblk_request)
 				 vtblk_req_free;
 	TAILQ_HEAD(, vtblk_request)
-				 vtblk_req_ready;
+				vtblk_req_ready;
 
 	struct taskqueue	*vtblk_tq;
 	struct task		 vtblk_intr_task;
 
-	int			 vtblk_sector_size;
 	int			 vtblk_max_nsegs;
-	int			 vtblk_unit;
 	int			 vtblk_request_count;
 
 	struct vtblk_request	 vtblk_dump_request;
@@ -118,6 +116,13 @@ static int	vtblk_suspend(device_t);
 static int	vtblk_resume(device_t);
 static int	vtblk_shutdown(device_t);
 
+static int	vtblk_open(struct disk *);
+static int	vtblk_close(struct disk *);
+static int	vtblk_ioctl(struct disk *, u_long, void *, int,
+	            struct thread *);
+static int	vtblk_dump(void *, void *, vm_offset_t, off_t, size_t);
+static void	vtblk_strategy(struct bio *);
+
 static void	vtblk_negotiate_features(struct vtblk_softc *);
 static int	vtblk_maximum_segments(struct vtblk_softc *,
 		    struct virtio_blk_config *);
@@ -126,13 +131,7 @@ static void	vtblk_alloc_disk(struct vtbl
 		    struct virtio_blk_config *);
 static void	vtblk_create_disk(struct vtblk_softc *);
 
-static int	vtblk_open(struct disk *);
-static int	vtblk_close(struct disk *);
-static int	vtblk_ioctl(struct disk *, u_long, void *, int,
-		    struct thread *);
-static int	vtblk_dump(void *, void *, vm_offset_t, off_t, size_t);
-static void	vtblk_strategy(struct bio *);
-
+static int	vtblk_quiesce(struct vtblk_softc *);
 static void	vtblk_startio(struct vtblk_softc *);
 static struct vtblk_request * vtblk_bio_request(struct vtblk_softc *);
 static int	vtblk_execute_request(struct vtblk_softc *,
@@ -150,6 +149,7 @@ static int	vtblk_flush_dump(struct vtblk
 static int	vtblk_poll_request(struct vtblk_softc *,
 		    struct vtblk_request *);
 
+static void	vtblk_finish_completed(struct vtblk_softc *);
 static void	vtblk_drain_vq(struct vtblk_softc *, int);
 static void	vtblk_drain(struct vtblk_softc *);
 
@@ -163,7 +163,8 @@ static struct vtblk_request * vtblk_dequ
 static void	vtblk_enqueue_ready(struct vtblk_softc *,
 		    struct vtblk_request *);
 
-static void	vtblk_bio_error(struct bio *, int);
+static int	vtblk_request_error(struct vtblk_request *);
+static void	vtblk_finish_bio(struct bio *, int);
 
 /* Tunables. */
 static int vtblk_no_ident = 0;
@@ -185,16 +186,14 @@ TUNABLE_INT("hw.vtblk.no_ident", &vtblk_
 				mtx_init(VTBLK_MTX((_sc)), (_name), \
 				    "VTBLK Lock", MTX_DEF)
 #define VTBLK_LOCK(_sc)		mtx_lock(VTBLK_MTX((_sc)))
-#define VTBLK_TRYLOCK(_sc)	mtx_trylock(VTBLK_MTX((_sc)))
 #define VTBLK_UNLOCK(_sc)	mtx_unlock(VTBLK_MTX((_sc)))
 #define VTBLK_LOCK_DESTROY(_sc)	mtx_destroy(VTBLK_MTX((_sc)))
 #define VTBLK_LOCK_ASSERT(_sc)	mtx_assert(VTBLK_MTX((_sc)), MA_OWNED)
 #define VTBLK_LOCK_ASSERT_NOTOWNED(_sc) \
 				mtx_assert(VTBLK_MTX((_sc)), MA_NOTOWNED)
 
-#define VTBLK_BIO_SEGMENTS(_bp)	sglist_count((_bp)->bio_data, (_bp)->bio_bcount)
-
 #define VTBLK_DISK_NAME		"vtbd"
+#define	VTBLK_QUIESCE_TIMEOUT	(30 * hz)
 
 /*
  * Each block request uses at least two segments - one for the header
@@ -213,7 +212,7 @@ static device_method_t vtblk_methods[] =
 	DEVMETHOD(device_resume,	vtblk_resume),
 	DEVMETHOD(device_shutdown,	vtblk_shutdown),
 
-	{ 0, 0 }
+	DEVMETHOD_END
 };
 
 static driver_t vtblk_driver = {
@@ -281,7 +280,6 @@ vtblk_attach(device_t dev)
 
 	sc = device_get_softc(dev);
 	sc->vtblk_dev = dev;
-	sc->vtblk_unit = device_get_unit(dev);
 
 	VTBLK_LOCK_INIT(sc, device_get_nameunit(dev));
 
@@ -299,13 +297,8 @@ vtblk_attach(device_t dev)
 		sc->vtblk_flags |= VTBLK_FLAG_READONLY;
 
 	/* Get local copy of config. */
-	if (virtio_with_feature(dev, VIRTIO_BLK_F_TOPOLOGY) == 0) {
-		bzero(&blkcfg, sizeof(struct virtio_blk_config));
-		virtio_read_device_config(dev, 0, &blkcfg,
-		    offsetof(struct virtio_blk_config, physical_block_exp));
-	} else
-		virtio_read_device_config(dev, 0, &blkcfg,
-		    sizeof(struct virtio_blk_config));
+	virtio_read_device_config(dev, 0, &blkcfg,
+	    sizeof(struct virtio_blk_config));
 
 	/*
 	 * With the current sglist(9) implementation, it is not easy
@@ -323,11 +316,13 @@ vtblk_attach(device_t dev)
 	}
 
 	sc->vtblk_max_nsegs = vtblk_maximum_segments(sc, &blkcfg);
+        if (sc->vtblk_max_nsegs <= VTBLK_MIN_SEGMENTS) {
+		error = EINVAL;
+		device_printf(dev, "fewer than minimum number of segments "
+		    "allowed: %d\n", sc->vtblk_max_nsegs);
+		goto fail;
+	}
 
-	/*
-	 * Allocate working sglist. The number of segments may be too
-	 * large to safely store on the stack.
-	 */
 	sc->vtblk_sglist = sglist_alloc(sc->vtblk_max_nsegs, M_NOWAIT);
 	if (sc->vtblk_sglist == NULL) {
 		error = ENOMEM;
@@ -385,7 +380,7 @@ vtblk_detach(device_t dev)
 	sc = device_get_softc(dev);
 
 	VTBLK_LOCK(sc);
-	sc->vtblk_flags |= VTBLK_FLAG_DETACHING;
+	sc->vtblk_flags |= VTBLK_FLAG_DETACH;
 	if (device_is_attached(dev))
 		vtblk_stop(sc);
 	VTBLK_UNLOCK(sc);
@@ -417,15 +412,19 @@ static int
 vtblk_suspend(device_t dev)
 {
 	struct vtblk_softc *sc;
+	int error;
 
 	sc = device_get_softc(dev);
 
 	VTBLK_LOCK(sc);
-	sc->vtblk_flags |= VTBLK_FLAG_SUSPENDED;
-	/* TODO Wait for any inflight IO to complete? */
+	sc->vtblk_flags |= VTBLK_FLAG_SUSPEND;
+	/* XXX BMV: virtio_stop(), etc needed here? */
+	error = vtblk_quiesce(sc);
+	if (error)
+		sc->vtblk_flags &= ~VTBLK_FLAG_SUSPEND;
 	VTBLK_UNLOCK(sc);
 
-	return (0);
+	return (error);
 }
 
 static int
@@ -436,8 +435,9 @@ vtblk_resume(device_t dev)
 	sc = device_get_softc(dev);
 
 	VTBLK_LOCK(sc);
-	sc->vtblk_flags &= ~VTBLK_FLAG_SUSPENDED;
-	/* TODO Resume IO? */
+	/* XXX BMV: virtio_reinit(), etc needed here? */
+	sc->vtblk_flags &= ~VTBLK_FLAG_SUSPEND;
+	vtblk_startio(sc);
 	VTBLK_UNLOCK(sc);
 
 	return (0);
@@ -458,7 +458,7 @@ vtblk_open(struct disk *dp)
 	if ((sc = dp->d_drv1) == NULL)
 		return (ENXIO);
 
-	return (sc->vtblk_flags & VTBLK_FLAG_DETACHING ? ENXIO : 0);
+	return (sc->vtblk_flags & VTBLK_FLAG_DETACH ? ENXIO : 0);
 }
 
 static int
@@ -498,11 +498,7 @@ vtblk_dump(void *arg, void *virtual, vm_
 	if ((sc = dp->d_drv1) == NULL)
 		return (ENXIO);
 
-	if (VTBLK_TRYLOCK(sc) == 0) {
-		device_printf(sc->vtblk_dev,
-		    "softc already locked, cannot dump...\n");
-		return (EBUSY);
-	}
+	VTBLK_LOCK(sc);
 
 	if ((sc->vtblk_flags & VTBLK_FLAG_DUMPING) == 0) {
 		vtblk_prepare_dump(sc);
@@ -513,6 +509,10 @@ vtblk_dump(void *arg, void *virtual, vm_
 		error = vtblk_write_dump(sc, virtual, offset, length);
 	else if (virtual == NULL && offset == 0)
 		error = vtblk_flush_dump(sc);
+	else {
+		error = EINVAL;
+		sc->vtblk_flags &= ~VTBLK_FLAG_DUMPING;
+	}
 
 	VTBLK_UNLOCK(sc);
 
@@ -525,7 +525,7 @@ vtblk_strategy(struct bio *bp)
 	struct vtblk_softc *sc;
 
 	if ((sc = bp->bio_disk->d_drv1) == NULL) {
-		vtblk_bio_error(bp, EINVAL);
+		vtblk_finish_bio(bp, EINVAL);
 		return;
 	}
 
@@ -535,29 +535,37 @@ vtblk_strategy(struct bio *bp)
 	 */
 	if (sc->vtblk_flags & VTBLK_FLAG_READONLY &&
 	    (bp->bio_cmd == BIO_WRITE || bp->bio_cmd == BIO_FLUSH)) {
-		vtblk_bio_error(bp, EROFS);
+		vtblk_finish_bio(bp, EROFS);
 		return;
 	}
 
+#ifdef	INVARIANTS
 	/*
 	 * Prevent read/write buffers spanning too many segments from
 	 * getting into the queue. This should only trip if d_maxsize
 	 * was incorrectly set.
 	 */
 	if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
-		KASSERT(VTBLK_BIO_SEGMENTS(bp) <= sc->vtblk_max_nsegs -
-		    VTBLK_MIN_SEGMENTS,
+		int nsegs, max_nsegs;
+		
+		nsegs = sglist_count(bp->bio_data, bp->bio_bcount);
+		max_nsegs = sc->vtblk_max_nsegs - VTBLK_MIN_SEGMENTS;
+
+		KASSERT(nsegs <= max_nsegs,
 		    ("bio spanned too many segments: %d, max: %d",
-		    VTBLK_BIO_SEGMENTS(bp),
-		    sc->vtblk_max_nsegs - VTBLK_MIN_SEGMENTS));
+		    nsegs, max_nsegs));
 	}
+#endif
 
 	VTBLK_LOCK(sc);
-	if ((sc->vtblk_flags & VTBLK_FLAG_DETACHING) == 0) {
+	if (sc->vtblk_flags & VTBLK_FLAG_DETACH)
+		vtblk_finish_bio(bp, ENXIO);
+	else {
 		bioq_disksort(&sc->vtblk_bioq, bp);
-		vtblk_startio(sc);
-	} else
-		vtblk_bio_error(bp, ENXIO);
+
+		if ((sc->vtblk_flags & VTBLK_FLAG_SUSPEND) == 0)
+			vtblk_startio(sc);
+	}
 	VTBLK_UNLOCK(sc);
 }
 
@@ -622,7 +630,7 @@ vtblk_alloc_disk(struct vtblk_softc *sc,
 	dp->d_ioctl = vtblk_ioctl;
 	dp->d_strategy = vtblk_strategy;
 	dp->d_name = VTBLK_DISK_NAME;
-	dp->d_unit = sc->vtblk_unit;
+	dp->d_unit = device_get_unit(dev);
 	dp->d_drv1 = sc;
 
 	if ((sc->vtblk_flags & VTBLK_FLAG_READONLY) == 0)
@@ -632,10 +640,9 @@ vtblk_alloc_disk(struct vtblk_softc *sc,
 	dp->d_mediasize = blkcfg->capacity * 512;
 
 	if (virtio_with_feature(dev, VIRTIO_BLK_F_BLK_SIZE))
-		sc->vtblk_sector_size = blkcfg->blk_size;
+		dp->d_sectorsize = blkcfg->blk_size;
 	else
-		sc->vtblk_sector_size = 512;
-	dp->d_sectorsize = sc->vtblk_sector_size;
+		dp->d_sectorsize = 512;
 
 	/*
 	 * The VirtIO maximum I/O size is given in terms of segments.
@@ -685,6 +692,26 @@ vtblk_create_disk(struct vtblk_softc *sc
 	disk_create(dp, DISK_VERSION);
 }
 
+static int
+vtblk_quiesce(struct vtblk_softc *sc)
+{
+	int error;
+
+	error = 0;
+
+	VTBLK_LOCK_ASSERT(sc);
+
+	while (!virtqueue_empty(sc->vtblk_vq)) {
+		if (mtx_sleep(&sc->vtblk_vq, VTBLK_MTX(sc), PRIBIO, "vtblkq",
+		    VTBLK_QUIESCE_TIMEOUT) == EWOULDBLOCK) {
+			error = EBUSY;
+			break;
+		}
+	}
+
+	return (error);
+}
+
 static void
 vtblk_startio(struct vtblk_softc *sc)
 {
@@ -697,9 +724,6 @@ vtblk_startio(struct vtblk_softc *sc)
 
 	VTBLK_LOCK_ASSERT(sc);
 
-	if (sc->vtblk_flags & VTBLK_FLAG_SUSPENDED)
-		return;
-
 	while (!virtqueue_full(vq)) {
 		if ((req = vtblk_dequeue_ready(sc)) == NULL)
 			req = vtblk_bio_request(sc);
@@ -752,9 +776,8 @@ vtblk_bio_request(struct vtblk_softc *sc
 		req->vbr_hdr.sector = bp->bio_offset / 512;
 		break;
 	default:
-		KASSERT(0, ("bio with unhandled cmd: %d", bp->bio_cmd));
-		req->vbr_hdr.type = -1;
-		break;
+		panic("%s: bio with unhandled cmd: %d", __FUNCTION__,
+		    bp->bio_cmd);
 	}
 
 	if (bp->bio_flags & BIO_ORDERED)
@@ -768,7 +791,7 @@ vtblk_execute_request(struct vtblk_softc
 {
 	struct sglist *sg;
 	struct bio *bp;
-	int writable, error;
+	int readable, writable, error;
 
 	sg = sc->vtblk_sglist;
 	bp = req->vbr_bp;
@@ -799,10 +822,9 @@ vtblk_execute_request(struct vtblk_softc
 	KASSERT(sg->sg_nseg >= VTBLK_MIN_SEGMENTS,
 	    ("fewer than min segments: %d", sg->sg_nseg));
 
-	error = virtqueue_enqueue(sc->vtblk_vq, req, sg,
-	    sg->sg_nseg - writable, writable);
+	readable = sg->sg_nseg - writable;
 
-	return (error);
+	return (virtqueue_enqueue(sc->vtblk_vq, req, sg, readable, writable));
 }
 
 static int
@@ -822,37 +844,23 @@ static void
 vtblk_intr_task(void *arg, int pending)
 {
 	struct vtblk_softc *sc;
-	struct vtblk_request *req;
 	struct virtqueue *vq;
-	struct bio *bp;
 
 	sc = arg;
 	vq = sc->vtblk_vq;
 
 	VTBLK_LOCK(sc);
-	if (sc->vtblk_flags & VTBLK_FLAG_DETACHING) {
+	if (sc->vtblk_flags & VTBLK_FLAG_DETACH) {
 		VTBLK_UNLOCK(sc);
 		return;
 	}
 
-	while ((req = virtqueue_dequeue(vq, NULL)) != NULL) {
-		bp = req->vbr_bp;
+	vtblk_finish_completed(sc);
 
-		if (req->vbr_ack == VIRTIO_BLK_S_OK)
-			bp->bio_resid = 0;
-		else {
-			bp->bio_flags |= BIO_ERROR;
-			if (req->vbr_ack == VIRTIO_BLK_S_UNSUPP)
-				bp->bio_error = ENOTSUP;
-			else
-				bp->bio_error = EIO;
-		}
-
-		biodone(bp);
-		vtblk_enqueue_request(sc, req);
-	}
-
-	vtblk_startio(sc);
+	if ((sc->vtblk_flags & VTBLK_FLAG_SUSPEND) == 0)
+		vtblk_startio(sc);
+	else
+		wakeup(&sc->vtblk_vq);
 
 	if (virtqueue_enable_intr(vq) != 0) {
 		virtqueue_disable_intr(vq);
@@ -905,9 +913,10 @@ vtblk_get_ident(struct vtblk_softc *sc)
 
 	VTBLK_LOCK(sc);
 	error = vtblk_poll_request(sc, req);
-	vtblk_enqueue_request(sc, req);
 	VTBLK_UNLOCK(sc);
 
+	vtblk_enqueue_request(sc, req);
+
 	if (error) {
 		device_printf(sc->vtblk_dev,
 		    "error getting device identifier: %d\n", error);
@@ -1006,17 +1015,35 @@ vtblk_poll_request(struct vtblk_softc *s
 	r = virtqueue_poll(vq, NULL);
 	KASSERT(r == req, ("unexpected request response"));
 
-	if (req->vbr_ack != VIRTIO_BLK_S_OK) {
-		error = req->vbr_ack == VIRTIO_BLK_S_UNSUPP ? ENOTSUP : EIO;
-		if (bootverbose)
-			device_printf(dev,
-			    "vtblk_poll_request: IO error: %d\n", error);
+	error = vtblk_request_error(req);
+	if (error && bootverbose) {
+		device_printf(dev, "vtblk_poll_request: IO error: %d\n",
+		    error);
 	}
 
 	return (error);
 }
 
 static void
+vtblk_finish_completed(struct vtblk_softc *sc)
+{
+	struct vtblk_request *req;
+	struct bio *bp;
+	int error;
+
+	while ((req = virtqueue_dequeue(sc->vtblk_vq, NULL)) != NULL) {
+		bp = req->vbr_bp;
+
+		error = vtblk_request_error(req);
+		if (error)
+			disk_err(bp, "hard error", -1, 1);
+
+		vtblk_finish_bio(bp, error);
+		vtblk_enqueue_request(sc, req);
+	}
+}
+
+static void
 vtblk_drain_vq(struct vtblk_softc *sc, int skip_done)
 {
 	struct virtqueue *vq;
@@ -1028,7 +1055,7 @@ vtblk_drain_vq(struct vtblk_softc *sc, i
 
 	while ((req = virtqueue_drain(vq, &last)) != NULL) {
 		if (!skip_done)
-			vtblk_bio_error(req->vbr_bp, ENXIO);
+			vtblk_finish_bio(req->vbr_bp, ENXIO);
 
 		vtblk_enqueue_request(sc, req);
 	}
@@ -1045,17 +1072,19 @@ vtblk_drain(struct vtblk_softc *sc)
 
 	bioq = &sc->vtblk_bioq;
 
-	if (sc->vtblk_vq != NULL)
+	if (sc->vtblk_vq != NULL) {
+		vtblk_finish_completed(sc);
 		vtblk_drain_vq(sc, 0);
+	}
 
 	while ((req = vtblk_dequeue_ready(sc)) != NULL) {
-		vtblk_bio_error(req->vbr_bp, ENXIO);
+		vtblk_finish_bio(req->vbr_bp, ENXIO);
 		vtblk_enqueue_request(sc, req);
 	}
 
 	while (bioq_first(bioq) != NULL) {
 		bp = bioq_takefirst(bioq);
-		vtblk_bio_error(bp, ENXIO);
+		vtblk_finish_bio(bp, ENXIO);
 	}
 
 	vtblk_free_requests(sc);
@@ -1065,9 +1094,9 @@ static int
 vtblk_alloc_requests(struct vtblk_softc *sc)
 {
 	struct vtblk_request *req;
-	int i, size;
+	int i, nreqs;
 
-	size = virtqueue_size(sc->vtblk_vq);
+	nreqs = virtqueue_size(sc->vtblk_vq);
 
 	/*
 	 * Preallocate sufficient requests to keep the virtqueue full. Each
@@ -1075,9 +1104,9 @@ vtblk_alloc_requests(struct vtblk_softc 
 	 * the number allocated when indirect descriptors are not available.
 	 */
 	if ((sc->vtblk_flags & VTBLK_FLAG_INDIRECT) == 0)
-		size /= VTBLK_MIN_SEGMENTS;
+		nreqs /= VTBLK_MIN_SEGMENTS;
 
-	for (i = 0; i < size; i++) {
+	for (i = 0; i < nreqs; i++) {
 		req = uma_zalloc(vtblk_req_zone, M_NOWAIT);
 		if (req == NULL)
 			return (ENOMEM);
@@ -1094,6 +1123,9 @@ vtblk_free_requests(struct vtblk_softc *
 {
 	struct vtblk_request *req;
 
+	KASSERT(TAILQ_EMPTY(&sc->vtblk_req_ready),
+	    ("ready requests left on queue"));
+
 	while ((req = vtblk_dequeue_request(sc)) != NULL) {
 		sc->vtblk_request_count--;
 		uma_zfree(vtblk_req_zone, req);
@@ -1141,9 +1173,35 @@ vtblk_enqueue_ready(struct vtblk_softc *
 	TAILQ_INSERT_HEAD(&sc->vtblk_req_ready, req, vbr_link);
 }
 
+static int
+vtblk_request_error(struct vtblk_request *req)
+{
+	int error;
+
+	switch (req->vbr_ack) {
+	case VIRTIO_BLK_S_OK:
+		error = 0;
+		break;
+	case VIRTIO_BLK_S_UNSUPP:
+		error = ENOTSUP;
+		break;
+	default:
+		error = EIO;
+		break;
+	}
+
+	return (error);
+}
+
 static void
-vtblk_bio_error(struct bio *bp, int error)
+vtblk_finish_bio(struct bio *bp, int error)
 {
 
-	biofinish(bp, NULL, error);
+	if (error) {
+		bp->bio_resid = bp->bio_bcount;
+		bp->bio_error = error;
+		bp->bio_flags |= BIO_ERROR;
+	}
+
+	biodone(bp);
 }

Modified: stable/9/sys/dev/virtio/block/virtio_blk.h
==============================================================================
--- head/sys/dev/virtio/block/virtio_blk.h	Fri Nov 18 05:43:43 2011	(r227652)
+++ stable/9/sys/dev/virtio/block/virtio_blk.h	Tue Aug 21 00:03:04 2012	(r239472)
@@ -1,7 +1,30 @@
-/*
+/*-
  * This header is BSD licensed so anyone can use the definitions to implement
  * compatible drivers/servers.
  *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
  * $FreeBSD$
  */
 
@@ -39,16 +62,6 @@ struct virtio_blk_config {
 
 	/* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */
 	uint32_t blk_size;
-
-	/* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY  */
-	/* exponent for physical block per logical block. */
-	uint8_t physical_block_exp;
-	/* alignment offset in logical blocks. */
-	uint8_t alignment_offset;
-	/* minimum I/O size without performance penalty in logical blocks. */
-	uint16_t min_io_size;
-	/* optimal sustained I/O size in logical blocks. */
-	uint32_t opt_io_size;
 } __packed;
 
 /*

Modified: stable/9/sys/dev/virtio/network/if_vtnet.c
==============================================================================
--- head/sys/dev/virtio/network/if_vtnet.c	Fri Nov 18 05:43:43 2011	(r227652)
+++ stable/9/sys/dev/virtio/network/if_vtnet.c	Tue Aug 21 00:03:04 2012	(r239472)
@@ -223,7 +223,7 @@ static device_method_t vtnet_methods[] =
 	/* VirtIO methods. */
 	DEVMETHOD(virtio_config_change, vtnet_config_change),
 
-	{ 0, 0 }
+	DEVMETHOD_END
 };
 
 static driver_t vtnet_driver = {
@@ -317,8 +317,20 @@ vtnet_attach(device_t dev)
 	if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VQ)) {
 		sc->vtnet_flags |= VTNET_FLAG_CTRL_VQ;
 
-		if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_RX))
+		if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_RX)) {
+			sc->vtnet_mac_filter = malloc(
+			    sizeof(struct vtnet_mac_filter), M_DEVBUF,
+			    M_NOWAIT | M_ZERO);
+			if (sc->vtnet_mac_filter == NULL) {
+				device_printf(dev,
+				    "cannot allocate mac filter table\n");
+				error = ENOMEM;
+				goto fail;
+			}
+
 			sc->vtnet_flags |= VTNET_FLAG_CTRL_RX;
+		}
+
 		if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VLAN))
 			sc->vtnet_flags |= VTNET_FLAG_VLAN_FILTER;
 	}
@@ -505,7 +517,12 @@ vtnet_detach(device_t dev)
 		sc->vtnet_vlan_detach = NULL;
 	}
 
-	if (ifp) {
+	if (sc->vtnet_mac_filter != NULL) {
+		free(sc->vtnet_mac_filter, M_DEVBUF);
+		sc->vtnet_mac_filter = NULL;
+	}
+
+	if (ifp != NULL) {
 		if_free(ifp);
 		sc->vtnet_ifp = NULL;
 	}
@@ -742,17 +759,11 @@ vtnet_update_link_status(struct vtnet_so
 
 	if (link && ((sc->vtnet_flags & VTNET_FLAG_LINK) == 0)) {
 		sc->vtnet_flags |= VTNET_FLAG_LINK;
-		if (bootverbose)
-			device_printf(dev, "Link is up\n");
-
 		if_link_state_change(ifp, LINK_STATE_UP);
 		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
 			vtnet_start_locked(ifp);
 	} else if (!link && (sc->vtnet_flags & VTNET_FLAG_LINK)) {
 		sc->vtnet_flags &= ~VTNET_FLAG_LINK;
-		if (bootverbose)
-			device_printf(dev, "Link is down\n");
-
 		if_link_state_change(ifp, LINK_STATE_DOWN);
 	}
 }
@@ -1105,7 +1116,7 @@ vtnet_alloc_rxbuf(struct vtnet_softc *sc
 		KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG,
 		    ("chained Rx mbuf requested without LRO_NOMRG"));
 
-		for (i = 0; i < nbufs - 1; i++) {
+		for (i = 1; i < nbufs; i++) {
 			m = m_getjcl(M_DONTWAIT, MT_DATA, 0, clsize);
 			if (m == NULL)
 				goto fail;
@@ -1143,9 +1154,8 @@ vtnet_replace_rxbuf(struct vtnet_softc *
 	clsize = sc->vtnet_rx_mbuf_size;
 	nreplace = 0;
 
-	if (m->m_next != NULL)
-		KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG,
-		    ("chained Rx mbuf without LRO_NOMRG"));
+	KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG ||
+	    m->m_next == NULL, ("chained Rx mbuf without LRO_NOMRG"));
 
 	/*
 	 * Since LRO_NOMRG mbuf chains are so large, we want to avoid
@@ -1275,8 +1285,8 @@ vtnet_enqueue_rxbuf(struct vtnet_softc *
 	int offset, error;
 
 	VTNET_LOCK_ASSERT(sc);
-	if ((sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG) == 0)
-		KASSERT(m->m_next == NULL, ("chained Rx mbuf"));
+	KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG ||
+	    m->m_next == NULL, ("chained Rx mbuf without LRO_NOMRG"));
 
 	sglist_init(&sg, VTNET_MAX_RX_SEGS, segs);
 
@@ -1688,7 +1698,8 @@ vtnet_rxeof(struct vtnet_softc *sc, int 
 			break;
 	}
 
-	virtqueue_notify(vq);
+	if (deq > 0)
+		virtqueue_notify(vq);
 
 	if (rx_npktsp != NULL)
 		*rx_npktsp = rx_npkts;
@@ -1946,9 +1957,14 @@ vtnet_encap(struct vtnet_softc *sc, stru
 	struct mbuf *m;
 	int error;
 
+	m = *m_head;
+
 	txhdr = uma_zalloc(vtnet_tx_header_zone, M_NOWAIT | M_ZERO);
-	if (txhdr == NULL)
+	if (txhdr == NULL) {
+		*m_head = NULL;
+		m_freem(m);
 		return (ENOMEM);
+	}
 
 	/*
 	 * Always use the non-mergeable header to simplify things. When
@@ -1957,21 +1973,22 @@ vtnet_encap(struct vtnet_softc *sc, stru
 	 * the correct header size to the host.
 	 */
 	hdr = &txhdr->vth_uhdr.hdr;
-	m = *m_head;
-
-	error = ENOBUFS;
 
 	if (m->m_flags & M_VLANTAG) {
 		m = ether_vlanencap(m, m->m_pkthdr.ether_vtag);
-		if ((*m_head = m) == NULL)
+		if ((*m_head = m) == NULL) {
+			error = ENOBUFS;
 			goto fail;
+		}
 		m->m_flags &= ~M_VLANTAG;
 	}
 
 	if (m->m_pkthdr.csum_flags != 0) {
 		m = vtnet_tx_offload(sc, m, hdr);
-		if ((*m_head = m) == NULL)
+		if ((*m_head = m) == NULL) {
+			error = ENOBUFS;
 			goto fail;
+		}
 	}
 
 	error = vtnet_enqueue_txbuf(sc, m_head, txhdr);
@@ -2387,6 +2404,7 @@ vtnet_rx_filter_mac(struct vtnet_softc *
 	uint8_t ack;
 
 	ifp = sc->vtnet_ifp;
+	filter = sc->vtnet_mac_filter;
 	ucnt = 0;
 	mcnt = 0;
 	promisc = 0;
@@ -2397,19 +2415,6 @@ vtnet_rx_filter_mac(struct vtnet_softc *
 	KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_RX,
 	    ("CTRL_RX feature not negotiated"));
 
-	/*
-	 * Allocate the MAC filtering table. Note we could do this
-	 * at attach time, but it is probably not worth keeping it
-	 * around for an infrequent occurrence.
-	 */
-	filter = malloc(sizeof(struct vtnet_mac_filter), M_DEVBUF,
-	    M_NOWAIT | M_ZERO);
-	if (filter == NULL) {
-		device_printf(sc->vtnet_dev,
-		    "cannot allocate MAC address filtering table\n");
-		return;
-	}
-
 	/* Unicast MAC addresses: */
 	if_addr_rlock(ifp);
 	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
@@ -2481,8 +2486,6 @@ vtnet_rx_filter_mac(struct vtnet_softc *
 		if_printf(ifp, "error setting host MAC filter table\n");
 
 out:
-	free(filter, M_DEVBUF);
-
 	if (promisc)
 		if (vtnet_set_promisc(sc, 1) != 0)
 			if_printf(ifp, "cannot enable promiscuous mode\n");

Modified: stable/9/sys/dev/virtio/network/if_vtnetvar.h
==============================================================================
--- head/sys/dev/virtio/network/if_vtnetvar.h	Fri Nov 18 05:43:43 2011	(r227652)
+++ stable/9/sys/dev/virtio/network/if_vtnetvar.h	Tue Aug 21 00:03:04 2012	(r239472)
@@ -99,6 +99,7 @@ struct vtnet_softc {
 #define VTNET_MEDIATYPE		 (IFM_ETHER | IFM_1000_T | IFM_FDX)
 	char			 vtnet_hwaddr[ETHER_ADDR_LEN];
 
+	struct vtnet_mac_filter	*vtnet_mac_filter;
 	/*
 	 * During reset, the host's VLAN filtering table is lost. The
 	 * array below is used to restore all the VLANs configured on

Modified: stable/9/sys/dev/virtio/network/virtio_net.h
==============================================================================
--- head/sys/dev/virtio/network/virtio_net.h	Fri Nov 18 05:43:43 2011	(r227652)
+++ stable/9/sys/dev/virtio/network/virtio_net.h	Tue Aug 21 00:03:04 2012	(r239472)
@@ -1,7 +1,30 @@
-/*
+/*-
  * This header is BSD licensed so anyone can use the definitions to implement
  * compatible drivers/servers.
  *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
  * $FreeBSD$
  */
 

Modified: stable/9/sys/dev/virtio/pci/virtio_pci.c
==============================================================================
--- head/sys/dev/virtio/pci/virtio_pci.c	Fri Nov 18 05:43:43 2011	(r227652)
+++ stable/9/sys/dev/virtio/pci/virtio_pci.c	Tue Aug 21 00:03:04 2012	(r239472)
@@ -189,7 +189,7 @@ static device_method_t vtpci_methods[] =
 	DEVMETHOD(virtio_bus_read_device_config,  vtpci_read_dev_config),
 	DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config),
 
-	{ 0, 0 }
+	DEVMETHOD_END
 };
 
 static driver_t vtpci_driver = {

Modified: stable/9/sys/dev/virtio/pci/virtio_pci.h
==============================================================================
--- head/sys/dev/virtio/pci/virtio_pci.h	Fri Nov 18 05:43:43 2011	(r227652)
+++ stable/9/sys/dev/virtio/pci/virtio_pci.h	Tue Aug 21 00:03:04 2012	(r239472)
@@ -1,4 +1,4 @@
-/*
+/*-
  * Copyright IBM Corp. 2007
  *
  * Authors:
@@ -7,6 +7,29 @@
  * This header is BSD licensed so anyone can use the definitions to implement
  * compatible drivers/servers.
  *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
  * $FreeBSD$
  */
 

Modified: stable/9/sys/dev/virtio/virtio.h
==============================================================================
--- head/sys/dev/virtio/virtio.h	Fri Nov 18 05:43:43 2011	(r227652)
+++ stable/9/sys/dev/virtio/virtio.h	Tue Aug 21 00:03:04 2012	(r239472)
@@ -1,7 +1,30 @@
-/*
+/*-
  * This header is BSD licensed so anyone can use the definitions to implement
  * compatible drivers/servers.
  *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201208210003.q7L0342r018598>