Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 23 Jul 2001 04:11:23 +0200
From:      Thomas Moestl <tmoestl@gmx.net>
To:        freebsd-hackers@FreeBSD.org
Subject:   [PATCH REVIEW] zdestroy() for the zone allocator (and small nfs patch)
Message-ID:  <20010723041123.A47216@crow.dom2ip.de>

next in thread | raw e-mail | index | archive | help

--1yeeQ81UyVL57Vl7
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi,

I've attached a patch that adds a functions for zone destruction to
the kernel zone allocator, which is needed to properly support the
unload case for modules that create zones (such as nfs.ko).
As a first application of this, it also patches the relevant nfs code
to destroy the internal nfs zones on module unload (failing to do so 
can cause substantial resource leaks and crashes).

I'd like to commit this, but I think it would be good to get some
additional review before, especially on the part that deals with
ZONE_INTERRUPT zones (which need somewhat lower level vm handling
than the rest).
The handling of regular zones involves some magic (and is probably a
little gross), but it works without having to maintain additional
state information.

So, to all who are interested in this, could you please review and
comment it? 

Thanks,
	- thomas

--1yeeQ81UyVL57Vl7
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="zdest6.diff"

Index: nfs/nfs.h
===================================================================
RCS file: /home/ncvs/src/sys/nfs/nfs.h,v
retrieving revision 1.59
diff -u -r1.59 nfs.h
--- nfs/nfs.h	2001/04/17 20:45:21	1.59
+++ nfs/nfs.h	2001/07/21 14:40:59
@@ -633,6 +633,7 @@
 			      struct mbuf *));
 int	nfs_adv __P((struct mbuf **, caddr_t *, int, int));
 void	nfs_nhinit __P((void));
+void	nfs_nhuninit __P((void));
 void	nfs_timer __P((void*));
 int	nfsrv_dorec __P((struct nfssvc_sock *, struct nfsd *, 
 			 struct nfsrv_descript **));
Index: nfs/nfs_node.c
===================================================================
RCS file: /home/ncvs/src/sys/nfs/nfs_node.c,v
retrieving revision 1.49
diff -u -r1.49 nfs_node.c
--- nfs/nfs_node.c	2001/05/01 08:13:14	1.49
+++ nfs/nfs_node.c	2001/07/22 12:57:46
@@ -154,6 +154,12 @@
 	nfsnodehashtbl = hashinit(desiredvnodes, M_NFSHASH, &nfsnodehash);
 }
 
