summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/kern/vfs_cache.c68
-rw-r--r--sys/kern/vfs_export.c26
-rw-r--r--sys/kern/vfs_subr.c26
-rw-r--r--sys/sys/vnode.h1
4 files changed, 119 insertions, 2 deletions
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
index bf792cd..2299e70 100644
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -98,6 +98,10 @@ static u_long numneg; /* number of cache entries allocated */
SYSCTL_ULONG(_debug, OID_AUTO, numneg, CTLFLAG_RD, &numneg, 0, "");
static u_long numcache; /* number of cache entries allocated */
SYSCTL_ULONG(_debug, OID_AUTO, numcache, CTLFLAG_RD, &numcache, 0, "");
+static u_long numcachehv; /* number of cache entries with vnodes held */
+SYSCTL_ULONG(_debug, OID_AUTO, numcachehv, CTLFLAG_RD, &numcachehv, 0, "");
+static u_long numcachepl; /* number of cache purge for leaf entries */
+SYSCTL_ULONG(_debug, OID_AUTO, numcachepl, CTLFLAG_RD, &numcachepl, 0, "");
struct nchstats nchstats; /* cache effectiveness statistics */
static int doingcache = 1; /* 1 => enable the cache */
@@ -227,8 +231,10 @@ cache_zap(ncp)
{
LIST_REMOVE(ncp, nc_hash);
LIST_REMOVE(ncp, nc_src);
- if (LIST_EMPTY(&ncp->nc_dvp->v_cache_src))
+ if (LIST_EMPTY(&ncp->nc_dvp->v_cache_src)) {
vdrop(ncp->nc_dvp);
+ numcachehv--;
+ }
if (ncp->nc_vp) {
TAILQ_REMOVE(&ncp->nc_vp->v_cache_dst, ncp, nc_dst);
} else {
@@ -404,8 +410,10 @@ cache_enter(dvp, vp, cnp)
hash = fnv_32_buf(&dvp->v_id, sizeof(dvp->v_id), hash);
ncpp = NCHHASH(hash);
LIST_INSERT_HEAD(ncpp, ncp, nc_hash);
- if (LIST_EMPTY(&dvp->v_cache_src))
+ if (LIST_EMPTY(&dvp->v_cache_src)) {
vhold(dvp);
+ numcachehv++;
+ }
LIST_INSERT_HEAD(&dvp->v_cache_src, ncp, nc_src);
if (vp) {
TAILQ_INSERT_HEAD(&vp->v_cache_dst, ncp, nc_dst);
@@ -491,6 +499,62 @@ cache_purgevfs(mp)
}
/*
+ * Flush all dirctory entries with no child directories held in
+ * the cache.
+ *
+ * Since we need to check it anyway, we will flush all the invalid
+ * entries at the same time.
+ */
+void
+cache_purgeleafdirs(ndir)
+ int ndir;
+{
+ struct nchashhead *ncpp;
+ struct namecache *ncp, *nnp, *ncpc, *nnpc;
+ struct vnode *dvp;
+
+ /* Scan hash tables for applicable entries */
+ for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl && ndir > 0; ncpp--) {
+ for (ncp = LIST_FIRST(ncpp); ncp != 0 && ndir > 0; ncp = nnp) {
+ nnp = LIST_NEXT(ncp, nc_hash);
+ if (ncp->nc_dvp != 0) {
+ /*
+ * Skip over if nc_dvp of this cache holds
+ * a child directory, or the hold count of
+ * nc_dvp is greater than 1 (in which case
+ * nc_dvp is likely to be the working
+ * directory of a process).
+ */
+ if (ncp->nc_dvp->v_holdcnt > 1)
+ continue;
+ for (ncpc = LIST_FIRST(&ncp->nc_dvp->v_cache_src);
+ ncpc != 0; ncpc = nnpc) {
+ nnpc = LIST_NEXT(ncpc, nc_src);
+ if (ncpc->nc_vp != 0 && ncpc->nc_vp->v_type == VDIR)
+ break;
+ }
+ if (ncpc == 0) {
+ /*
+ * Zap all of this directory's children,
+ * held in ncp->nc_dvp->v_cache_src.
+ */
+ dvp = ncp->nc_dvp;
+ while (!LIST_EMPTY(&dvp->v_cache_src))
+ cache_zap(LIST_FIRST(&dvp->v_cache_src));
+
+ ndir--;
+
+ /* Restart in case where nnp is reclaimed. */
+ nnp = LIST_FIRST(ncpp);
+ continue;
+ }
+ }
+ }
+ }
+ numcachepl++;
+}
+
+/*
* Perform canonical checks and cache lookup and pass on to filesystem
* through the vop_cachedlookup only if needed.
*/
diff --git a/sys/kern/vfs_export.c b/sys/kern/vfs_export.c
index 29dec8a..853bf76 100644
--- a/sys/kern/vfs_export.c
+++ b/sys/kern/vfs_export.c
@@ -122,6 +122,21 @@ SYSCTL_LONG(_debug, OID_AUTO, wantfreevnodes, CTLFLAG_RW, &wantfreevnodes, 0, ""
/* Number of vnodes in the free list. */
static u_long freevnodes = 0;
SYSCTL_LONG(_debug, OID_AUTO, freevnodes, CTLFLAG_RD, &freevnodes, 0, "");
+/* Number of vnode allocation. */
+static u_long vnodeallocs = 0;
+SYSCTL_LONG(_debug, OID_AUTO, vnodeallocs, CTLFLAG_RD, &vnodeallocs, 0, "");
+/* Period of vnode recycle from namecache in vnode allocation times. */
+static u_long vnoderecycleperiod = 1000;
+SYSCTL_LONG(_debug, OID_AUTO, vnoderecycleperiod, CTLFLAG_RW, &vnoderecycleperiod, 0, "");
+/* Minimum number of total vnodes required to invoke vnode recycle from namecache. */
+static u_long vnoderecyclemintotalvn = 2000;
+SYSCTL_LONG(_debug, OID_AUTO, vnoderecyclemintotalvn, CTLFLAG_RW, &vnoderecyclemintotalvn, 0, "");
+/* Minimum number of free vnodes required to invoke vnode recycle from namecache. */
+static u_long vnoderecycleminfreevn = 2000;
+SYSCTL_LONG(_debug, OID_AUTO, vnoderecycleminfreevn, CTLFLAG_RW, &vnoderecycleminfreevn, 0, "");
+/* Number of vnodes attempted to recycle at a time. */
+static u_long vnoderecyclenumber = 3000;
+SYSCTL_LONG(_debug, OID_AUTO, vnoderecyclenumber, CTLFLAG_RW, &vnoderecyclenumber, 0, "");
/*
* Various variables used for debugging the new implementation of
@@ -553,6 +568,7 @@ getnewvnode(tag, mp, vops, vpp)
if (vp == NULL || vp->v_usecount)
panic("getnewvnode: free vnode isn't");
TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
+
/*
* Don't recycle if active in the namecache or
* if it still has cached pages or we cannot get
@@ -632,9 +648,19 @@ getnewvnode(tag, mp, vops, vpp)
*vpp = vp;
vp->v_usecount = 1;
vp->v_data = 0;
+
splx(s);
vfs_object_create(vp, p, p->p_ucred);
+
+ vnodeallocs++;
+ if (vnodeallocs % vnoderecycleperiod == 0 &&
+ freevnodes < vnoderecycleminfreevn &&
+ vnoderecyclemintotalvn < numvnodes) {
+ /* Recycle vnodes. */
+ cache_purgeleafdirs(vnoderecyclenumber);
+ }
+
return (0);
}
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index 29dec8a..853bf76 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -122,6 +122,21 @@ SYSCTL_LONG(_debug, OID_AUTO, wantfreevnodes, CTLFLAG_RW, &wantfreevnodes, 0, ""
/* Number of vnodes in the free list. */
static u_long freevnodes = 0;
SYSCTL_LONG(_debug, OID_AUTO, freevnodes, CTLFLAG_RD, &freevnodes, 0, "");
+/* Number of vnode allocation. */
+static u_long vnodeallocs = 0;
+SYSCTL_LONG(_debug, OID_AUTO, vnodeallocs, CTLFLAG_RD, &vnodeallocs, 0, "");
+/* Period of vnode recycle from namecache in vnode allocation times. */
+static u_long vnoderecycleperiod = 1000;
+SYSCTL_LONG(_debug, OID_AUTO, vnoderecycleperiod, CTLFLAG_RW, &vnoderecycleperiod, 0, "");
+/* Minimum number of total vnodes required to invoke vnode recycle from namecache. */
+static u_long vnoderecyclemintotalvn = 2000;
+SYSCTL_LONG(_debug, OID_AUTO, vnoderecyclemintotalvn, CTLFLAG_RW, &vnoderecyclemintotalvn, 0, "");
+/* Minimum number of free vnodes required to invoke vnode recycle from namecache. */
+static u_long vnoderecycleminfreevn = 2000;
+SYSCTL_LONG(_debug, OID_AUTO, vnoderecycleminfreevn, CTLFLAG_RW, &vnoderecycleminfreevn, 0, "");
+/* Number of vnodes attempted to recycle at a time. */
+static u_long vnoderecyclenumber = 3000;
+SYSCTL_LONG(_debug, OID_AUTO, vnoderecyclenumber, CTLFLAG_RW, &vnoderecyclenumber, 0, "");
/*
* Various variables used for debugging the new implementation of
@@ -553,6 +568,7 @@ getnewvnode(tag, mp, vops, vpp)
if (vp == NULL || vp->v_usecount)
panic("getnewvnode: free vnode isn't");
TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
+
/*
* Don't recycle if active in the namecache or
* if it still has cached pages or we cannot get
@@ -632,9 +648,19 @@ getnewvnode(tag, mp, vops, vpp)
*vpp = vp;
vp->v_usecount = 1;
vp->v_data = 0;
+
splx(s);
vfs_object_create(vp, p, p->p_ucred);
+
+ vnodeallocs++;
+ if (vnodeallocs % vnoderecycleperiod == 0 &&
+ freevnodes < vnoderecycleminfreevn &&
+ vnoderecyclemintotalvn < numvnodes) {
+ /* Recycle vnodes. */
+ cache_purgeleafdirs(vnoderecyclenumber);
+ }
+
return (0);
}
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index 760fad7..719d1d3 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -552,6 +552,7 @@ int cache_lookup __P((struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp));
void cache_purge __P((struct vnode *vp));
void cache_purgevfs __P((struct mount *mp));
+void cache_purgeleafdirs __P((int ndir));
void cvtstat __P((struct stat *st, struct ostat *ost));
void cvtnstat __P((struct stat *sb, struct nstat *nsb));
int getnewvnode __P((enum vtagtype tag,
OpenPOWER on IntegriCloud