Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src-draft/trunk]: src/sys/kern Teach percpu to run constructors on CPU attach.
details: https://anonhg.NetBSD.org/src-all/rev/f96050b5dc22
branches: trunk
changeset: 932578:f96050b5dc22
user: Taylor R Campbell <riastradh%NetBSD.org@localhost>
date: Mon May 11 18:01:19 2020 +0000
description:
Teach percpu to run constructors on CPU attach.
This should make percpu work reliably for things like cprng_strong
and cprng_fast much earlier -- not just after all CPUs have been
detected.
diffstat:
sys/kern/subr_percpu.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 85 insertions(+), 0 deletions(-)
diffs (158 lines):
diff -r 71a19ee790d6 -r f96050b5dc22 sys/kern/subr_percpu.c
--- a/sys/kern/subr_percpu.c Mon May 11 17:24:04 2020 +0000
+++ b/sys/kern/subr_percpu.c Mon May 11 18:01:19 2020 +0000
@@ -50,8 +50,10 @@
struct percpu {
unsigned pc_offset;
size_t pc_size;
+ percpu_callback_t pc_ctor;
percpu_callback_t pc_dtor;
void *pc_cookie;
+ LIST_ENTRY(percpu) pc_list;
};
static krwlock_t percpu_swap_lock __cacheline_aligned;
@@ -59,6 +61,9 @@
static struct {
kmutex_t lock;
unsigned int nextoff;
+ LIST_HEAD(, percpu) ctor_list;
+ struct lwp *busy;
+ kcondvar_t cv;
} percpu_allocation __cacheline_aligned;
static percpu_cpu_t *
@@ -217,6 +222,9 @@
rw_init(&percpu_swap_lock);
mutex_init(&percpu_allocation.lock, MUTEX_DEFAULT, IPL_NONE);
percpu_allocation.nextoff = PERCPU_QUANTUM_SIZE;
+ LIST_INIT(&percpu_allocation.ctor_list);
+ percpu_allocation.busy = NULL;
+ cv_init(&percpu_allocation.cv, "percpu");
percpu_offset_arena = vmem_xcreate("percpu", 0, 0, PERCPU_QUANTUM_SIZE,
percpu_backend_alloc, NULL, NULL, PERCPU_QCACHE_MAX, VM_SLEEP,
@@ -227,18 +235,50 @@
* percpu_init_cpu: cpu initialization
*
* => should be called before the cpu appears on the list for CPU_INFO_FOREACH.
+ * => may be called for static CPUs afterward (typically just primary CPU)
*/
void
percpu_init_cpu(struct cpu_info *ci)
{
percpu_cpu_t * const pcc = cpu_percpu(ci);
+ struct percpu *pc;
size_t size = percpu_allocation.nextoff; /* XXX racy */
ASSERT_SLEEPABLE();
+
+ /*
+ * For the primary CPU, prior percpu_create may have already
+ * triggered allocation, so there's nothing more for us to do
+ * here.
+ */
+ if (pcc->pcc_size)
+ return;
+ KASSERT(pcc->pcc_data == NULL);
+
+ /*
+ * Otherwise, allocate storage and, while the constructor list
+ * is locked, run constructors for all percpus on this CPU.
+ */
pcc->pcc_size = size;
if (size) {
pcc->pcc_data = kmem_zalloc(pcc->pcc_size, KM_SLEEP);
+ mutex_enter(&percpu_allocation.lock);
+ while (percpu_allocation.busy)
+ cv_wait(&percpu_allocation.cv,
+ &percpu_allocation.lock);
+ percpu_allocation.busy = curlwp;
+ LIST_FOREACH(pc, &percpu_allocation.ctor_list, pc_list) {
+ KASSERT(pc->pc_ctor);
+ mutex_exit(&percpu_allocation.lock);
+ (*pc->pc_ctor)((char *)pcc->pcc_data + pc->pc_offset,
+ pc->pc_cookie, ci);
+ mutex_enter(&percpu_allocation.lock);
+ }
+ KASSERT(percpu_allocation.busy == curlwp);
+ percpu_allocation.busy = NULL;
+ cv_broadcast(&percpu_allocation.cv);
+ mutex_exit(&percpu_allocation.lock);
}
}
@@ -281,6 +321,7 @@
pc = kmem_alloc(sizeof(*pc), KM_SLEEP);
pc->pc_offset = offset;
pc->pc_size = size;
+ pc->pc_ctor = ctor;
pc->pc_dtor = dtor;
pc->pc_cookie = cookie;
@@ -289,6 +330,22 @@
struct cpu_info *ci;
void *buf;
+ /*
+ * Wait until nobody is using the list of percpus with
+ * constructors.
+ */
+ mutex_enter(&percpu_allocation.lock);
+ while (percpu_allocation.busy)
+ cv_wait(&percpu_allocation.cv,
+ &percpu_allocation.lock);
+ percpu_allocation.busy = curlwp;
+ mutex_exit(&percpu_allocation.lock);
+
+ /*
+ * Run the constructor for all CPUs. We use a
+ * temporary buffer wo that we need not hold the
+ * percpu_swap_lock while running the constructor.
+ */
buf = kmem_alloc(size, KM_SLEEP);
for (CPU_INFO_FOREACH(cii, ci)) {
memset(buf, 0, size);
@@ -299,6 +356,19 @@
}
explicit_memset(buf, 0, size);
kmem_free(buf, size);
+
+ /*
+ * Insert the percpu into the list of percpus with
+ * constructors. We are now done using the list, so it
+ * is safe for concurrent percpu_create or concurrent
+ * percpu_init_cpu to run.
+ */
+ mutex_enter(&percpu_allocation.lock);
+ KASSERT(percpu_allocation.busy == curlwp);
+ percpu_allocation.busy = NULL;
+ cv_broadcast(&percpu_allocation.cv);
+ LIST_INSERT_HEAD(&percpu_allocation.ctor_list, pc, pc_list);
+ mutex_exit(&percpu_allocation.lock);
} else {
percpu_zero(pc, size);
}
@@ -320,6 +390,21 @@
ASSERT_SLEEPABLE();
KASSERT(size == pc->pc_size);
+ /*
+ * If there's a constructor, take the percpu off the list of
+ * percpus with constructors, but first wait until nobody is
+ * using the list.
+ */
+ if (pc->pc_ctor) {
+ mutex_enter(&percpu_allocation.lock);
+ while (percpu_allocation.busy)
+ cv_wait(&percpu_allocation.cv,
+ &percpu_allocation.lock);
+ LIST_REMOVE(pc, pc_list);
+ mutex_exit(&percpu_allocation.lock);
+ }
+
+ /* If there's a destructor, run it now for all CPUs. */
if (pc->pc_dtor) {
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
Home |
Main Index |
Thread Index |
Old Index