+void
+nfs_nhuninit()
+{
+	zdestroy(nfsnode_zone);
+}
+
 /*
  * Look up a vnode/nfsnode by file handle.
  * Callers must check for mount points!!
Index: nfs/nfs_subs.c
===================================================================
RCS file: /home/ncvs/src/sys/nfs/nfs_subs.c,v
retrieving revision 1.103
diff -u -r1.103 nfs_subs.c
--- nfs/nfs_subs.c	2001/07/04 16:20:16	1.103
+++ nfs/nfs_subs.c	2001/07/22 01:48:05
@@ -1185,6 +1185,8 @@
 	lease_updatetime = nfs_prev_lease_updatetime;
 	sysent[SYS_nfssvc].sy_narg = nfs_prev_nfssvc_sy_narg;
 	sysent[SYS_nfssvc].sy_call = nfs_prev_nfssvc_sy_call;
+	nfs_nhuninit();
+	zdestroy(nfsmount_zone);
 	return (0);
 }
 
Index: vm/vm_zone.c
===================================================================
RCS file: /home/ncvs/src/sys/vm/vm_zone.c,v
retrieving revision 1.46
diff -u -r1.46 vm_zone.c
--- vm/vm_zone.c	2001/07/09 03:37:33	1.46
+++ vm/vm_zone.c	2001/07/22 16:18:49
@@ -28,6 +28,7 @@
 #include <vm/vm.h>
 #include <vm/vm_object.h>
 #include <vm/vm_page.h>
+#include <vm/vm_param.h>
 #include <vm/vm_map.h>
 #include <vm/vm_kern.h>
 #include <vm/vm_extern.h>
@@ -253,6 +254,111 @@
 }
 
 /*
+ * Destroy a zone, freeing the allocated memory.
+ * This does not do any locking for the zone; make sure it is not used
+ * any more before calling. All zalloc()'ated memory in the zone must have
+ * been zfree()'d.
+ * zdestroy() may not be used with zbootinit()'ed zones.
+ */
+void
+zdestroy(vm_zone_t z)
+{
+	int i, nitems, nbytes;
+	void *item, *min, **itp;
+	vm_map_t map;
+	vm_map_entry_t entry;
+	vm_object_t obj;
+	vm_pindex_t pindex;
+	vm_prot_t prot;
+	boolean_t wired;
+
+	GIANT_REQUIRED;
+	KASSERT(z != NULL, ("invalid zone"));
+	/*
+	 * This is needed, or the algorithm used for non-interrupt zones will
+	 * blow up badly.
+	 */
+	KASSERT(z->ztotal == z->zfreecnt,
+	    ("zdestroy() used with an active zone"));
+	KASSERT((z->zflags & ZONE_BOOT) == 0,
+	    ("zdestroy() used with a zbootinit()'ed zone"))
+
+	if (z->zflags & ZONE_INTERRUPT) {
+		kmem_free(kernel_map, z->zkva, z->zpagemax * PAGE_SIZE);
+		vm_object_deallocate(z->zobj);
+		atomic_subtract_int(&zone_kmem_kvaspace,
+		    z->zpagemax * PAGE_SIZE);
+		atomic_subtract_int(&zone_kmem_pages,
+		    z->zpagecount);
+	} else {
+		/*
+		 * This is evil h0h0 magic:
+		 * The items may be in z->zitems in a random oder; we have to
+		 * free the start of an allocated area, but do not want to save
+		 * extra information. Additionally, we may not access items that
+		 * were in a freed area.
+		 * This is achieved in the following way: the smallest address
+		 * is selected, and, after removing all items that are in a
+		 * range of z->zalloc * PAGE_SIZE (one allocation unit) from
+		 * it, kmem_free is called on it (since it is the smallest one,
+		 * it must be the start of an area). This is repeated until all
+		 * items are gone.
+		 */
+		nbytes = z->zalloc * PAGE_SIZE;
+		nitems = nbytes / z->zsize;
+		while (z->zitems != NULL) {
+			/* Find minimal element. */
+			item = min = z->zitems;
+			while (item != NULL) {
+				if (item < min)
+					min = item;
+				item = ((void **)item)[0];
+			}
+			/* Free. */
+			itp = &z->zitems;
+			i = 0;
+			while (*itp != NULL && i < nitems) {
+				if ((char *)*itp >= (char *)min &&
+				    (char *)*itp < (char *)min + nbytes) {
+					*itp = ((void **)*itp)[0];
+					i++;
+				} else
+					itp = &((void **)*itp)[0];
+			}
+			KASSERT(i == nitems, ("zdestroy(): corrupt zone"));
+			/*
+			 * We can allocate from kmem_map (kmem_malloc) or
+			 * kernel_map (kmem_alloc).
+			 * kmem_map is a submap of kernel_map, so we can use
+			 * vm_map_lookup to retrieve the map we need use.
+			 */
+			map = kernel_map;
+			if (vm_map_lookup(&map, (vm_offset_t)min, VM_PROT_NONE,
+			    &entry, &obj, &pindex, &prot, &wired) !=
+			    KERN_SUCCESS)
+				panic("zalloc mapping lost");
+			/* Need to unlock. */
+			vm_map_lookup_done(map, entry);
+			if (map == kmem_map) {
+				atomic_subtract_int(&zone_kmem_pages,
+				    z->zalloc);
+			} else if (map == kernel_map) {
+				atomic_subtract_int(&zone_kern_pages,
+				    z->zalloc);
+			} else
+				panic("zdestroy(): bad map");
+			kmem_free(map, (vm_offset_t)min, nbytes);
+		}
+	}
+
+	mtx_lock(&zone_mtx);
+	SLIST_REMOVE(&zlist, z, vm_zone, zent);
+	mtx_unlock(&zone_mtx);
+	mtx_destroy(&z->zmtx);
+	free(z, M_ZONE);
+}
+
+/*
  * Grow the specified zone to accomodate more items.
  */
 static void *
@@ -285,6 +391,7 @@
 		}
 		nitems = (i * PAGE_SIZE) / z->zsize;
 	} else {
+		/* Please check zdestroy() when changing this! */
 		nbytes = z->zalloc * PAGE_SIZE;
 
 		/*
Index: vm/vm_zone.h
===================================================================
RCS file: /home/ncvs/src/sys/vm/vm_zone.h,v
retrieving revision 1.18
diff -u -r1.18 vm_zone.h
--- vm/vm_zone.h	2001/05/01 08:13:21	1.18
+++ vm/vm_zone.h	2001/07/21 15:23:39
@@ -55,6 +55,7 @@
                      int flags, int zalloc);
 void		 zbootinit(vm_zone_t z, char *name, int size,
                      void *item, int nitems);
+void		 zdestroy(vm_zone_t z);
 void		*zalloc(vm_zone_t z);
 void		 zfree(vm_zone_t z, void *item);
 

--1yeeQ81UyVL57Vl7--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




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