kernel - Fix two rare namecache bugs
authorMatthew Dillon <dillon@apollo.backplane.com>
Sun, 11 Feb 2018 06:11:18 +0000 (22:11 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sun, 11 Feb 2018 06:11:18 +0000 (22:11 -0800)
* Fix calculations which use the vfscache_negs global.  This global
  is somewhat heuristical and can values which are a bit off, including
  0.  Copy to a local and limit the range, fixing a divide-by-zero
  bug and a negative-number handling bug.

* Fix a bug in the handling of a race in _cache_cleanneg().  We were
  unlocking the ncp but failing to drop it, leaving it with a ref.
  The accumulating namecache records prevent umount from succeeding.

  This race can only occur regularly when kern.maxvnodes is set to
  a low value.

sys/kern/vfs_cache.c

index e2e0315..3d70b50 100644 (file)
@@ -3811,14 +3811,27 @@ _cache_cleanneg(long count)
        struct namecache *ncp;
        static uint32_t neg_rover;
        uint32_t n;
+       long vnegs;
 
        n = neg_rover++;        /* SMP heuristical, race ok */
        cpu_ccfence();
        n = n % (uint32_t)ncpus;
 
+       /*
+        * Normalize vfscache_negs and count.  count is sometimes based
+        * on vfscache_negs.  vfscache_negs is heuristical and can sometimes
+        * have crazy values.
+        */
+       vnegs = vfscache_negs;
+       cpu_ccfence();
+       if (vnegs <= MINNEG)
+               vnegs = MINNEG;
+       if (count < 1)
+               count = 1;
+
        pn = &pcpu_ncache[n];
        spin_lock(&pn->neg_spin);
-       count = pn->neg_count * count / vfscache_negs + 1;
+       count = pn->neg_count * count / vnegs + 1;
        spin_unlock(&pn->neg_spin);
 
        /*
@@ -3848,8 +3861,8 @@ _cache_cleanneg(long count)
                                if (ncp)
                                        _cache_drop(ncp);
                        } else {
-                               kprintf("cache_cleanneg: race avoided\n");
                                _cache_unlock(ncp);
+                               _cache_drop(ncp);
                        }
                } else {
                        _cache_drop(ncp);