==== //depot/projects/soc2008/snb-dirhash/sys-ufs-ufs/dirhash.h#1 - /Users/snb/Documents/soc/perforce/snb-dirhash/sys-ufs-ufs/dirhash.h ==== --- /tmp/tmp.23276.25 2008-07-07 23:06:16.000000000 +0200 +++ /Users/snb/Documents/soc/perforce/snb-dirhash/sys-ufs-ufs/dirhash.h 2008-07-06 23:16:04.000000000 +0200 @@ -68,6 +68,12 @@ #define DH_SCOREINIT 8 /* initial dh_score when dirhash built */ #define DH_SCOREMAX 64 /* max dh_score value */ +/* + * If a vm_lowmem signal is received, we will try to free memory by + * deleting all hashes older than DH_RECLAIMAGE seconds. + */ +#define DH_RECLAIMAGE 5 + /* * The main hash table has 2 levels. It is an array of pointers to * blocks of DH_NBLKOFF offsets. @@ -101,6 +107,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/ufs_dirhash.c#1 - /Users/snb/Documents/soc/perforce/snb-dirhash/sys-ufs-ufs/ufs_dirhash.c ==== --- /tmp/tmp.23276.28 2008-07-07 23:06:16.000000000 +0200 +++ /Users/snb/Documents/soc/perforce/snb-dirhash/sys-ufs-ufs/ufs_dirhash.c 2008-07-07 14:10:15.000000000 +0200 @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include #include @@ -80,6 +82,9 @@ 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 ufsdirhash_hash(struct dirhash *dh, char *name, int namelen); @@ -88,7 +93,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 void ufsdirhash_free_locked(struct inode *ip); static uma_zone_t ufsdirhash_zone; @@ -324,6 +331,7 @@ dh->dh_seqopt = 0; dh->dh_seqoff = 0; dh->dh_score = DH_SCOREINIT; + dh->dh_lastused = time_second; /* * Use non-blocking mallocs so that we will revert to a linear @@ -494,6 +502,9 @@ /* Update the score. */ if (dh->dh_score < DH_SCOREMAX) dh->dh_score++; + + /* Update last used time. */ + dh->dh_lastused = time_second; DIRHASHLIST_UNLOCK(); vp = ip->i_vnode; @@ -736,6 +747,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)); ufsdirhash_release(dh); @@ -1075,6 +1089,46 @@ } /* + * 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 = dh->dh_memreq; + dh->dh_memreq = 0; + + /* Unlock everything, free the detached memory. */ + ufsdirhash_release(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. */ @@ -1082,9 +1136,6 @@ ufsdirhash_recycle(int wanted) { struct dirhash *dh; - doff_t **hash; - u_int8_t *blkfree; - int i, mem, narrays; DIRHASHLIST_LOCK(); dh = TAILQ_FIRST(&ufsdirhash_list); @@ -1094,6 +1145,7 @@ DIRHASHLIST_UNLOCK(); return (-1); } + /* * If we can't lock it it's in use and we don't want to * recycle it anyway. @@ -1102,36 +1154,54 @@ dh = TAILQ_NEXT(dh, dh_list); continue; } - 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 = dh->dh_memreq; - dh->dh_memreq = 0; + ufsdirhash_destroy(dh); - /* Unlock everything, free the detached memory. */ - ufsdirhash_release(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, and repeat if necessary. */ - DIRHASHLIST_LOCK(); - ufs_dirhashmem -= mem; + /* Repeat if necessary. */ dh = TAILQ_FIRST(&ufsdirhash_list); } /* 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; + + ufs_dirhashlowmemcount++; + + DIRHASHLIST_LOCK(); + /* + * Delete all dirhashes not used for more than DH_RECLAIMAGE seconds. + * If we can't get a lock on the dirhash, it will be skipped. + */ + for (dh = TAILQ_FIRST(&ufsdirhash_list); dh != NULL; dh = + TAILQ_NEXT(dh, dh_list)) { + if (time_second - dh->dh_lastused > DH_RECLAIMAGE && + lockmgr(&dh->dh_lock, LK_EXCLUSIVE | LK_NOWAIT, NULL)) + memfreed += ufsdirhash_destroy(dh); + } + + /* + * If no hashes were old enough, instead try deleting a single dirhash + * from the end of the list. + */ + dh = TAILQ_FIRST(&ufsdirhash_list); + while (memfreed == 0 && dh != NULL) { + if (lockmgr(&dh->dh_lock, LK_EXCLUSIVE | LK_NOWAIT, NULL)) { + dh = TAILQ_NEXT(dh, dh_list); + continue; + } + memfreed += ufsdirhash_destroy(dh); + } + DIRHASHLIST_UNLOCK(); +} + void ufsdirhash_init() @@ -1140,6 +1210,10 @@ 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