Module Name:    src
Committed By:   ad
Date:           Sun Mar 22 14:16:50 UTC 2020

Modified Files:
        src/sys/kern [ad-namecache]: vfs_cache.c

Log Message:
Resurrect the counter stuff from HEAD, which tries to deal with 32-bit
counter rollover.  Drive it by sysctl and/or callout every 5 minutes,
since there's no garbage collection kthread any more.


To generate a diff of this commit:
cvs rdiff -u -r1.126.2.16 -r1.126.2.17 src/sys/kern/vfs_cache.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/vfs_cache.c
diff -u src/sys/kern/vfs_cache.c:1.126.2.16 src/sys/kern/vfs_cache.c:1.126.2.17
--- src/sys/kern/vfs_cache.c:1.126.2.16	Sun Mar 22 01:58:22 2020
+++ src/sys/kern/vfs_cache.c	Sun Mar 22 14:16:50 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: vfs_cache.c,v 1.126.2.16 2020/03/22 01:58:22 ad Exp $	*/
+/*	$NetBSD: vfs_cache.c,v 1.126.2.17 2020/03/22 14:16:50 ad Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2019, 2020 The NetBSD Foundation, Inc.
@@ -153,9 +153,10 @@
  *	and the definition of "struct vnode" for the particulars.
  *
  *	Per-CPU statistics, and LRU list totals are read unlocked, since
- *	an approximate value is OK.  We maintain uintptr_t sized per-CPU
- *	counters and 64-bit global counters under the theory that uintptr_t
- *	sized counters are less likely to be hosed by nonatomic increment.
+ *	an approximate value is OK.  We maintain 32-bit sized per-CPU
+ *	counters and 64-bit global counters under the theory that 32-bit
+ *	sized counters are less likely to be hosed by nonatomic increment
+ *	(on 32-bit platforms).
  *
  *	The lock order is:
  *
@@ -171,7 +172,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_cache.c,v 1.126.2.16 2020/03/22 01:58:22 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_cache.c,v 1.126.2.17 2020/03/22 14:16:50 ad Exp $");
 
 #define __NAMECACHE_PRIVATE
 #ifdef _KERNEL_OPT
@@ -180,6 +181,8 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_cache.c,
 #endif
 
 #include <sys/types.h>
+#include <sys/atomic.h>
+#include <sys/callout.h>
 #include <sys/cpu.h>
 #include <sys/errno.h>
 #include <sys/evcnt.h>
@@ -199,15 +202,13 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_cache.c,
 #include <miscfs/genfs/genfs.h>
 
 static void	cache_activate(struct namecache *);
+static void	cache_update_stats(void *);
 static int	cache_compare_key(void *, const void *, const void *);
 static int	cache_compare_nodes(void *, const void *, const void *);
 static void	cache_deactivate(void);
 static void	cache_reclaim(void);
 static int	cache_stat_sysctl(SYSCTLFN_ARGS);
 
-/* Per-CPU counters. */
-struct nchstats_percpu _NAMEI_CACHE_STATS(uintptr_t);
-
 /* Global pool cache. */
 static pool_cache_t cache_pool __read_mostly;
 
@@ -225,8 +226,15 @@ static struct {
 
 static kmutex_t cache_lru_lock __cacheline_aligned;
 
-/* Cache effectiveness statistics.  This holds total from per-cpu stats */
-struct nchstats	nchstats __cacheline_aligned;
+/* Cache effectiveness statistics.  nchstats holds system-wide total. */
+struct nchstats	nchstats;
+struct nchstats_percpu _NAMEI_CACHE_STATS(uint32_t);
+struct nchcpu {
+	struct nchstats_percpu cur;
+	struct nchstats_percpu last;
+};
+static callout_t cache_stat_callout;
+static kmutex_t cache_stat_lock __cacheline_aligned;
 
 #define	COUNT(f)	do { \
 	lwp_t *l = curlwp; \
@@ -235,6 +243,12 @@ struct nchstats	nchstats __cacheline_ali
 	KPREEMPT_ENABLE(l); \
 } while (/* CONSTCOND */ 0);
 
+#define	UPDATE(nchcpu, f) do { \
+	uint32_t cur = atomic_load_relaxed(&nchcpu->cur.f); \
+	nchstats.f += cur - nchcpu->last.f; \
+	nchcpu->last.f = cur; \
+} while (/* CONSTCOND */ 0)
+
 /*
  * Tunables.  cache_maxlen replaces the historical doingcache:
  * set it zero to disable caching for debugging purposes.
@@ -242,6 +256,7 @@ struct nchstats	nchstats __cacheline_ali
 int cache_lru_maxdeact __read_mostly = 2;	/* max # to deactivate */
 int cache_lru_maxscan __read_mostly = 64;	/* max # to scan/reclaim */
 int cache_maxlen __read_mostly = USHRT_MAX;	/* max name length to cache */
