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 +#include #include #include #include @@ -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 #include @@ -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