kernel - Reduce excessive inode hash table allocations
authorMatthew Dillon <dillon@apollo.backplane.com>
Sun, 16 Oct 2016 21:31:40 +0000 (14:31 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sun, 16 Oct 2016 21:35:03 +0000 (14:35 -0700)
* Reduce excessive inode hash table allocations in various filesystems
  (primarily ufs).  Introduce vfs_inodehashsize() to calculate a reasonable
  hash table size instead of using 'maxvnodes'.

* The new formula is to generally use maxvnodes / 2 (2 x stacking for the
  chained hash table).  When maxvnodes is large we use maxvnodes / 4, and
  if maxvnodes is very large (> 1M vnodes) we use maxvnodes / 8.  This
  significantly reduces the amount of kernel memory used when mounting
  ufs, ext2fs, hpfs, isofs, msdosfs, nfs, ntfs, and smbfs filesystems.

14 files changed:
sys/gnu/vfs/ext2fs/ext2_ihash.c
sys/gnu/vfs/ext2fs/ext2_quota.c
sys/kern/vfs_cache.c
sys/kern/vfs_subr.c
sys/sys/vnode.h
sys/vfs/hpfs/hpfs_hash.c
sys/vfs/isofs/cd9660/cd9660_node.c
sys/vfs/msdosfs/msdosfs_denode.c
sys/vfs/nfs/nfs_node.c
sys/vfs/ntfs/ntfs_ihash.c
sys/vfs/smbfs/smbfs_vfsops.c
sys/vfs/ufs/ffs_softdep.c
sys/vfs/ufs/ufs_ihash.c
sys/vfs/ufs/ufs_quota.c

index 345c11d..b5ddc89 100644 (file)
@@ -59,10 +59,10 @@ static struct lwkt_token ext2_ihash_token;
 void
 ext2_ihashinit(void)
 {
-       ext2_ihash = 16;
-       while (ext2_ihash < maxvnodes)
-               ext2_ihash <<= 1;
-       ext2_ihashtbl = kmalloc(sizeof(void *) * ext2_ihash, M_EXT2IHASH, M_WAITOK|M_ZERO);
+       ext2_ihash = vfs_inodehashsize();
+       ext2_ihashtbl = kmalloc(sizeof(void *) * ext2_ihash,
+                               M_EXT2IHASH,
+                               M_WAITOK|M_ZERO);
        --ext2_ihash;
        lwkt_token_init(&ext2_ihash_token, "ext2ihash");
 }
index 5a8c100..2fc5e4b 100644 (file)
@@ -695,7 +695,9 @@ static long ext2_numdquot, ext2_desireddquot = DQUOTINC;
 void
 ext2_dqinit(void)
 {
-       ext2_dqhashtbl = hashinit(maxvnodes, M_EXT2DQUOT, &ext2_dqhash);
+       int hsize = vfs_inodehashsize();
+
+       ext2_dqhashtbl = hashinit(hsize, M_EXT2DQUOT, &ext2_dqhash);
        TAILQ_INIT(&ext2_dqfreelist);
 }
 
@@ -737,6 +739,7 @@ ext2_dqget(struct vnode *vp, u_long id, struct ext2_mount *ump, int type,
                *dqp = dq;
                return (0);
        }
+
        /*
         * Not in cache, allocate a new one.
         */
@@ -745,7 +748,7 @@ ext2_dqget(struct vnode *vp, u_long id, struct ext2_mount *ump, int type,
                ext2_desireddquot += DQUOTINC;
        }
        if (ext2_numdquot < ext2_desireddquot) {
-               dq = (struct ext2_dquot *)kmalloc(sizeof *dq, M_EXT2DQUOT, M_WAITOK | M_ZERO);
+               dq = kmalloc(sizeof *dq, M_EXT2DQUOT, M_WAITOK | M_ZERO);
                ext2_numdquot++;
        } else {
                if ((dq = TAILQ_FIRST(&ext2_dqfreelist)) == NULL) {
index 35adafa..46f5680 100644 (file)
@@ -3691,14 +3691,20 @@ nchinit(void)
        int i;
        globaldata_t gd;
 
-       /* initialise per-cpu namecache effectiveness statistics. */
+       /*
+        * Initialise per-cpu namecache effectiveness statistics.
+        */
        for (i = 0; i < ncpus; ++i) {
                gd = globaldata_find(i);
                gd->gd_nchstats = &nchstats[i];
        }
+
+       /*
+        * Create a generous namecache hash table
+        */
        TAILQ_INIT(&ncneglist);
        spin_init(&ncspin, "nchinit");
-       nchashtbl = hashinit_ext(maxvnodes / 2,
+       nchashtbl = hashinit_ext(vfs_inodehashsize(),
                                 sizeof(struct nchash_head),
                                 M_VFSCACHE, &nchash);
        for (i = 0; i <= (int)nchash; ++i) {
index ae2814f..73470a9 100644 (file)
@@ -118,7 +118,7 @@ struct nfs_public nfs_pub;  /* publicly exported FS */
 
 int maxvnodes;
 SYSCTL_INT(_kern, KERN_MAXVNODES, maxvnodes, CTLFLAG_RW, 
-               &maxvnodes, 0, "Maximum number of vnodes");
+          &maxvnodes, 0, "Maximum number of vnodes");
 
 static struct radix_node_head *vfs_create_addrlist_af(int af,
                    struct netexport *nep);
@@ -2478,3 +2478,31 @@ vn_mark_atime(struct vnode *vp, struct thread *td)
                VOP_MARKATIME(vp, cred);
        }
 }
+
+/*
+ * Calculate the number of entries in an inode-related chained hash table.
+ * With today's memory sizes, maxvnodes can wind up being a very large
+ * number.  There is no reason to waste memory, so tolerate some stacking.
+ */
+int
+vfs_inodehashsize(void)
+{
+       int hsize;
+
+       hsize = 32;
+       while (hsize < maxvnodes)
+               hsize <<= 1;
+       while (hsize > maxvnodes * 2)
+               hsize >>= 1;            /* nominal 2x stacking */
+
+       if (maxvnodes > 1024 * 1024)
+               hsize >>= 1;            /* nominal 8x stacking */
+
+       if (maxvnodes > 128 * 1024)
+               hsize >>= 1;            /* nominal 4x stacking */
+
+       if (hsize < 16)
+               hsize = 16;
+
+       return hsize;
+}
index b808f93..2ddef90 100644 (file)
@@ -507,6 +507,7 @@ cdev_t      vn_todev (struct vnode *vp);
 void   vfs_timestamp (struct timespec *);
 size_t vfs_flagstostr(int flags, const struct mountctl_opt *optp, char *buf, size_t len, int *errorp);
 void   vn_mark_atime(struct vnode *vp, struct thread *td);
+int    vfs_inodehashsize(void);
 int    vn_writechk (struct vnode *vp, struct nchandle *nch);
 int    ncp_writechk(struct nchandle *nch);
 int    vop_stdopen (struct vop_open_args *ap);
index b1f3794..4f33356 100644 (file)
@@ -60,9 +60,10 @@ struct lock hpfs_hphash_lock;
 void
 hpfs_hphashinit(void)
 {
+       int hsize = vfs_inodehashsize();
 
        lockinit(&hpfs_hphash_lock, "hpfs_hphashlock", 0, 0);
-       hpfs_hphashtbl = hashinit(maxvnodes, M_HPFSHASH, &hpfs_hphash);
+       hpfs_hphashtbl = hashinit(hsize, M_HPFSHASH, &hpfs_hphash);
        lwkt_token_init(&hpfs_hphash_token, "hpfsihash");
 }
 
index 446112f..59395e3 100644 (file)
@@ -71,18 +71,21 @@ static unsigned     cd9660_chars2ui(unsigned char *begin, int len);
 int
 cd9660_init(struct vfsconf *vfsp)
 {
-       int hlimit;
+       int hsize;
 
-       if ((hlimit = maxvnodes) < CD9660_HASH_SIZE_LIMIT)
-               hlimit = CD9660_HASH_SIZE_LIMIT;
+       hsize = vfs_inodehashsize();
+
+       if (hsize < CD9660_HASH_SIZE_LIMIT)
+               hsize = CD9660_HASH_SIZE_LIMIT;
 
        isohash = 16;
-       while (isohash < hlimit)
+       while (isohash < hsize)
                isohash <<= 1;
        isohashtbl = kmalloc(sizeof(void *) * isohash,
-                           M_ISOFSMNT, M_WAITOK|M_ZERO);
+                            M_ISOFSMNT, M_WAITOK|M_ZERO);
        --isohash;
        lwkt_token_init(&cd9660_ihash_token, "cd9660ihash");
+
        return (0);
 }
 
index 9159410..730196d 100644 (file)
@@ -117,14 +117,12 @@ static struct denode *
 int 
 msdosfs_init(struct vfsconf *vfsp)
 {
-       dehash = 16;
-       while (dehash < maxvnodes)
-               dehash <<= 1;
+       dehash = vfs_inodehashsize();
        dehashtbl = kmalloc(sizeof(void *) * dehash,
-                           M_MSDOSFSMNT,
-                           M_WAITOK|M_ZERO);
+                           M_MSDOSFSMNT, M_WAITOK|M_ZERO);
        --dehash;
        lwkt_token_init(&dehash_token, "msdosihash");
+
        return (0);
 }
 
