Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 14 Aug 2008 13:26:41 GMT
From:      Nick Barkas <snb@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 147376 for review
Message-ID:  <200808141326.m7EDQfn8084577@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=147376

Change 147376 by snb@snb_toro on 2008/08/14 13:26:35

	Dynamic memory dirhash for FreeBSD 7

Affected files ...

.. //depot/projects/soc2008/snb-dirhash/sys-ufs-ufs/freebsd7/dirhash.h#2 edit
.. //depot/projects/soc2008/snb-dirhash/sys-ufs-ufs/freebsd7/ufs_dirhash.c#2 edit

Differences ...

==== //depot/projects/soc2008/snb-dirhash/sys-ufs-ufs/freebsd7/dirhash.h#2 (text+ko) ====

@@ -22,7 +22,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/ufs/ufs/dirhash.h,v 1.5 2005/01/07 02:29:26 imp Exp $
+ * $FreeBSD$
  */
 
 #ifndef _UFS_UFS_DIRHASH_H_
@@ -100,6 +100,8 @@
 
 	int	dh_onlist;	/* true if on the ufsdirhash_list chain */
 
+	time_t	dh_lastused;	/* time the dirhash was last read or written*/
+
 	/* Protected by ufsdirhash_mtx. */
 	TAILQ_ENTRY(dirhash) dh_list;	/* chain of all dirhashes */
 };

==== //depot/projects/soc2008/snb-dirhash/sys-ufs-ufs/freebsd7/ufs_dirhash.c#2 (text+ko) ====

@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/ufs/ufs/ufs_dirhash.c,v 1.23 2005/10/31 15:41:28 rwatson Exp $");
+__FBSDID("$FreeBSD$");
 
 #include "opt_ufs.h"
 
@@ -47,6 +47,8 @@
 #include <sys/vnode.h>
 #include <sys/mount.h>
 #include <sys/sysctl.h>
+#include <sys/eventhandler.h>
+#include <sys/time.h>
 #include <vm/uma.h>
 
 #include <ufs/ufs/quota.h>
@@ -79,6 +81,13 @@
 static int ufs_dirhashcheck = 0;
 SYSCTL_INT(_vfs_ufs, OID_AUTO, dirhash_docheck, CTLFLAG_RW, &ufs_dirhashcheck,
     0, "enable extra sanity tests");
+static int ufs_dirhashlowmemcount = 0;
+SYSCTL_INT(_vfs_ufs, OID_AUTO, dirhash_lowmemcount, CTLFLAG_RD, 
+    &ufs_dirhashlowmemcount, 0, "number of times low memory hook called");
+static int ufs_dirhashreclaimage = 5;
+SYSCTL_INT(_vfs_ufs, OID_AUTO, dirhash_reclaimage, CTLFLAG_RW, 
+    &ufs_dirhashreclaimage, 0, 
+    "max time in seconds of hash inactivity before deletion in low VM events");
 
 
 static int ufsdirhash_hash(struct dirhash *dh, char *name, int namelen);
@@ -87,7 +96,9 @@
 static int ufsdirhash_findslot(struct dirhash *dh, char *name, int namelen,
 	   doff_t offset);
 static doff_t ufsdirhash_getprev(struct direct *dp, doff_t offset);
+static int ufsdirhash_destroy(struct dirhash *dh);
 static int ufsdirhash_recycle(int wanted);
+static void ufsdirhash_lowmem(void);
 
 static uma_zone_t	ufsdirhash_zone;
 
@@ -215,6 +226,7 @@
 	dh->dh_seqopt = 0;
 	dh->dh_seqoff = 0;
 	dh->dh_score = DH_SCOREINIT;
+	dh->dh_lastused = time_second;
 	ip->i_dirhash = dh;
 
 	bmask = VFSTOUFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
@@ -377,6 +389,9 @@
 	if (dh->dh_score < DH_SCOREMAX)
 		dh->dh_score++;
 
+	/* Update last used time. */
+	dh->dh_lastused = time_second;
+
 	vp = ip->i_vnode;
 	bmask = VFSTOUFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
 	blkoff = -1;
@@ -643,6 +658,9 @@
 		dh->dh_hused++;
 	DH_ENTRY(dh, slot) = offset;
 
+	/* Update last used time. */
+	dh->dh_lastused = time_second;
+
 	/* Update the per-block summary info. */
 	ufsdirhash_adjfree(dh, offset, -DIRSIZ(0, dirp));
 	DIRHASH_UNLOCK(dh);