+int cache_stat_interval __read_mostly = 300;	/* in seconds */
 
 /* sysctl */
 static struct	sysctllog *cache_sysctllog;
@@ -1000,6 +1015,11 @@ nchinit(void)
 	TAILQ_INIT(&cache_lru.list[LRU_ACTIVE]);
 	TAILQ_INIT(&cache_lru.list[LRU_INACTIVE]);
 
+	mutex_init(&cache_stat_lock, MUTEX_DEFAULT, IPL_NONE);
+	callout_init(&cache_stat_callout, CALLOUT_MPSAFE);
+	callout_setfunc(&cache_stat_callout, cache_update_stats, NULL);
+	callout_schedule(&cache_stat_callout, cache_stat_interval * hz);
+
 	KASSERT(cache_sysctllog == NULL);
 	sysctl_createv(&cache_sysctllog, 0, NULL, NULL,
 		       CTLFLAG_PERMANENT,
@@ -1362,6 +1382,41 @@ namecache_count_2passes(void)
 }
 
 /*
+ * Sum the stats from all CPUs into nchstats.  This needs to run at least
+ * once within every window where a 32-bit counter could roll over.  It's
+ * called regularly by timer to ensure this.
+ */
+static void
+cache_update_stats(void *cookie)
+{
+	CPU_INFO_ITERATOR cii;
+	struct cpu_info *ci;
+
+	mutex_enter(&cache_stat_lock);
+	for (CPU_INFO_FOREACH(cii, ci)) {
+		struct nchcpu *nchcpu = ci->ci_data.cpu_nch;
+		UPDATE(nchcpu, ncs_goodhits);
+		UPDATE(nchcpu, ncs_neghits);
+		UPDATE(nchcpu, ncs_badhits);
+		UPDATE(nchcpu, ncs_falsehits);
+		UPDATE(nchcpu, ncs_miss);
+		UPDATE(nchcpu, ncs_long);
+		UPDATE(nchcpu, ncs_pass2);
+		UPDATE(nchcpu, ncs_2passes);
+		UPDATE(nchcpu, ncs_revhits);
+		UPDATE(nchcpu, ncs_revmiss);
+		UPDATE(nchcpu, ncs_collisions);
+		UPDATE(nchcpu, ncs_denied);
+	}
+	if (cookie != NULL) {
+		memcpy(cookie, &nchstats, sizeof(nchstats));
+	}
+	/* Reset the timer; arrive back here in N minutes at latest. */
+	callout_schedule(&cache_stat_callout, cache_stat_interval * hz);
+	mutex_exit(&cache_stat_lock);
+}
+
+/*
  * Fetch the current values of the stats.  We return the most
  * recent values harvested into nchstats by cache_reclaim(), which
  * will be less than a second old.
@@ -1369,9 +1424,7 @@ namecache_count_2passes(void)
 static int
 cache_stat_sysctl(SYSCTLFN_ARGS)
 {
-	CPU_INFO_ITERATOR cii;
 	struct nchstats stats;
-	struct cpu_info *ci;
 
 	if (oldp == NULL) {
 		*oldlenp = sizeof(nchstats);
@@ -1383,27 +1436,9 @@ cache_stat_sysctl(SYSCTLFN_ARGS)
 		return 0;
 	}
 
+	/* Refresh the global stats. */
 	sysctl_unlock();
-	memset(&stats, 0, sizeof(nchstats));
-	for (CPU_INFO_FOREACH(cii, ci)) {
-		struct nchstats_percpu *np = ci->ci_data.cpu_nch;
-
-		stats.ncs_goodhits += np->ncs_goodhits;
-		stats.ncs_neghits += np->ncs_neghits;
-		stats.ncs_badhits += np->ncs_badhits;
-		stats.ncs_falsehits += np->ncs_falsehits;
-		stats.ncs_miss += np->ncs_miss;
-		stats.ncs_long += np->ncs_long;
-		stats.ncs_pass2 += np->ncs_pass2;
-		stats.ncs_2passes += np->ncs_2passes;
-		stats.ncs_revhits += np->ncs_revhits;
-		stats.ncs_revmiss += np->ncs_revmiss;
-		stats.ncs_collisions += np->ncs_collisions;
-		stats.ncs_denied += np->ncs_denied;
-	}
-	mutex_enter(&cache_lru_lock);
-	memcpy(&nchstats, &stats, sizeof(nchstats));
-	mutex_exit(&cache_lru_lock);
+	cache_update_stats(&stats);
 	sysctl_relock();
 
 	*oldlenp = MIN(sizeof(stats), *oldlenp);

Reply via email to