PERFORCE change 147376 for review
Nick Barkas
snb at FreeBSD.org
Thu Aug 14 13:26:42 UTC 2008
http://perforce.freebsd.org/chv.cgi?CH=147376
Change 147376 by snb at 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
More information about the p4-projects
mailing list