Index: kern/vfs_hash.c =================================================================== --- kern/vfs_hash.c (revision 281469) +++ kern/vfs_hash.c (working copy) @@ -161,3 +161,39 @@ vp->v_hash = hash; rw_wunlock(&vfs_hash_lock); } + +void +vfs_hash_changesize(int newhashsize) +{ + struct vfs_hash_head *vfs_hash_newtbl, *vfs_hash_oldtbl; + u_long vfs_hash_newmask, vfs_hash_oldmask; + struct vnode *vp; + int i; + + vfs_hash_newtbl = hashinit(newhashsize, M_VFS_HASH, &vfs_hash_newmask); + /* If same hash table size, nothing to do */ + if (vfs_hash_mask == vfs_hash_newmask) { + free(vfs_hash_newtbl, M_VFS_HASH); + return; + } + /* + * Move everything from the old hash table to the new table. + * None of the vnodes in the table can be recycled because to + * do so, they have to be removed from the hash table. + */ + rw_wlock(&vfs_hash_lock); + vfs_hash_oldtbl = vfs_hash_tbl; + vfs_hash_oldmask = vfs_hash_mask; + vfs_hash_tbl = vfs_hash_newtbl; + vfs_hash_mask = vfs_hash_newmask; + for (i = 0; i <= vfs_hash_oldmask; i++) { + while ((vp = LIST_FIRST(&vfs_hash_oldtbl[i])) != NULL) { + LIST_REMOVE(vp, v_hashlist); + LIST_INSERT_HEAD( + vfs_hash_bucket(vp->v_mount, vp->v_hash), + vp, v_hashlist); + } + } + rw_wunlock(&vfs_hash_lock); + free(vfs_hash_oldtbl, M_VFS_HASH); +} Index: kern/vfs_cache.c =================================================================== --- kern/vfs_cache.c (revision 281469) +++ kern/vfs_cache.c (working copy) @@ -96,6 +96,7 @@ TAILQ_ENTRY(namecache) nc_dst; /* destination vnode list */ struct vnode *nc_dvp; /* vnode of parent of name */ struct vnode *nc_vp; /* vnode the name refers to */ + u_long nc_hashval; /* hash value used for hasing list */ u_char nc_flag; /* flag bits */ u_char nc_nlen; /* length of name */ char nc_name[0]; /* segment name + nul */ @@ -115,6 +116,7 @@ TAILQ_ENTRY(namecache) nc_dst; /* destination vnode list */ struct vnode *nc_dvp; /* vnode of parent of name */ struct vnode *nc_vp; /* vnode the name refers to */ + u_long nc_hashval; /* hash value used for hasing list */ u_char nc_flag; /* flag bits */ u_char nc_nlen; /* length of name */ struct timespec nc_time; /* timespec provided by fs */ @@ -324,7 +326,7 @@ sysctl_debug_hashstat_rawnchash(SYSCTL_HANDLER_ARGS) { int error; - struct nchashhead *ncpp; + struct nchashhead *ncpp, starthashtbl; struct namecache *ncp; int n_nchash; int count; @@ -334,8 +336,14 @@ return SYSCTL_OUT(req, 0, n_nchash * sizeof(int)); /* Scan hash tables for applicable entries */ + starthashtbl = nchashtbl; for (ncpp = nchashtbl; n_nchash > 0; n_nchash--, ncpp++) { CACHE_RLOCK(); + /* If the hash table gets replaced, give up */ + if (starthashtbl != nchashtbl) { + CACHE_RUNLOCK(); + return (EAGAIN); + } count = 0; LIST_FOREACH(ncp, ncpp, nc_hash) { count++; @@ -363,6 +371,7 @@ if (!req->oldptr) return SYSCTL_OUT(req, 0, 4 * sizeof(int)); + CACHE_RLOCK(); n_nchash = nchash + 1; /* nchash is max index, not count */ used = 0; maxlength = 0; @@ -370,17 +379,16 @@ /* Scan hash tables for applicable entries */ for (ncpp = nchashtbl; n_nchash > 0; n_nchash--, ncpp++) { count = 0; - CACHE_RLOCK(); LIST_FOREACH(ncp, ncpp, nc_hash) { count++; } - CACHE_RUNLOCK(); if (count) used++; if (maxlength < count) maxlength = count; } n_nchash = nchash + 1; + CACHE_RUNLOCK(); pct = (used * 100) / (n_nchash / 100); error = SYSCTL_OUT(req, &n_nchash, sizeof(n_nchash)); if (error) @@ -872,6 +880,7 @@ * Insert the new namecache entry into the appropriate chain * within the cache entries table. */ + ncp->nc_hashval = hash; LIST_INSERT_HEAD(ncpp, ncp, nc_hash); if (flag != NCF_ISDOTDOT) { if (LIST_EMPTY(&dvp->v_cache_src)) { @@ -934,7 +943,41 @@ } SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_SECOND, nchinit, NULL); +void +cache_changesize(int newhashsize) +{ + struct nchashhead *new_nchashtbl, *old_nchashtbl; + u_long new_nchash, old_nchash; + struct namecache *ncp; + int i; + new_nchashtbl = hashinit(newhashsize, M_VFSCACHE, &new_nchash); + /* If same hash table size, nothing to do */ + if (nchash == new_nchash) { + free(new_nchashtbl, M_VFSCACHE); + return; + } + /* + * Move everything from the old hash table to the new table. + * None of the namecache entries in the table can be removed + * because to do so, they have to be removed from the hash table. + */ + CACHE_WLOCK(); + old_nchashtbl = nchashtbl; + old_nchash = nchash; + nchashtbl = new_nchashtbl; + nchash = new_nchash; + for (i = 0; i <= old_nchash; i++) { + while ((ncp = LIST_FIRST(&old_nchashtbl[i])) != NULL) { + LIST_REMOVE(ncp, nc_hash); + LIST_INSERT_HEAD(NCHHASH(ncp->nc_hashval), ncp, + nc_hash); + } + } + CACHE_WUNLOCK(); + free(old_nchashtbl, M_VFSCACHE); +} + /* * Invalidate all entries to a particular vnode. */ Index: kern/vfs_subr.c =================================================================== --- kern/vfs_subr.c (revision 281469) +++ kern/vfs_subr.c (working copy) @@ -275,8 +275,22 @@ * XXX desiredvnodes is historical cruft and should not exist. */ int desiredvnodes; -SYSCTL_INT(_kern, KERN_MAXVNODES, maxvnodes, CTLFLAG_RW, - &desiredvnodes, 0, "Maximum number of vnodes"); + +static int +sysctl_update_desiredvnodes(SYSCTL_HANDLER_ARGS) +{ + int error; + + if ((error = sysctl_handle_int(oidp, arg1, arg2, req)) != 0) + return (error); + vfs_hash_changesize(desiredvnodes); + cache_changesize(desiredvnodes); + return (0); +} + +SYSCTL_PROC(_kern, KERN_MAXVNODES, maxvnodes, + CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW, &desiredvnodes, 0, + sysctl_update_desiredvnodes, "I", "Maximum number of vnodes"); SYSCTL_ULONG(_kern, OID_AUTO, minvnodes, CTLFLAG_RW, &wantfreevnodes, 0, "Minimum number of vnodes (legacy)"); static int vnlru_nowhere; Index: sys/vnode.h =================================================================== --- sys/vnode.h (revision 281469) +++ sys/vnode.h (working copy) @@ -602,6 +602,7 @@ typedef int (*vn_get_ino_t)(struct mount *, void *, int, struct vnode **); /* cache_* may belong in namei.h. */ +void cache_changesize(int newhashsize); #define cache_enter(dvp, vp, cnp) \ cache_enter_time(dvp, vp, cnp, NULL, NULL) void cache_enter_time(struct vnode *dvp, struct vnode *vp, @@ -838,6 +839,7 @@ /* vfs_hash.c */ typedef int vfs_hash_cmp_t(struct vnode *vp, void *arg); +void vfs_hash_changesize(int newhashsize); int vfs_hash_get(const struct mount *mp, u_int hash, int flags, struct thread *td, struct vnode **vpp, vfs_hash_cmp_t *fn, void *arg); u_int vfs_hash_index(struct vnode *vp); int vfs_hash_insert(struct vnode *vp, u_int hash, int flags, struct thread *td, struct vnode **vpp, vfs_hash_cmp_t *fn, void *arg);