Module Name: src
Committed By: ad
Date: Sun Jun 14 21:34:25 UTC 2020
Modified Files:
src/sys/kern: subr_pool.c
src/sys/sys: pool.h
src/usr.bin/vmstat: vmstat.c
Log Message:
pool_cache:
- make all counters per-CPU and make cache layer do its work with atomic ops.
- conserve memory by caching empty groups globally.
To generate a diff of this commit:
cvs rdiff -u -r1.270 -r1.271 src/sys/kern/subr_pool.c
cvs rdiff -u -r1.90 -r1.91 src/sys/sys/pool.h
cvs rdiff -u -r1.240 -r1.241 src/usr.bin/vmstat/vmstat.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/subr_pool.c
diff -u src/sys/kern/subr_pool.c:1.270 src/sys/kern/subr_pool.c:1.271
--- src/sys/kern/subr_pool.c:1.270 Sun Jun 7 09:45:19 2020
+++ src/sys/kern/subr_pool.c Sun Jun 14 21:34:25 2020
@@ -1,8 +1,8 @@
-/* $NetBSD: subr_pool.c,v 1.270 2020/06/07 09:45:19 maxv Exp $ */
+/* $NetBSD: subr_pool.c,v 1.271 2020/06/14 21:34:25 ad Exp $ */
/*
- * Copyright (c) 1997, 1999, 2000, 2002, 2007, 2008, 2010, 2014, 2015, 2018
- * The NetBSD Foundation, Inc.
+ * Copyright (c) 1997, 1999, 2000, 2002, 2007, 2008, 2010, 2014, 2015, 2018,
+ * 2020 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_pool.c,v 1.270 2020/06/07 09:45:19 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_pool.c,v 1.271 2020/06/14 21:34:25 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_ddb.h"
@@ -52,6 +52,7 @@ __KERNEL_RCSID(0, "$NetBSD: subr_pool.c,
#include <sys/pool.h>
#include <sys/syslog.h>
#include <sys/debug.h>
+#include <sys/lock.h>
#include <sys/lockdebug.h>
#include <sys/xcall.h>
#include <sys/cpu.h>
@@ -337,6 +338,9 @@ static struct pool pcg_large_pool;
static struct pool cache_pool;
static struct pool cache_cpu_pool;
+static pcg_t *volatile pcg_large_cache __cacheline_aligned;
+static pcg_t *volatile pcg_normal_cache __cacheline_aligned;
+
/* List of all caches. */
TAILQ_HEAD(,pool_cache) pool_cache_head =
TAILQ_HEAD_INITIALIZER(pool_cache_head);
@@ -344,14 +348,17 @@ TAILQ_HEAD(,pool_cache) pool_cache_head
int pool_cache_disable; /* global disable for caching */
static const pcg_t pcg_dummy; /* zero sized: always empty, yet always full */
-static bool pool_cache_put_slow(pool_cache_cpu_t *, int,
+static bool pool_cache_put_slow(pool_cache_t, pool_cache_cpu_t *, int,
void *);
-static bool pool_cache_get_slow(pool_cache_cpu_t *, int,
+static bool pool_cache_get_slow(pool_cache_t, pool_cache_cpu_t *, int,
void **, paddr_t *, int);
static void pool_cache_cpu_init1(struct cpu_info *, pool_cache_t);
-static void pool_cache_invalidate_groups(pool_cache_t, pcg_t *);
+static int pool_cache_invalidate_groups(pool_cache_t, pcg_t *);
static void pool_cache_invalidate_cpu(pool_cache_t, u_int);
static void pool_cache_transfer(pool_cache_t);
+static int pool_pcg_get(pcg_t *volatile *, pcg_t **);
+static int pool_pcg_put(pcg_t *volatile *, pcg_t *);
+static pcg_t * pool_pcg_trunc(pcg_t *volatile *);
static int pool_catchup(struct pool *);
static void pool_prime_page(struct pool *, void *,
@@ -1804,7 +1811,8 @@ pool_print1(struct pool *pp, const char
pool_cache_t pc;
pcg_t *pcg;
pool_cache_cpu_t *cc;
- uint64_t cpuhit, cpumiss;
+ uint64_t cpuhit, cpumiss, pchit, pcmiss;
+ uint32_t nfull;
int i, print_log = 0, print_pagelist = 0, print_cache = 0;
char c;
@@ -1881,31 +1889,26 @@ pool_print1(struct pool *pp, const char
if (pc != NULL) {
cpuhit = 0;
cpumiss = 0;
+ pcmiss = 0;
+ nfull = 0;
for (i = 0; i < __arraycount(pc->pc_cpus); i++) {
if ((cc = pc->pc_cpus[i]) == NULL)
continue;
cpuhit += cc->cc_hits;
cpumiss += cc->cc_misses;
+ pcmiss += cc->cc_pcmisses;
+ nfull += cc->cc_nfull;
}
+ pchit = cpumiss - pcmiss;
(*pr)("\tcpu layer hits %llu misses %llu\n", cpuhit, cpumiss);
- (*pr)("\tcache layer hits %llu misses %llu\n",
- pc->pc_hits, pc->pc_misses);
- (*pr)("\tcache layer entry uncontended %llu contended %llu\n",
- pc->pc_hits + pc->pc_misses - pc->pc_contended,
- pc->pc_contended);
- (*pr)("\tcache layer empty groups %u full groups %u\n",
- pc->pc_nempty, pc->pc_nfull);
+ (*pr)("\tcache layer hits %llu misses %llu\n", pchit, pcmiss);
+ (*pr)("\tcache layer full groups %u\n", nfull);
if (print_cache) {
(*pr)("\tfull cache groups:\n");
for (pcg = pc->pc_fullgroups; pcg != NULL;
pcg = pcg->pcg_next) {
PR_GROUPLIST(pcg);
}
- (*pr)("\tempty cache groups:\n");
- for (pcg = pc->pc_emptygroups; pcg != NULL;
- pcg = pcg->pcg_next) {
- PR_GROUPLIST(pcg);
- }
}
}
#undef PR_GROUPLIST
@@ -2051,7 +2054,6 @@ pool_cache_bootstrap(pool_cache_t pc, si
palloc = &pool_allocator_nointr;
}
pool_init(pp, size, align, align_offset, flags, wchan, palloc, ipl);
- mutex_init(&pc->pc_lock, MUTEX_DEFAULT, ipl);
if (ctor == NULL) {
ctor = NO_CTOR;
@@ -2060,27 +2062,22 @@ pool_cache_bootstrap(pool_cache_t pc, si
dtor = NO_DTOR;
}
- pc->pc_emptygroups = NULL;
pc->pc_fullgroups = NULL;
pc->pc_partgroups = NULL;
pc->pc_ctor = ctor;
pc->pc_dtor = dtor;
pc->pc_arg = arg;
- pc->pc_hits = 0;
- pc->pc_misses = 0;
- pc->pc_nempty = 0;
- pc->pc_npart = 0;
- pc->pc_nfull = 0;
- pc->pc_contended = 0;
pc->pc_refcnt = 0;
pc->pc_freecheck = NULL;
if ((flags & PR_LARGECACHE) != 0) {
pc->pc_pcgsize = PCG_NOBJECTS_LARGE;
pc->pc_pcgpool = &pcg_large_pool;
+ pc->pc_pcgcache = &pcg_large_cache;
} else {
pc->pc_pcgsize = PCG_NOBJECTS_NORMAL;
pc->pc_pcgpool = &pcg_normal_pool;
+ pc->pc_pcgcache = &pcg_normal_cache;
}
/* Allocate per-CPU caches. */
@@ -2157,7 +2154,6 @@ pool_cache_bootstrap_destroy(pool_cache_
pool_cache_invalidate_cpu(pc, i);
/* Finally, destroy it. */
- mutex_destroy(&pc->pc_lock);
pool_destroy(pp);
}
@@ -2177,7 +2173,6 @@ pool_cache_cpu_init1(struct cpu_info *ci
KASSERT(index < __arraycount(pc->pc_cpus));
if ((cc = pc->pc_cpus[index]) != NULL) {
- KASSERT(cc->cc_cpuindex == index);
return;
}
@@ -2189,20 +2184,19 @@ pool_cache_cpu_init1(struct cpu_info *ci
cc = &pc->pc_cpu0;
pc->pc_ncpu = 1;
} else {
- mutex_enter(&pc->pc_lock);
pc->pc_ncpu++;
- mutex_exit(&pc->pc_lock);
cc = pool_get(&cache_cpu_pool, PR_WAITOK);
}
- cc->cc_ipl = pc->pc_pool.pr_ipl;
- cc->cc_iplcookie = makeiplcookie(cc->cc_ipl);
- cc->cc_cache = pc;
- cc->cc_cpuindex = index;
- cc->cc_hits = 0;
- cc->cc_misses = 0;
cc->cc_current = __UNCONST(&pcg_dummy);
cc->cc_previous = __UNCONST(&pcg_dummy);
+ cc->cc_pcgcache = pc->pc_pcgcache;
+ cc->cc_hits = 0;
+ cc->cc_misses = 0;
+ cc->cc_pcmisses = 0;
+ cc->cc_contended = 0;
+ cc->cc_nfull = 0;
+ cc->cc_npart = 0;
pc->pc_cpus[index] = cc;
}
@@ -2268,16 +2262,17 @@ pool_cache_destruct_object(pool_cache_t
/*
* pool_cache_invalidate_groups:
*
- * Invalidate a chain of groups and destruct all objects.
+ * Invalidate a chain of groups and destruct all objects. Return the
+ * number of groups that were invalidated.
*/
-static void
+static int
pool_cache_invalidate_groups(pool_cache_t pc, pcg_t *pcg)
{
void *object;
pcg_t *next;
- int i;
+ int i, n;
- for (; pcg != NULL; pcg = next) {
+ for (n = 0; pcg != NULL; pcg = next, n++) {
next = pcg->pcg_next;
for (i = 0; i < pcg->pcg_avail; i++) {
@@ -2292,6 +2287,7 @@ pool_cache_invalidate_groups(pool_cache_
pool_put(&pcg_normal_pool, pcg);
}
}
+ return n;
}
/*
@@ -2311,7 +2307,8 @@ void
pool_cache_invalidate(pool_cache_t pc)
{
uint64_t where;
- pcg_t *full, *empty, *part;
+ pcg_t *pcg;
+ int n, s;
KASSERT(!cpu_intr_p() && !cpu_softintr_p());
@@ -2335,22 +2332,24 @@ pool_cache_invalidate(pool_cache_t pc)
xc_wait(where);
}
- /* Empty pool caches, then invalidate objects */
- mutex_enter(&pc->pc_lock);
- full = pc->pc_fullgroups;
- empty = pc->pc_emptygroups;
- part = pc->pc_partgroups;
- pc->pc_fullgroups = NULL;
- pc->pc_emptygroups = NULL;
- pc->pc_partgroups = NULL;
- pc->pc_nfull = 0;
- pc->pc_nempty = 0;
- pc->pc_npart = 0;
- mutex_exit(&pc->pc_lock);
-
- pool_cache_invalidate_groups(pc, full);
- pool_cache_invalidate_groups(pc, empty);
- pool_cache_invalidate_groups(pc, part);
+ /* Now dequeue and invalidate everything. */
+ pcg = pool_pcg_trunc(&pcg_normal_cache);
+ (void)pool_cache_invalidate_groups(pc, pcg);
+
+ pcg = pool_pcg_trunc(&pcg_large_cache);
+ (void)pool_cache_invalidate_groups(pc, pcg);
+
+ pcg = pool_pcg_trunc(&pc->pc_fullgroups);
+ n = pool_cache_invalidate_groups(pc, pcg);
+ s = splvm();
+ ((pool_cache_cpu_t *)pc->pc_cpus[curcpu()->ci_index])->cc_nfull -= n;
+ splx(s);
+
+ pcg = pool_pcg_trunc(&pc->pc_partgroups);
+ n = pool_cache_invalidate_groups(pc, pcg);
+ s = splvm();
+ ((pool_cache_cpu_t *)pc->pc_cpus[curcpu()->ci_index])->cc_npart -= n;
+ splx(s);
}
/*
@@ -2421,61 +2420,131 @@ pool_cache_prime(pool_cache_t pc, int n)
pool_prime(&pc->pc_pool, n);
}
+/*
+ * pool_pcg_get:
+ *
+ * Get a cache group from the specified list. Return true if
+ * contention was encountered. Must be called at IPL_VM because
+ * of spin wait vs. kernel_lock.
+ */
+static int
+pool_pcg_get(pcg_t *volatile *head, pcg_t **pcgp)
+{
+ int count = SPINLOCK_BACKOFF_MIN;
+ pcg_t *o, *n;
+
+ for (o = atomic_load_relaxed(head);; o = n) {
+ if (__predict_false(o == &pcg_dummy)) {
+ /* Wait for concurrent get to complete. */
+ SPINLOCK_BACKOFF(count);
+ n = atomic_load_relaxed(head);
+ continue;
+ }
+ if (__predict_false(o == NULL)) {
+ break;
+ }
+ /* Lock out concurrent get/put. */
+ n = atomic_cas_ptr(head, o, __UNCONST(&pcg_dummy));
+ if (o == n) {
+ /* Fetch pointer to next item and then unlock. */
+#ifndef __HAVE_ATOMIC_AS_MEMBAR
+ membar_datadep_consumer(); /* alpha */
+#endif
+ n = atomic_load_relaxed(&o->pcg_next);
+ atomic_store_release(head, n);
+ break;
+ }
+ }
+ *pcgp = o;
+ return count != SPINLOCK_BACKOFF_MIN;
+}
+
+/*
+ * pool_pcg_trunc:
+ *
+ * Chop out entire list of pool cache groups.
+ */
+static pcg_t *
+pool_pcg_trunc(pcg_t *volatile *head)
+{
+ int count = SPINLOCK_BACKOFF_MIN, s;
+ pcg_t *o, *n;
+
+ s = splvm();
+ for (o = atomic_load_relaxed(head);; o = n) {
+ if (__predict_false(o == &pcg_dummy)) {
+ /* Wait for concurrent get to complete. */
+ SPINLOCK_BACKOFF(count);
+ n = atomic_load_relaxed(head);
+ continue;
+ }
+ n = atomic_cas_ptr(head, o, NULL);
+ if (o == n) {
+ splx(s);
+#ifndef __HAVE_ATOMIC_AS_MEMBAR
+ membar_datadep_consumer(); /* alpha */
+#endif
+ return o;
+ }
+ }
+}
+
+/*
+ * pool_pcg_put:
+ *
+ * Put a pool cache group to the specified list. Return true if
+ * contention was encountered. Must be called at IPL_VM because of
+ * spin wait vs. kernel_lock.
+ */
+static int
+pool_pcg_put(pcg_t *volatile *head, pcg_t *pcg)
+{
+ int count = SPINLOCK_BACKOFF_MIN;
+ pcg_t *o, *n;
+
+ for (o = atomic_load_relaxed(head);; o = n) {
+ if (__predict_false(o == &pcg_dummy)) {
+ /* Wait for concurrent get to complete. */
+ SPINLOCK_BACKOFF(count);
+ n = atomic_load_relaxed(head);
+ continue;
+ }
+ pcg->pcg_next = o;
+#ifndef __HAVE_ATOMIC_AS_MEMBAR
+ membar_exit();
+#endif
+ n = atomic_cas_ptr(head, o, pcg);
+ if (o == n) {
+ return count != SPINLOCK_BACKOFF_MIN;
+ }
+ }
+}
+
static bool __noinline
-pool_cache_get_slow(pool_cache_cpu_t *cc, int s, void **objectp,
- paddr_t *pap, int flags)
+pool_cache_get_slow(pool_cache_t pc, pool_cache_cpu_t *cc, int s,
+ void **objectp, paddr_t *pap, int flags)
{
pcg_t *pcg, *cur;
- uint64_t ncsw;
- pool_cache_t pc;
void *object;
KASSERT(cc->cc_current->pcg_avail == 0);
KASSERT(cc->cc_previous->pcg_avail == 0);
- pc = cc->cc_cache;
cc->cc_misses++;
/*
- * Nothing was available locally. Try and grab a group
- * from the cache.
+ * If there's a full group, release our empty group back to the
+ * cache. Install the full group as cc_current and return.
*/
- if (__predict_false(!mutex_tryenter(&pc->pc_lock))) {
- ncsw = curlwp->l_ncsw;
- __insn_barrier();
- mutex_enter(&pc->pc_lock);
- pc->pc_contended++;
-
- /*
- * If we context switched while locking, then
- * our view of the per-CPU data is invalid:
- * retry.
- */
- __insn_barrier();
- if (curlwp->l_ncsw != ncsw) {
- mutex_exit(&pc->pc_lock);
- return true;
- }
- }
-
- if (__predict_true((pcg = pc->pc_fullgroups) != NULL)) {
- /*
- * If there's a full group, release our empty
- * group back to the cache. Install the full
- * group as cc_current and return.
- */
+ cc->cc_contended += pool_pcg_get(&pc->pc_fullgroups, &pcg);
+ if (__predict_true(pcg != NULL)) {
+ KASSERT(pcg->pcg_avail == pcg->pcg_size);
if (__predict_true((cur = cc->cc_current) != &pcg_dummy)) {
KASSERT(cur->pcg_avail == 0);
- cur->pcg_next = pc->pc_emptygroups;
- pc->pc_emptygroups = cur;
- pc->pc_nempty++;
+ (void)pool_pcg_put(cc->cc_pcgcache, cur);
}
- KASSERT(pcg->pcg_avail == pcg->pcg_size);
+ cc->cc_nfull--;
cc->cc_current = pcg;
- pc->pc_fullgroups = pcg->pcg_next;
- pc->pc_hits++;
- pc->pc_nfull--;
- mutex_exit(&pc->pc_lock);
return true;
}
@@ -2484,8 +2553,7 @@ pool_cache_get_slow(pool_cache_cpu_t *cc
* path: fetch a new object from the pool and construct
* it.
*/
- pc->pc_misses++;
- mutex_exit(&pc->pc_lock);
+ cc->cc_pcmisses++;
splx(s);
object = pool_get(&pc->pc_pool, flags);
@@ -2549,7 +2617,6 @@ pool_cache_get_paddr(pool_cache_t pc, in
while (/* CONSTCOND */ true) {
/* Try and allocate an object from the current group. */
cc = pc->pc_cpus[curcpu()->ci_index];
- KASSERT(cc->cc_cache == pc);
pcg = cc->cc_current;
if (__predict_true(pcg->pcg_avail > 0)) {
object = pcg->pcg_objects[--pcg->pcg_avail].pcgo_va;
@@ -2585,7 +2652,7 @@ pool_cache_get_paddr(pool_cache_t pc, in
* no more objects are available, it will return false.
* Otherwise, we need to retry.
*/
- if (!pool_cache_get_slow(cc, s, &object, pap, flags)) {
+ if (!pool_cache_get_slow(pc, cc, s, &object, pap, flags)) {
if (object != NULL) {
kmsan_orig(object, pc->pc_pool.pr_size,
KMSAN_TYPE_POOL, __RET_ADDR);
@@ -2603,77 +2670,33 @@ pool_cache_get_paddr(pool_cache_t pc, in
}
static bool __noinline
-pool_cache_put_slow(pool_cache_cpu_t *cc, int s, void *object)
+pool_cache_put_slow(pool_cache_t pc, pool_cache_cpu_t *cc, int s, void *object)
{
- struct lwp *l = curlwp;
pcg_t *pcg, *cur;
- uint64_t ncsw;
- pool_cache_t pc;
KASSERT(cc->cc_current->pcg_avail == cc->cc_current->pcg_size);
KASSERT(cc->cc_previous->pcg_avail == cc->cc_previous->pcg_size);
- pc = cc->cc_cache;
- pcg = NULL;
cc->cc_misses++;
- ncsw = l->l_ncsw;
- __insn_barrier();
/*
- * If there are no empty groups in the cache then allocate one
- * while still unlocked.
+ * Try to get an empty group from the cache. If there are no empty
+ * groups in the cache then allocate one.
*/
- if (__predict_false(pc->pc_emptygroups == NULL)) {
+ (void)pool_pcg_get(cc->cc_pcgcache, &pcg);
+ if (__predict_false(pcg == NULL)) {
if (__predict_true(!pool_cache_disable)) {
pcg = pool_get(pc->pc_pcgpool, PR_NOWAIT);
}
- /*
- * If pool_get() blocked, then our view of
- * the per-CPU data is invalid: retry.
- */
- __insn_barrier();
- if (__predict_false(l->l_ncsw != ncsw)) {
- if (pcg != NULL) {
- pool_put(pc->pc_pcgpool, pcg);
- }
- return true;
- }
if (__predict_true(pcg != NULL)) {
pcg->pcg_avail = 0;
pcg->pcg_size = pc->pc_pcgsize;
}
}
- /* Lock the cache. */
- if (__predict_false(!mutex_tryenter(&pc->pc_lock))) {
- mutex_enter(&pc->pc_lock);
- pc->pc_contended++;
-
- /*
- * If we context switched while locking, then our view of
- * the per-CPU data is invalid: retry.
- */
- __insn_barrier();
- if (__predict_false(l->l_ncsw != ncsw)) {
- mutex_exit(&pc->pc_lock);
- if (pcg != NULL) {
- pool_put(pc->pc_pcgpool, pcg);
- }
- return true;
- }
- }
-
- /* If there are no empty groups in the cache then allocate one. */
- if (pcg == NULL && pc->pc_emptygroups != NULL) {
- pcg = pc->pc_emptygroups;
- pc->pc_emptygroups = pcg->pcg_next;
- pc->pc_nempty--;
- }
-
/*
- * If there's a empty group, release our full group back
- * to the cache. Install the empty group to the local CPU
- * and return.
+ * If there's a empty group, release our full group back to the
+ * cache. Install the empty group to the local CPU and return.
*/
if (pcg != NULL) {
KASSERT(pcg->pcg_avail == 0);
@@ -2683,14 +2706,12 @@ pool_cache_put_slow(pool_cache_cpu_t *cc
cur = cc->cc_current;
if (__predict_true(cur != &pcg_dummy)) {
KASSERT(cur->pcg_avail == cur->pcg_size);
- cur->pcg_next = pc->pc_fullgroups;
- pc->pc_fullgroups = cur;
- pc->pc_nfull++;
+ cc->cc_contended +=
+ pool_pcg_put(&pc->pc_fullgroups, cur);
+ cc->cc_nfull++;
}
cc->cc_current = pcg;
}
- pc->pc_hits++;
- mutex_exit(&pc->pc_lock);
return true;
}
@@ -2699,8 +2720,7 @@ pool_cache_put_slow(pool_cache_cpu_t *cc
* allocate an empty group. Take the slow path and destroy
* the object here and now.
*/
- pc->pc_misses++;
- mutex_exit(&pc->pc_lock);
+ cc->cc_pcmisses++;
splx(s);
pool_cache_destruct_object(pc, object);
@@ -2738,7 +2758,6 @@ pool_cache_put_paddr(pool_cache_t pc, vo
while (/* CONSTCOND */ true) {
/* If the current group isn't full, release it there. */
cc = pc->pc_cpus[curcpu()->ci_index];
- KASSERT(cc->cc_cache == pc);
pcg = cc->cc_current;
if (__predict_true(pcg->pcg_avail < pcg->pcg_size)) {
pcg->pcg_objects[pcg->pcg_avail].pcgo_va = object;
@@ -2765,7 +2784,7 @@ pool_cache_put_paddr(pool_cache_t pc, vo
* If put_slow() releases the object for us, it
* will return false. Otherwise we need to retry.
*/
- if (!pool_cache_put_slow(cc, s, object))
+ if (!pool_cache_put_slow(pc, cc, s, object))
break;
}
}
@@ -2780,11 +2799,10 @@ static void
pool_cache_transfer(pool_cache_t pc)
{
pool_cache_cpu_t *cc;
- pcg_t *prev, *cur, **list;
+ pcg_t *prev, *cur;
int s;
s = splvm();
- mutex_enter(&pc->pc_lock);
cc = pc->pc_cpus[curcpu()->ci_index];
cur = cc->cc_current;
cc->cc_current = __UNCONST(&pcg_dummy);
@@ -2792,33 +2810,26 @@ pool_cache_transfer(pool_cache_t pc)
cc->cc_previous = __UNCONST(&pcg_dummy);
if (cur != &pcg_dummy) {
if (cur->pcg_avail == cur->pcg_size) {
- list = &pc->pc_fullgroups;
- pc->pc_nfull++;
+ (void)pool_pcg_put(&pc->pc_fullgroups, cur);
+ cc->cc_nfull++;
} else if (cur->pcg_avail == 0) {
- list = &pc->pc_emptygroups;
- pc->pc_nempty++;
+ (void)pool_pcg_put(pc->pc_pcgcache, cur);
} else {
- list = &pc->pc_partgroups;
- pc->pc_npart++;
+ (void)pool_pcg_put(&pc->pc_partgroups, cur);
+ cc->cc_npart++;
}
- cur->pcg_next = *list;
- *list = cur;
}
if (prev != &pcg_dummy) {
if (prev->pcg_avail == prev->pcg_size) {
- list = &pc->pc_fullgroups;
- pc->pc_nfull++;
+ (void)pool_pcg_put(&pc->pc_fullgroups, prev);
+ cc->cc_nfull++;
} else if (prev->pcg_avail == 0) {
- list = &pc->pc_emptygroups;
- pc->pc_nempty++;
+ (void)pool_pcg_put(pc->pc_pcgcache, prev);
} else {
- list = &pc->pc_partgroups;
- pc->pc_npart++;
+ (void)pool_pcg_put(&pc->pc_partgroups, prev);
+ cc->cc_npart++;
}
- prev->pcg_next = *list;
- *list = prev;
}
- mutex_exit(&pc->pc_lock);
splx(s);
}
@@ -3325,31 +3336,32 @@ pool_sysctl(SYSCTLFN_ARGS)
data.pr_cache_nmiss_pcpu = 0;
data.pr_cache_nhit_pcpu = 0;
+ data.pr_cache_nmiss_global = 0;
+ data.pr_cache_nempty = 0;
+ data.pr_cache_ncontended = 0;
+ data.pr_cache_npartial = 0;
if (pp->pr_cache) {
+ uint32_t nfull = 0;
pc = pp->pr_cache;
data.pr_cache_meta_size = pc->pc_pcgsize;
- data.pr_cache_nfull = pc->pc_nfull;
- data.pr_cache_npartial = pc->pc_npart;
- data.pr_cache_nempty = pc->pc_nempty;
- data.pr_cache_ncontended = pc->pc_contended;
- data.pr_cache_nmiss_global = pc->pc_misses;
- data.pr_cache_nhit_global = pc->pc_hits;
for (i = 0; i < pc->pc_ncpu; ++i) {
cc = pc->pc_cpus[i];
if (cc == NULL)
continue;
+ data.pr_cache_ncontended += cc->cc_contended;
data.pr_cache_nmiss_pcpu += cc->cc_misses;
data.pr_cache_nhit_pcpu += cc->cc_hits;
+ data.pr_cache_nmiss_global += cc->cc_pcmisses;
+ nfull += cc->cc_nfull; /* 32-bit rollover! */
+ data.pr_cache_npartial = cc->cc_npart;
}
+ data.pr_cache_nfull = nfull;
} else {
data.pr_cache_meta_size = 0;
data.pr_cache_nfull = 0;
- data.pr_cache_npartial = 0;
- data.pr_cache_nempty = 0;
- data.pr_cache_ncontended = 0;
- data.pr_cache_nmiss_global = 0;
- data.pr_cache_nhit_global = 0;
}
+ data.pr_cache_nhit_global = data.pr_cache_nmiss_pcpu -
+ data.pr_cache_nmiss_global;
error = sysctl_copyout(l, &data, oldp, sizeof(data));
if (error)
Index: src/sys/sys/pool.h
diff -u src/sys/sys/pool.h:1.90 src/sys/sys/pool.h:1.91
--- src/sys/sys/pool.h:1.90 Mon Apr 13 00:27:17 2020
+++ src/sys/sys/pool.h Sun Jun 14 21:34:25 2020
@@ -1,7 +1,8 @@
-/* $NetBSD: pool.h,v 1.90 2020/04/13 00:27:17 chs Exp $ */
+/* $NetBSD: pool.h,v 1.91 2020/06/14 21:34:25 ad Exp $ */
/*-
- * Copyright (c) 1997, 1998, 1999, 2000, 2007 The NetBSD Foundation, Inc.
+ * Copyright (c) 1997, 1998, 1999, 2000, 2007, 2020
+ * The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@@ -216,7 +217,7 @@ struct pool {
/*
* Cache group sizes, assuming 4-byte paddr_t on !_LP64.
- * All groups will be aligned to CACHE_LINE_SIZE.
+ * All groups will be aligned to COHERENCY_UNIT.
*/
#ifdef _LP64
#define PCG_NOBJECTS_NORMAL 15 /* 256 byte group */
@@ -239,17 +240,17 @@ typedef struct pool_cache_group {
pcgpair_t pcg_objects[1]; /* the objects */
} pcg_t;
+/* Pool cache CPU. Sized to 64 bytes on _LP64. */
typedef struct pool_cache_cpu {
- uint64_t cc_misses;
- uint64_t cc_hits;
struct pool_cache_group *cc_current;
struct pool_cache_group *cc_previous;
- struct pool_cache *cc_cache;
- int cc_ipl;
- int cc_cpuindex;
-#ifdef _KERNEL
- ipl_cookie_t cc_iplcookie;
-#endif
+ pcg_t *volatile *cc_pcgcache;
+ uint64_t cc_misses;
+ uint64_t cc_hits;
+ uint64_t cc_pcmisses;
+ uint64_t cc_contended;
+ uint32_t cc_nfull;
+ uint32_t cc_npart;
} pool_cache_cpu_t;
struct pool_cache {
@@ -257,34 +258,30 @@ struct pool_cache {
struct pool pc_pool;
/* Cache layer. */
- kmutex_t pc_lock; /* locks cache layer */
TAILQ_ENTRY(pool_cache)
pc_cachelist; /* entry on global cache list */
- pcg_t *pc_emptygroups;/* list of empty cache groups */
- pcg_t *pc_fullgroups; /* list of full cache groups */
- pcg_t *pc_partgroups; /* groups for reclamation */
struct pool *pc_pcgpool; /* Pool of cache groups */
+ pcg_t *volatile *pc_pcgcache; /* list of empty cache groups */
int pc_pcgsize; /* Use large cache groups? */
int pc_ncpu; /* number cpus set up */
int (*pc_ctor)(void *, void *, int);
void (*pc_dtor)(void *, void *);
void *pc_arg; /* for ctor/ctor */
- uint64_t pc_hits; /* cache layer hits */
- uint64_t pc_misses; /* cache layer misses */
- uint64_t pc_contended; /* contention events on cache */
- unsigned int pc_nempty; /* empty groups in cache */
- unsigned int pc_nfull; /* full groups in cache */
- unsigned int pc_npart; /* partial groups in cache */
unsigned int pc_refcnt; /* ref count for pagedaemon, etc */
+ void *pc_cpus[MAXCPUS];
/* Diagnostic aides. */
void *pc_freecheck;
bool pc_redzone;
size_t pc_reqsize;
- /* CPU layer. */
+ /* Hot items. */
+ pcg_t *volatile pc_fullgroups /* list of full cache groups */
+ __aligned(CACHE_LINE_SIZE);
+ pcg_t *volatile pc_partgroups; /* groups for reclamation */
+
+ /* Boot cpu. */
pool_cache_cpu_t pc_cpu0 __aligned(CACHE_LINE_SIZE);
- void *pc_cpus[MAXCPUS] __aligned(CACHE_LINE_SIZE);
};
#endif /* __POOL_EXPOSE */
Index: src/usr.bin/vmstat/vmstat.c
diff -u src/usr.bin/vmstat/vmstat.c:1.240 src/usr.bin/vmstat/vmstat.c:1.241
--- src/usr.bin/vmstat/vmstat.c:1.240 Thu Jun 11 22:21:05 2020
+++ src/usr.bin/vmstat/vmstat.c Sun Jun 14 21:34:25 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: vmstat.c,v 1.240 2020/06/11 22:21:05 ad Exp $ */
+/* $NetBSD: vmstat.c,v 1.241 2020/06/14 21:34:25 ad Exp $ */
/*-
* Copyright (c) 1998, 2000, 2001, 2007, 2019, 2020
@@ -71,7 +71,7 @@ __COPYRIGHT("@(#) Copyright (c) 1980, 19
#if 0
static char sccsid[] = "@(#)vmstat.c 8.2 (Berkeley) 3/1/95";
#else
-__RCSID("$NetBSD: vmstat.c,v 1.240 2020/06/11 22:21:05 ad Exp $");
+__RCSID("$NetBSD: vmstat.c,v 1.241 2020/06/14 21:34:25 ad Exp $");
#endif
#endif /* not lint */
@@ -1796,7 +1796,8 @@ dopoolcache(int verbose)
TAILQ_HEAD(,pool) pool_head;
struct pool pool, *pp = &pool;
char name[32];
- uint64_t cpuhit, cpumiss, tot;
+ uint64_t cpuhit, cpumiss, pchit, pcmiss, contended, tot;
+ uint32_t nfull;
void *addr;
int first, ovflw;
size_t i;
@@ -1815,12 +1816,13 @@ dopoolcache(int verbose)
deref_kptr(pp->pr_wchan, name, sizeof(name),
"pool wait channel trashed");
deref_kptr(pp->pr_cache, pc, sizeof(*pc), "pool cache trashed");
- if (pc->pc_misses == 0 && !verbose)
- continue;
name[sizeof(name)-1] = '\0';
cpuhit = 0;
cpumiss = 0;
+ pcmiss = 0;
+ contended = 0;
+ nfull = 0;
for (i = 0; i < __arraycount(pc->pc_cpus); i++) {
if ((addr = pc->pc_cpus[i]) == NULL)
continue;
@@ -1828,7 +1830,14 @@ dopoolcache(int verbose)
"pool cache cpu trashed");
cpuhit += cc->cc_hits;
cpumiss += cc->cc_misses;
+ pcmiss += cc->cc_pcmisses;
+ nfull += cc->cc_nfull;
+ contended += cc->cc_contended;
}
+ pchit = cpumiss - pcmiss;
+
+ if (pcmiss == 0 && !verbose)
+ continue;
if (first) {
(void)printf("Pool cache statistics.\n");
@@ -1849,14 +1858,14 @@ dopoolcache(int verbose)
ovflw = 0;
PRWORD(ovflw, "%-*s", 13, 1, name);
- PRWORD(ovflw, " %*llu", 6, 1, (long long)pc->pc_contended);
+ PRWORD(ovflw, " %*llu", 6, 1, (long long)contended);
PRWORD(ovflw, " %*u", 6, 1, pc->pc_pcgsize);
- PRWORD(ovflw, " %*u", 5, 1, pc->pc_nfull);
- PRWORD(ovflw, " %*u", 5, 1, pc->pc_nempty);
- PRWORD(ovflw, " %*llu", 10, 1, (long long)pc->pc_misses);
+ PRWORD(ovflw, " %*u", 5, 1, nfull);
+ PRWORD(ovflw, " %*u", 5, 1, 0);
+ PRWORD(ovflw, " %*llu", 10, 1, (long long)pcmiss);
- tot = pc->pc_hits + pc->pc_misses;
- p = pc->pc_hits * 100.0 / (tot);
+ tot = pchit + pcmiss;
+ p = pchit * 100.0 / (tot);
PRWORD(ovflw, " %*llu", 11, 1, (long long)tot);
PRWORD(ovflw, " %*.1f", 6, 1, p);