index f1b0073..89cc6c4 100644 (file)
@@ -71,8 +71,11 @@ static struct lock nfsnhash_lock;
 void
 nfs_nhinit(void)
 {
-       nfsnode_objcache = objcache_create_simple(M_NFSNODE, sizeof(struct nfsnode));
-       nfsnodehashtbl = hashinit(maxvnodes, M_NFSHASH, &nfsnodehash);
+       int hsize = vfs_inodehashsize();
+
+       nfsnode_objcache = objcache_create_simple(M_NFSNODE,
+                                                 sizeof(struct nfsnode));
+       nfsnodehashtbl = hashinit(hsize, M_NFSHASH, &nfsnodehash);
        lockinit(&nfsnhash_lock, "nfsnht", 0, 0);
 }
 
index 2e1ff36..183065a 100644 (file)
@@ -62,8 +62,10 @@ struct lock ntfs_hashlock;
 void
 ntfs_nthashinit(void)
 {
+       int hsize = vfs_inodehashsize();
+
        lockinit(&ntfs_hashlock, "ntfs_nthashlock", 0, 0);
-       ntfs_nthashtbl = hashinit(maxvnodes, M_NTFSNTHASH, &ntfs_nthash);
+       ntfs_nthashtbl = hashinit(hsize, M_NTFSNTHASH, &ntfs_nthash);
        lwkt_token_init(&ntfs_nthash_slock, "ntfsihash");
 }
 
