PERFORCE change 144800 for review
Nick Barkas
snb at FreeBSD.org
Sun Jul 6 21:16:24 UTC 2008
http://perforce.freebsd.org/chv.cgi?CH=144800
Change 144800 by snb at snb_toro on 2008/07/06 21:16:04
Upon receipt of vm_lowmem() signals, try deleting all dirhashes older
than DH_RECLAIMAGE, set here to five seconds for now. If all the
dirhashes are newer than this, just fall back to trying to delete the
one at the beginning of ufsdirhash_list.
DH_RECLAIMAGE probably will need tweaking, and so far dh_lastused is
only being updated during calls to ufsdirhash_build(),
ufsdirhash_lookup(), and ufsdirhash_add(). It may need to be updated
when other functions are called as well. Also, I have not yet tested
this code. This is a check point before I lose my network connection.
Affected files ...
.. //depot/projects/soc2008/snb-dirhash/sys-ufs-ufs/dirhash.h#2 edit
.. //depot/projects/soc2008/snb-dirhash/sys-ufs-ufs/ufs_dirhash.c#5 edit
Differences ...
==== //depot/projects/soc2008/snb-dirhash/sys-ufs-ufs/dirhash.h#2 (text+ko) ====
@@ -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#5 (text+ko) ====
@@ -49,6 +49,7 @@
#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>
@@ -92,7 +93,8 @@
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(void);
+static int ufsdirhash_destroy(struct dirhash *dh);
+static int ufsdirhash_destroy_first(void);
static int ufsdirhash_recycle(int wanted);
static void ufsdirhash_lowmem(void);
static void ufsdirhash_free_locked(struct inode *ip);
@@ -330,6 +332,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
@@ -500,6 +503,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;
@@ -742,6 +748,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);
@@ -1081,34 +1090,17 @@
}
/*
- * Delete the first dirhash on the list and reclaim its memory.
- * Assumes that ufsdirhash_list is locked, and leaves it locked.
- * If unable to obtain a lock on the first dirhash, moves down
- * the list until it can lock a dirhash and destroys it. Returns
- * the amount of memory freed, or -1 if unable to find any
- * dirhashes that can be destroyed.
+ * 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()
+ufsdirhash_destroy(struct dirhash *dh)
{
- struct dirhash *dh;
doff_t **hash;
u_int8_t *blkfree;
int i, mem, narrays;
- dh = TAILQ_FIRST(&ufsdirhash_list);
- if (dh == NULL)
- return (-1);
-
- /*
- * If we can't lock it it's in use and we don't want to
- * destroy it anyway. Go on to the next in the list.
- */
- while (lockmgr(&dh->dh_lock, LK_EXCLUSIVE | LK_NOWAIT, NULL)) {
- dh = TAILQ_NEXT(dh, dh_list);
- if (dh == NULL)
- return (-1);
- }
KASSERT(dh->dh_hash != NULL, ("dirhash: NULL hash on list"));
/* Remove it from the list and detach its memory. */
@@ -1132,7 +1124,7 @@
/* Account for the returned memory. */
DIRHASHLIST_LOCK();
- ufs_dirhashmem -= mem;
+ ufs_dirhashmem -= mem;
return (mem);
}
@@ -1155,9 +1147,16 @@
return (-1);
}
- /* Try deleting a dirhash. Give up if we can't delete any. */
- if (ufsdirhash_destroy() < 0)
- return (-1);
+ /*
+ * If we can't lock it it's in use and we don't want to
+ * recycle it anyway.
+ */
+ if (lockmgr(&dh->dh_lock, LK_EXCLUSIVE | LK_NOWAIT, NULL)) {
+ dh = TAILQ_NEXT(dh, dh_list);
+ continue;
+ }
+
+ ufsdirhash_destroy(dh);
/* Repeat if necessary. */
dh = TAILQ_FIRST(&ufsdirhash_list);
@@ -1172,15 +1171,35 @@
static void
ufsdirhash_lowmem()
{
+ struct dirhash *dh;
+ int memfreed = 0;
+
ufs_dirhashlowmemcount++;
DIRHASHLIST_LOCK();
- if (ufs_dirhashmem > 0)
- /*
- * Try deleting only one dirhash for now, and don't bother
- * to check if it worked.
- */
- ufsdirhash_destroy();
+ /*
+ * 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();
}
More information about the p4-projects
mailing list