diff options
-rw-r--r-- | sys/kern/vfs_cache.c | 68 | ||||
-rw-r--r-- | sys/kern/vfs_export.c | 26 | ||||
-rw-r--r-- | sys/kern/vfs_subr.c | 26 | ||||
-rw-r--r-- | sys/sys/vnode.h | 1 |
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, |