index f238b85..e7e531b 100644 (file)
@@ -113,6 +113,7 @@ smbfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
        struct vnode *vp;
        struct smb_cred scred;
        int error;
+       int hsize;
        char *pc, *pe;
 
        if (data == NULL) {
@@ -144,7 +145,9 @@ smbfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
        smp = kmalloc(sizeof(*smp), M_SMBFSDATA, M_WAITOK | M_USE_RESERVE | M_ZERO);
        mp->mnt_data = (qaddr_t)smp;
        smp->sm_cred = crhold(cred);
-       smp->sm_hash = hashinit(maxvnodes, M_SMBFSHASH, &smp->sm_hashlen);
+
+       hsize = vfs_inodehashsize();
+       smp->sm_hash = hashinit(hsize, M_SMBFSHASH, &smp->sm_hashlen);
        if (smp->sm_hash == NULL)
                goto bad;
        lockinit(&smp->sm_hashlock, "smbfsh", 0, 0);
index e459c8a..c4e3dfa 100644 (file)
@@ -1042,14 +1042,17 @@ top:
 void 
 softdep_initialize(void)
 {
+       size_t idsize = sizeof(struct inodedep);
+       int hsize = vfs_inodehashsize();
+
        LIST_INIT(&mkdirlisthd);
        LIST_INIT(&softdep_workitem_pending);
-       max_softdeps = min(maxvnodes * 8,
-               M_INODEDEP->ks_limit / (2 * sizeof(struct inodedep)));
-       pagedep_hashtbl = hashinit(maxvnodes / 5, M_PAGEDEP, &pagedep_hash);
+       max_softdeps = min(maxvnodes * 8, M_INODEDEP->ks_limit / (2 * idsize));
+
+       pagedep_hashtbl = hashinit(hsize / 4, M_PAGEDEP, &pagedep_hash);
        lockinit(&lk, "ffs_softdep", 0, LK_CANRECURSE);
        sema_init(&pagedep_in_progress, "pagedep", 0);
-       inodedep_hashtbl = hashinit(maxvnodes, M_INODEDEP, &inodedep_hash);
+       inodedep_hashtbl = hashinit(hsize, M_INODEDEP, &inodedep_hash);
        sema_init(&inodedep_in_progress, "inodedep", 0);
        newblk_hashtbl = hashinit(64, M_NEWBLK, &newblk_hash);
        sema_init(&newblk_in_progress, "newblk", 0);
index 5e4e144..096011e 100644 (file)
@@ -55,11 +55,7 @@ static MALLOC_DEFINE(M_UFSIHASH, "UFS ihash", "UFS Inode hash tables");
 void
 ufs_ihashinit(struct ufsmount *ump)
 {
-       u_long target = maxvnodes / 4 + 1;
-
-       ump->um_ihash = 16;
-       while (ump->um_ihash < target)
-               ump->um_ihash <<= 1;
+       ump->um_ihash = vfs_inodehashsize();
        ump->um_ihashtbl = kmalloc(sizeof(void *) * ump->um_ihash,
                                   M_UFSIHASH,
                                   M_WAITOK|M_ZERO);
index 2d0a0bd..513fc23 100644 (file)
@@ -736,7 +736,9 @@ static long ufs_numdquot, ufs_desireddquot = DQUOTINC;
 void
 ufs_dqinit(void)
 {
-       ufs_dqhashtbl = hashinit(maxvnodes, M_DQUOT, &ufs_dqhash);
+       int hsize = vfs_inodehashsize();
+
+       ufs_dqhashtbl = hashinit(hsize, M_DQUOT, &ufs_dqhash);
        TAILQ_INIT(&ufs_dqfreelist);
 }