Index: src2/sys/vm/vm_zone.c
===================================================================
--- src2.orig/sys/vm/vm_zone.c	2008-01-19 15:48:34.094720000 +0100
+++ src2/sys/vm/vm_zone.c	2008-01-19 17:12:28.000000000 +0100
@@ -16,6 +16,7 @@
  */
 
 #include <sys/param.h>
+#include <sys/queue.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
@@ -37,6 +38,7 @@
 #define	ZONE_ERROR_INVALID 0
 #define	ZONE_ERROR_NOTFREE 1
 #define	ZONE_ERROR_ALREADYFREE 2
+#define	ZONE_ERROR_CANT_DESTROY 3
 
 #define	ZONE_ROUNDING	32
 
@@ -119,7 +121,7 @@
  * zalloc, zfree, are the allocation/free routines.
  */
 
-struct vm_zone *zlist;	/* exported to vmstat */
+LIST_HEAD(zlist, vm_zone) zlist = LIST_HEAD_INITIALIZER(zlist);
 static int sysctl_vm_zone(SYSCTL_HANDLER_ARGS);
 static int zone_kmem_pages, zone_kern_pages, zone_kmem_kvaspace;
 
@@ -152,6 +154,12 @@
 {
 	int totsize;
 
+	/*
+	 * Only zones created with zinit() are destroyable.
+	 */
+	if (z->zflags & ZONE_DESTROYABLE)
+		zerror(ZONE_ERROR_CANT_DESTROY);
+
 	if ((z->zflags & ZONE_BOOT) == 0) {
 		z->zsize = (size + ZONE_ROUNDING - 1) & ~(ZONE_ROUNDING - 1);
 		spin_init(&z->zlock);
@@ -162,10 +170,11 @@
 		z->znalloc = 0;
 		z->zitems = NULL;
 
-		z->znext = zlist;
-		zlist = z;
+		LIST_INSERT_HEAD(&zlist, z, zlink);
 	}
 
+	z->zkmvec = NULL;
+	z->zkmcur = z->zkmmax = 0;
 	z->zflags |= flags;
 
 	/*
@@ -181,7 +190,7 @@
 
 		z->zkva = kmem_alloc_pageable(&kernel_map, totsize);
 		if (z->zkva == 0) {
-			zlist = z->znext;
+			LIST_REMOVE(z, zlink);
 			return 0;
 		}
 
@@ -242,11 +251,15 @@
 		return NULL;
 
 	z->zflags = 0;
-	if (zinitna(z, NULL, name, size, nentries, flags, zalloc) == 0) {
+	if (zinitna(z, NULL, name, size, nentries,
+	            flags & ~ZONE_DESTROYABLE, zalloc) == 0) {
 		kfree(z, M_ZONE);
 		return NULL;
 	}
 
+	if (flags & ZONE_DESTROYABLE)
+		z->zflags |= ZONE_DESTROYABLE;
+
 	return z;
 }
 
@@ -285,14 +298,52 @@
 	z->zmax = nitems;
 	z->ztotal = nitems;
 
-	if (zlist == 0) {
-		zlist = z;
+	LIST_INSERT_HEAD(&zlist, z, zlink);
+}
+
+/*
+ * Release all resources owned by zone created with zinit().
+ */
+void
+zdestroy(vm_zone_t z)
+{
+	int i;
+
+	if (z == NULL)
+		zerror(ZONE_ERROR_INVALID);
+	if ((z->zflags & ZONE_DESTROYABLE) == 0)
+		zerror(ZONE_ERROR_CANT_DESTROY);
+
+	LIST_REMOVE(z, zlink);
+
+	/*
+	 * Release virtual mappings, physical memory and update sysctl stats.
+	 */
+	if (z->zflags & ZONE_INTERRUPT) {
+		/*
+		 * Free the mapping.
+		 */
+		kmem_free(&kernel_map, z->zkva, z->zpagemax*PAGE_SIZE);
+		atomic_subtract_int(&zone_kmem_kvaspace, z->zpagemax*PAGE_SIZE);
+		/*
+		 * Free the backing object and physical pages.
+		 */
+		vm_object_deallocate(z->zobj);
+		atomic_subtract_int(&zone_kmem_pages, z->zpagecount);
 	} else {
-		z->znext = zlist;
-		zlist = z;
+		for (i=0; i < z->zkmcur; i++) {
+			kmem_free(&kernel_map, z->zkmvec[i],
+			    z->zalloc*PAGE_SIZE);
+			atomic_subtract_int(&zone_kern_pages, z->zalloc);
+		}
+		kfree(z->zkmvec, M_ZONE);
 	}
+
+	spin_uninit(&z->zlock);
+	kfree(z, M_ZONE);
 }
 
+
 /*
  * void *zalloc(vm_zone_t zone) --
  *	Returns an item from a specified zone.  May not be called from a
@@ -335,6 +386,14 @@
 			if (m == NULL)
 				break;
 
+			/*
+			 * Unbusy page so it can freed in zdestroy().  Make
+			 * sure it is not on any queue and so can not be
+			 * recycled under our feet.
+			 */
+			KKASSERT(m->queue == PQ_NONE);
+			vm_page_flag_clear(m, PG_BUSY);
+
 			zkva = z->zkva + z->zpagecount * PAGE_SIZE;
 			pmap_kenter(zkva, VM_PAGE_TO_PHYS(m)); /* YYY */
 			bzero((void *)zkva, PAGE_SIZE);
@@ -375,6 +434,17 @@
 		if (item != NULL) {
 			zone_kern_pages += z->zalloc;	/* not MP-safe XXX */
 			bzero(item, nbytes);
+
+			if (z->zflags & ZONE_DESTROYABLE) {
+				if (z->zkmcur == z->zkmmax) {
+					z->zkmmax =
+						z->zkmmax==0 ? 1 : z->zkmmax*2;
+					z->zkmvec = krealloc(z->zkmvec,
+					    z->zkmmax * sizeof(z->zkmvec[0]),
+					    M_ZONE, M_WAITOK);
+				}
+				z->zkmvec[z->zkmcur++] = (vm_offset_t)item;
+			}
 		} else {
 			nbytes = 0;
 		}
@@ -428,7 +498,7 @@
 sysctl_vm_zone(SYSCTL_HANDLER_ARGS)
 {
 	int error=0;
-	vm_zone_t curzone, nextzone;
+	vm_zone_t curzone;
 	char tmpbuf[128];
 	char tmpname[14];
 
@@ -438,12 +508,11 @@
 	if (error)
 		return (error);
 
-	for (curzone = zlist; curzone; curzone = nextzone) {
+	LIST_FOREACH(curzone, &zlist, zlink) {
 		int i;
 		int len;
 		int offset;
 
-		nextzone = curzone->znext;
 		len = strlen(curzone->zname);
 		if (len >= (sizeof(tmpname) - 1))
 			len = (sizeof(tmpname) - 1);
@@ -453,7 +522,7 @@
 		memcpy(tmpname, curzone->zname, len);
 		tmpname[len] = ':';
 		offset = 0;
-		if (curzone == zlist) {
+		if (curzone == LIST_FIRST(&zlist)) {
 			offset = 1;
 			tmpbuf[0] = '\n';
 		}
@@ -465,7 +534,7 @@
 			curzone->zfreecnt, curzone->znalloc);
 
 		len = strlen((char *)tmpbuf);
-		if (nextzone == NULL)
+		if (LIST_NEXT(curzone, zlink) == NULL)
 			tmpbuf[len - 1] = 0;
 
 		error = SYSCTL_OUT(req, tmpbuf, len);
@@ -492,6 +561,9 @@
 	case ZONE_ERROR_ALREADYFREE:
 		msg = "zone: freeing free entry";
 		break;
+	case ZONE_ERROR_CANT_DESTROY:
+		msg = "zone: cannot destroy zone (not created with zinit()?)";
+		break;
 	default:
 		msg = "zone: invalid error";
 		break;
Index: src2/sys/vm/vm_zone.h
===================================================================
--- src2.orig/sys/vm/vm_zone.h	2008-01-19 15:49:14.000000000 +0100
+++ src2/sys/vm/vm_zone.h	2008-01-19 16:13:30.000000000 +0100
@@ -23,6 +23,7 @@
 #define ZONE_SPECIAL   0x0004	/* special vm_map_entry zone, see zget() */
 #define ZONE_BOOT      0x0010	/* Internal flag used by zbootinit */
 #define ZONE_USE_RESERVE 0x0020	/* use reserve memory if necessary */
+#define ZONE_DESTROYABLE 0x0040 /* can be zdestroy()'ed */
 
 #include <sys/spinlock.h>
 #include <sys/thread.h>
@@ -44,7 +45,15 @@
 	int		zallocflag;	/* flag for allocation */
 	struct vm_object *zobj;		/* object to hold zone */
 	char		*zname;		/* name for diags */
-	struct vm_zone	*znext;		/* list of zones for sysctl */
+	LIST_ENTRY(vm_zone) zlink;	/* link in zlist */
+ 	/*
+ 	 * The following fields track kmem_alloc()'ed blocks when
+ 	 * ZONE_DESTROYABLE set and normal zone (i.e. not ZONE_INTERRUPT nor
+ 	 * ZONE_SPECIAL).
+ 	 */
+ 	vm_offset_t	*zkmvec;	/* krealloc()'ed array */
+ 	int		zkmcur;		/* next free slot in zkmvec */
+ 	int		zkmmax;		/* # of slots in zkmvec */
 } *vm_zone_t;
 
 
@@ -57,5 +66,6 @@
 void		zfree (vm_zone_t z, void *item);
 void		zbootinit (vm_zone_t z, char *name, int size, void *item,
 			       int nitems);
+void		zdestroy(vm_zone_t z);
 
 #endif	/* _VM_VM_ZONE_H_ */
Index: src2/share/man/man9/zone.9
===================================================================
--- src2.orig/share/man/man9/zone.9	2006-10-19 20:44:00.000000000 +0200
+++ src2/share/man/man9/zone.9	2008-01-19 17:08:48.000000000 +0100
@@ -33,6 +33,7 @@
 .Nm zbootinit ,
 .Nm zinitna ,
 .Nm zinit ,
+.Nm zdestroy ,
 .Nm zalloc ,
 .Nm zfree ,
 .Nd zone allocator
@@ -46,6 +47,8 @@
 .Fn zinitna "vm_zone_t z" "struct vm_object *obj" "char *name" "int size" "int nentries" "int flags" "int zalloc"
 .Ft vm_zone_t
 .Fn zinit "char *name" "int size" "int nentries" "int flags" "int zalloc"
+.Ft void
+.Fn zdestroy "vm_zone_t z"
 .Ft void *
 .Fn zalloc "vm_zone_t z"
 .Ft void
@@ -102,16 +105,19 @@
 items) of the zone, respectively.
 The
 .Fa flags
-argument should be set to
+argument should have the
 .Dv ZONE_INTERRUPT
-if there is a chance that items may be allocated from the zone in
+bit set if there is a chance that items may be allocated from the zone in
 interrupt context; note that in this case, the zone will never grow
 larger than
 .Fa nentries
 items.
-In all other cases,
+The
 .Fa flags
-should be set to 0.
+argument should have the
+.Dv ZONE_DESTROYABLE
+bit set if the zone is to be destroyed with
+.Fn zdestroy .
 The final argument,
 .Fa zalloc ,
 indicates the number of VM pages by which the zone should grow every
@@ -150,6 +156,14 @@
 .Dv ZONE_INTERRUPT
 case.
 .Pp
+To release all the memory allocated for a zone, call
+.Fn zdestroy .
+Only zones created with
+.Fn zinit
+and with the
+.Dv ZONE_DESTROYABLE
+flag can be destroyed.
+.Pp
 To allocate an item from a zone, simply call
 .Fn zalloc
 with a pointer to that zone; it will return a pointer to an item, or
Index: src2/share/man/man9/Makefile
===================================================================
--- src2.orig/share/man/man9/Makefile	2007-11-18 15:10:41.000000000 +0100
+++ src2/share/man/man9/Makefile	2008-01-19 17:35:14.000000000 +0100
@@ -604,6 +604,7 @@
 	zone.9 zbootinit.9 \
 	zone.9 zfree.9 \
 	zone.9 zinit.9 \
-	zone.9 zinitna.9
+	zone.9 zinitna.9 \
+	zone.9 zdestroy.9
 
 .include <bsd.prog.mk>
