From 5ecd24e2200bb7738582682e773ea5ec189d5e12 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Sat, 10 Feb 2018 22:11:18 -0800 Subject: [PATCH] kernel - Fix two rare namecache bugs * 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 | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index e2e0315975..3d70b5081c 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -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); -- 2.41.0