40
/*
* The locking in this subsystem works as follows:
*
* When an entry is added to the cache, via cache_enter(),
* namecache_lock is taken to exclude other writers. The new
* entry is added to the hash list in a way which permits
* concurrent lookups and invalidations in the cache done on
* other CPUs to continue in parallel.
*
* When a lookup is done in the cache, via cache_lookup() or
* cache_lookup_raw(), the per-cpu lock below is taken. This
* protects calls to cache_lookup_entry() and cache_invalidate()
* against cache_reclaim() but allows lookups to continue in
* parallel with cache_enter().
*
* cache_revlookup() takes namecache_lock to exclude cache_enter()
* and cache_reclaim() since the list it operates on is not
* maintained to allow concurrent reads.
*
* When cache_reclaim() is called namecache_lock is held to hold
* off calls to cache_enter()/cache_revlookup() and each of the
* per-cpu locks is taken to hold off lookups. Holding all these
* locks essentially idles the subsystem, ensuring there are no
* concurrent references to the cache entries being freed.
*
* 32 bit per-cpu statistic counters (struct nchstats_percpu) are
* incremented when the operations they count are performed while
* running on the corresponding CPU. Frequently individual counters
* are incremented while holding a lock (either a per-cpu lock or
* namecache_lock) sufficient to preclude concurrent increments
* being done to the same counter, so non-atomic increments are
* done using the COUNT() macro. Counters which are incremented
* when one of these locks is not held use the COUNT_UNL() macro
* instead. COUNT_UNL() could be defined to do atomic increments
* but currently just does what COUNT() does, on the theory that
* it is unlikely the non-atomic increment will be interrupted
* by something on the same CPU that increments the same counter,
* but even if it does happen the consequences aren't serious.
*
* N.B.: Attempting to protect COUNT_UNL() increments by taking
* a per-cpu lock in the namecache_count_*() functions causes
* a deadlock. Don't do that, use atomic increments instead if
* the imperfections here bug you.
*
* The 64 bit system-wide statistic counts (struct nchstats) are
* maintained by sampling the per-cpu counters periodically, adding
* in the deltas since the last samples and recording the current
* samples to use to compute the next delta. The sampling is done
* as a side effect of cache_reclaim() which is run periodically,
* for its own purposes, often enough to avoid overflow of the 32
* bit counters. While sampling in this fashion requires no locking
* it is never-the-less done only after all locks have been taken by
* cache_reclaim() to allow cache_stat_sysctl() to hold off
* cache_reclaim() with minimal locking.
*
* cache_stat_sysctl() takes its CPU's per-cpu lock to hold off
* cache_reclaim() so that it can copy the subsystem total stats
* without them being concurrently modified. If CACHE_STATS_CURRENT
* is defined it also harvests the per-cpu increments into the total,
* which again requires cache_reclaim() to be held off.
*
* The per-cpu data (a lock and the per-cpu stats structures)
* are defined next.
*/