kernel - Fix namecache leak / broken hysteresis
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 23 Mar 2018 23:18:22 +0000 (16:18 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 23 Mar 2018 23:18:22 +0000 (16:18 -0700)
* We were double-using NCF_DESTROYED for both dummy ncp's for
  list iteration and to indicate a removed ncp.  This prevented
  the hysteresis code from properly cleaning up such ncp's.

  Symptoms were growing stalls every few seconds on one cpu as
  the hysteresis code would try to futiley clean up excess
  namecache entries.

* Use NCF_DUMMY instead of NCF_DESTROYED to check for dummy
  list iterators in the hysteresis code, allowing it to process
  NCF_DESTROYED ncps.

* These ncps could accumulate only in situations where the vnode
  nlinks count is greater than 1, since a file deletion when nlinks
  is 1 will delete the vnode and all related ncp's.

  When nlinks is greater than 1, a file entry deletion does not
  necessarily cause the vnode to be deleted, leaving the namecache
  records intact, but flagged NCF_DESTROYED, and prevented the
  hystersis code from operating correctly.

Reported-by: ftigeot
sys/kern/vfs_cache.c
sys/sys/namecache.h

index c01afe7..8e6638c 100644 (file)
@@ -3897,8 +3897,8 @@ _cache_cleanpos(long count)
                }
 
                /*
-                * Cycle ncp on list, ignore and do not move DESTROYED
-                * ncps (which might be dummies).
+                * Cycle ncp on list, ignore and do not move DUMMY
+                * ncps.  These are temporary list iterators.
                 *
                 * We must cycle the ncp to the end of the list to
                 * ensure that all ncp's have an equal chance of
@@ -3906,7 +3906,7 @@ _cache_cleanpos(long count)
                 */
                spin_lock(&nchpp->spin);
                ncp = TAILQ_FIRST(&nchpp->list);
-               while (ncp && (ncp->nc_flag & NCF_DESTROYED))
+               while (ncp && (ncp->nc_flag & NCF_DUMMY))
                        ncp = TAILQ_NEXT(ncp, nc_hash);
                if (ncp) {
                        TAILQ_REMOVE(&nchpp->list, ncp, nc_hash);
@@ -3944,9 +3944,13 @@ _cache_cleandefered(void)
        struct namecache dummy;
        int i;
 
+       /*
+        * Create a list iterator.  DUMMY indicates that this is a list
+        * iterator, DESTROYED prevents matches by lookup functions.
+        */
        numdefered = 0;
        bzero(&dummy, sizeof(dummy));
-       dummy.nc_flag = NCF_DESTROYED;
+       dummy.nc_flag = NCF_DESTROYED | NCF_DUMMY;
        dummy.nc_refs = 1;
 
        for (i = 0; i <= nchash; ++i) {
index 80733c9..21249d8 100644 (file)
@@ -159,6 +159,7 @@ struct nchandle {
 #define NCF_DESTROYED  0x0400  /* name association is considered destroyed */
 #define NCF_DEFEREDZAP 0x0800  /* zap defered due to lock unavailability */
 #define NCF_WXOK       0x1000  /* world-searchable (nlookup shortcut) */
+#define NCF_DUMMY      0x2000  /* dummy ncp, iterations ignore it */
 
 #define NC_EXLOCK_REQ  0x80000000      /* nc_lockstatus state flag */
 #define NC_SHLOCK_REQ  0x40000000      /* nc_lockstatus state flag */