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. */