@@ -1014,6 +1032,48 @@
 }
 
 /*
+ * Delete the given dirhash and reclaim its memory. Assumes that 
+ * ufsdirhash_list is locked, and leaves it locked. Also assumes 
+ * that dh is locked. Returns the amount of memory freed.
+ */
+static int
+ufsdirhash_destroy(struct dirhash *dh)
+{
+	doff_t **hash;
+	u_int8_t *blkfree;
+	int i, mem, narrays;
+
+	KASSERT(dh->dh_hash != NULL, ("dirhash: NULL hash on list"));
+
+	/* Remove it from the list and detach its memory. */
+	TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list);
+	dh->dh_onlist = 0;
+	hash = dh->dh_hash;
+	dh->dh_hash = NULL;
+	blkfree = dh->dh_blkfree;
+	dh->dh_blkfree = NULL;
+	narrays = dh->dh_narrays;
+	mem = narrays * sizeof(*dh->dh_hash) +
+	    narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) +
+	    dh->dh_nblk * sizeof(*dh->dh_blkfree);
+
+	/* Unlock everything, free the detached memory. */
+	DIRHASH_UNLOCK(dh);
+	DIRHASHLIST_UNLOCK();
+	for (i = 0; i < narrays; i++)
+		DIRHASH_BLKFREE(hash[i]);
+	FREE(hash, M_DIRHASH);
+	FREE(blkfree, M_DIRHASH);
+
+	/* Account for the returned memory. */
+	DIRHASHLIST_LOCK();
+	ufs_dirhashmem -= mem;
+
+	return (mem);
+}
+
+
+/*
  * Try to free up `wanted' bytes by stealing memory from existing
  * dirhashes. Returns zero with list locked if successful.
  */
@@ -1021,9 +1081,6 @@
 ufsdirhash_recycle(int wanted)
 {
 	struct dirhash *dh;
-	doff_t **hash;
-	u_int8_t *blkfree;
-	int i, mem, narrays;
 
 	DIRHASHLIST_LOCK();
 	while (wanted + ufs_dirhashmem > ufs_dirhashmaxmem) {
@@ -1033,7 +1090,6 @@
 			return (-1);
 		}
 		DIRHASH_LOCK(dh);
-		KASSERT(dh->dh_hash != NULL, ("dirhash: NULL hash on list"));
 
 		/* Decrement the score; only recycle if it becomes zero. */
 		if (--dh->dh_score > 0) {
@@ -1042,32 +1098,50 @@
 			return (-1);
 		}
 
-		/* Remove it from the list and detach its memory. */
-		TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list);
-		dh->dh_onlist = 0;
-		hash = dh->dh_hash;
-		dh->dh_hash = NULL;
-		blkfree = dh->dh_blkfree;
-		dh->dh_blkfree = NULL;
-		narrays = dh->dh_narrays;
-		mem = narrays * sizeof(*dh->dh_hash) +
-		    narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) +
-		    dh->dh_nblk * sizeof(*dh->dh_blkfree);
+		/* Destroy the dirhash, and repeat if necessary. */
+		ufsdirhash_destroy(dh);
+	}
+	/* Success; return with list locked. */
+	return (0);
+}
+
+/*
+ * Calback that frees some dirhashes when the system is low on virtual memory.
+ */
+static void
+ufsdirhash_lowmem()
+{
+	struct dirhash *dh;
+	int memfreed = 0;
+	/* XXX: this 10% may need to be adjusted */
+	int memwanted = ufs_dirhashmem / 10;
 
-		/* Unlock everything, free the detached memory. */
-		DIRHASH_UNLOCK(dh);
-		DIRHASHLIST_UNLOCK();
-		for (i = 0; i < narrays; i++)
-			DIRHASH_BLKFREE(hash[i]);
-		FREE(hash, M_DIRHASH);
-		FREE(blkfree, M_DIRHASH);
+	ufs_dirhashlowmemcount++;
 
-		/* Account for the returned memory, and repeat if necessary. */
-		DIRHASHLIST_LOCK();
-		ufs_dirhashmem -= mem;
+	DIRHASHLIST_LOCK();
+	/* 
+	 * Delete dirhashes not used for more than ufs_dirhashreclaimage 
+	 * seconds.
+	 */
+	for (dh = TAILQ_FIRST(&ufsdirhash_list); dh != NULL; dh = 
+	     TAILQ_NEXT(dh, dh_list)) {
+		if (time_second - dh->dh_lastused > ufs_dirhashreclaimage) {
+			DIRHASH_LOCK(dh);
+			memfreed += ufsdirhash_destroy(dh);
+		}
+	}
+	
+	/* 
+	 * If not enough memory was freed, keep deleting hashes from the head 
+	 * of the dirhash list. The ones closest to the head should be the 
+	 * oldest. 
+	 */
+	for (dh = TAILQ_FIRST(&ufsdirhash_list); memfreed < memwanted &&
+	     dh !=NULL; dh = TAILQ_NEXT(dh, dh_list)) {
+		DIRHASH_LOCK(dh);
+		memfreed += ufsdirhash_destroy(dh);
 	}
-	/* Success; return with list locked. */
-	return (0);
+	DIRHASHLIST_UNLOCK();
 }
 
 
@@ -1078,6 +1152,11 @@
 	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
 	mtx_init(&ufsdirhash_mtx, "dirhash list", NULL, MTX_DEF);
 	TAILQ_INIT(&ufsdirhash_list);
+
+	/* Register a callback function to handle low memory signals */
+	EVENTHANDLER_REGISTER(vm_lowmem, ufsdirhash_lowmem, NULL, 
+	    EVENTHANDLER_PRI_FIRST);
+
 }
 
 void



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