Module Name: src Committed By: riastradh Date: Sat Feb 1 12:49:02 UTC 2020
Modified Files: src/distrib/sets/lists/comp: mi src/share/man/man9: Makefile percpu.9 src/sys/kern: subr_percpu.c src/sys/sys: percpu.h Log Message: New function percpu_create. Associates a constructor and destructor with the percpu. Currently the constructor runs immediately, but in principle we could use the same API for future CPU hotplug support. This lets you sleep for allocation or draining users before deallocation when setting up or tearing down a percpu -- currently we have many abuses of percpu_foreach in tree for that purpose. Proposed on tech-kern: https://mail-index.NetBSD.org/tech-kern/2020/01/30/msg026036.html To generate a diff of this commit: cvs rdiff -u -r1.2307 -r1.2308 src/distrib/sets/lists/comp/mi cvs rdiff -u -r1.445 -r1.446 src/share/man/man9/Makefile cvs rdiff -u -r1.12 -r1.13 src/share/man/man9/percpu.9 cvs rdiff -u -r1.20 -r1.21 src/sys/kern/subr_percpu.c cvs rdiff -u -r1.3 -r1.4 src/sys/sys/percpu.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/comp/mi diff -u src/distrib/sets/lists/comp/mi:1.2307 src/distrib/sets/lists/comp/mi:1.2308 --- src/distrib/sets/lists/comp/mi:1.2307 Wed Jan 29 18:39:04 2020 +++ src/distrib/sets/lists/comp/mi Sat Feb 1 12:49:02 2020 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.2307 2020/01/29 18:39:04 maya Exp $ +# $NetBSD: mi,v 1.2308 2020/02/01 12:49:02 riastradh Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. ./etc/mtree/set.comp comp-sys-root @@ -11570,6 +11570,7 @@ ./usr/share/man/cat9/pcu_used_p.0 comp-sys-catman .cat ./usr/share/man/cat9/percpu.0 comp-sys-catman .cat ./usr/share/man/cat9/percpu_alloc.0 comp-sys-catman .cat +./usr/share/man/cat9/percpu_create.0 comp-sys-catman .cat ./usr/share/man/cat9/percpu_foreach.0 comp-sys-catman .cat ./usr/share/man/cat9/percpu_free.0 comp-sys-catman .cat ./usr/share/man/cat9/percpu_getref.0 comp-sys-catman .cat @@ -19498,6 +19499,7 @@ ./usr/share/man/html9/pcu_used_p.html comp-sys-htmlman html ./usr/share/man/html9/percpu.html comp-sys-htmlman html ./usr/share/man/html9/percpu_alloc.html comp-sys-htmlman html +./usr/share/man/html9/percpu_create.html comp-sys-htmlman html ./usr/share/man/html9/percpu_foreach.html comp-sys-htmlman html ./usr/share/man/html9/percpu_free.html comp-sys-htmlman html ./usr/share/man/html9/percpu_getref.html comp-sys-htmlman html @@ -27585,6 +27587,7 @@ ./usr/share/man/man9/pcu_used_p.9 comp-sys-man .man ./usr/share/man/man9/percpu.9 comp-sys-man .man ./usr/share/man/man9/percpu_alloc.9 comp-sys-man .man +./usr/share/man/man9/percpu_create.9 comp-sys-man .man ./usr/share/man/man9/percpu_foreach.9 comp-sys-man .man ./usr/share/man/man9/percpu_free.9 comp-sys-man .man ./usr/share/man/man9/percpu_getref.9 comp-sys-man .man Index: src/share/man/man9/Makefile diff -u src/share/man/man9/Makefile:1.445 src/share/man/man9/Makefile:1.446 --- src/share/man/man9/Makefile:1.445 Mon Jan 20 18:38:18 2020 +++ src/share/man/man9/Makefile Sat Feb 1 12:49:02 2020 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.445 2020/01/20 18:38:18 thorpej Exp $ +# $NetBSD: Makefile,v 1.446 2020/02/01 12:49:02 riastradh Exp $ # Makefile for section 9 (kernel function and variable) manual pages. @@ -663,6 +663,7 @@ MLINKS+=pcmcia.9 pcmcia_function_init.9 pcmcia.9 pcmcia_cis_read_n.9 \ pcmcia.9 pcmcia_scan_cis.9 MLINKS+=percpu.9 percpu_alloc.9 \ + percpu.9 percpu_create.9 \ percpu.9 percpu_free.9 \ percpu.9 percpu_getref.9 \ percpu.9 percpu_putref.9 \ Index: src/share/man/man9/percpu.9 diff -u src/share/man/man9/percpu.9:1.12 src/share/man/man9/percpu.9:1.13 --- src/share/man/man9/percpu.9:1.12 Wed May 31 23:54:17 2017 +++ src/share/man/man9/percpu.9 Sat Feb 1 12:49:02 2020 @@ -1,4 +1,4 @@ -.\" $NetBSD: percpu.9,v 1.12 2017/05/31 23:54:17 chs Exp $ +.\" $NetBSD: percpu.9,v 1.13 2020/02/01 12:49:02 riastradh Exp $ .\" .\" Copyright (c) 2010 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -27,12 +27,13 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd May 31, 2017 +.Dd January 29, 2020 .Dt PERCPU 9 .Os .Sh NAME .Nm percpu , .Nm percpu_alloc , +.Nm percpu_create , .Nm percpu_free , .Nm percpu_getref , .Nm percpu_putref , @@ -43,6 +44,8 @@ .Vt typedef void (*percpu_callback_t)(void *, void *, struct cpu_info *); .Ft percpu_t * .Fn percpu_alloc "size_t size" +.Ft percpu_t * +.Fn percpu_create "size_t size" "percpu_callback_t ctor" "percpu_callback_t dtor" "void *arg" .Ft void .Fn percpu_free "percpu_t *pc" "size_t size" .Ft void * @@ -85,6 +88,30 @@ The storage is initialized with zeroes. Treat this as an expensive operation. .Fn percpu_alloc returns a handle for the per-CPU storage. +.It Fn percpu_create "size" "ctor" "dtor" "arg" +Like +.Fn percpu_alloc , +but before returning, for each CPU, call +.Fn "(*ctor)" p arg ci +in the current thread, where +.Fa p +is the pointer to that CPU's storage and +.Fa ci +is the +.Vt "struct cpu_info *" +for that CPU. +Further, arrange that +.Fn percpu_free +will do the same with +.Fn "(*dtor)" p arg ci . +.Pp +.Fa ctor +and +.Fa dtor +.Em MAY +sleep, e.g. to allocate memory or to wait for users to drain before +deallocating memory. +Do not rely on any particular order of iteration over the CPUs. .It Fn percpu_free "pc" "size" Call this in thread context to return to the system the per-CPU storage held by @@ -93,7 +120,9 @@ return to the system the per-CPU storage should match the .Fa size passed to -.Fn percpu_alloc . +.Fn percpu_alloc +or +.Fn percpu_create . When .Fn percpu_free returns, @@ -111,6 +140,13 @@ Follow each .Fn percpu_getref call with a matching call to .Fn percpu_putref . +.Pp +Caller +.Em MUST NOT +sleep after +.Fn percpu_getref , +not even on an adaptive lock, before +.Fn percpu_putref . .It Fn percpu_putref "pc" Indicate that the thread is finished with the pointer returned by the matching @@ -118,9 +154,9 @@ call to .Fn percpu_getref . Re-enables preemption. .It Fn percpu_foreach "pc" "cb" "arg" -On each CPU, for +For each CPU, with .Fa ci -the corresponding +being the corresponding .Vt "struct cpu_info *" and .Fa "p" @@ -132,12 +168,17 @@ run .Fa "arg" .Fa "ci" .Fc . -Call this in thread context. +The call to .Fa cb -should be non-blocking and fast. -Do not rely on +runs in the current thread; use +.Xr xcall 9 +for cross-calls to run logic on other CPUs. +.Pp +Must be used in thread context. .Fa cb -to be run on the CPUs in any particular order. +.Em MUST NOT +sleep except on adaptive locks, and should be fast. +Do not rely on any particular order of iteration over the CPUs. .El .Sh CODE REFERENCES The Index: src/sys/kern/subr_percpu.c diff -u src/sys/kern/subr_percpu.c:1.20 src/sys/kern/subr_percpu.c:1.21 --- src/sys/kern/subr_percpu.c:1.20 Thu Dec 5 03:21:08 2019 +++ src/sys/kern/subr_percpu.c Sat Feb 1 12:49:02 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: subr_percpu.c,v 1.20 2019/12/05 03:21:08 riastradh Exp $ */ +/* $NetBSD: subr_percpu.c,v 1.21 2020/02/01 12:49:02 riastradh Exp $ */ /*- * Copyright (c)2007,2008 YAMAMOTO Takashi, @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: subr_percpu.c,v 1.20 2019/12/05 03:21:08 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: subr_percpu.c,v 1.21 2020/02/01 12:49:02 riastradh Exp $"); #include <sys/param.h> #include <sys/cpu.h> @@ -47,14 +47,12 @@ __KERNEL_RCSID(0, "$NetBSD: subr_percpu. #define PERCPU_QCACHE_MAX 0 #define PERCPU_IMPORT_SIZE 2048 -#if defined(DIAGNOSTIC) -#define MAGIC 0x50435055 /* "PCPU" */ -#define percpu_encrypt(pc) ((pc) ^ MAGIC) -#define percpu_decrypt(pc) ((pc) ^ MAGIC) -#else /* defined(DIAGNOSTIC) */ -#define percpu_encrypt(pc) (pc) -#define percpu_decrypt(pc) (pc) -#endif /* defined(DIAGNOSTIC) */ +struct percpu { + unsigned pc_offset; + size_t pc_size; + percpu_callback_t pc_dtor; + void *pc_cookie; +}; static krwlock_t percpu_swap_lock __cacheline_aligned; static kmutex_t percpu_allocation_lock __cacheline_aligned; @@ -71,7 +69,7 @@ cpu_percpu(struct cpu_info *ci) static unsigned int percpu_offset(percpu_t *pc) { - const unsigned int off = percpu_decrypt((uintptr_t)pc); + const unsigned int off = pc->pc_offset; KASSERT(off < percpu_nextoff); return off; @@ -253,14 +251,56 @@ percpu_init_cpu(struct cpu_info *ci) percpu_t * percpu_alloc(size_t size) { + + return percpu_create(size, NULL, NULL, NULL); +} + +/* + * percpu_create: allocate percpu storage and associate ctor/dtor with it + * + * => called in thread context. + * => considered as an expensive and rare operation. + * => allocated storage is initialized by ctor, or zeros if ctor is null + * => percpu_free will call dtor first, if dtor is nonnull + * => ctor or dtor may sleep, even on allocation + */ + +percpu_t * +percpu_create(size_t size, percpu_callback_t ctor, percpu_callback_t dtor, + void *cookie) +{ vmem_addr_t offset; percpu_t *pc; ASSERT_SLEEPABLE(); (void)vmem_alloc(percpu_offset_arena, size, VM_SLEEP | VM_BESTFIT, &offset); - pc = (percpu_t *)percpu_encrypt((uintptr_t)offset); - percpu_zero(pc, size); + + pc = kmem_alloc(sizeof(*pc), KM_SLEEP); + pc->pc_offset = offset; + pc->pc_size = size; + pc->pc_dtor = dtor; + pc->pc_cookie = cookie; + + if (ctor) { + CPU_INFO_ITERATOR cii; + struct cpu_info *ci; + void *buf; + + buf = kmem_alloc(size, KM_SLEEP); + for (CPU_INFO_FOREACH(cii, ci)) { + memset(buf, 0, size); + (*ctor)(buf, cookie, ci); + percpu_traverse_enter(); + memcpy(percpu_getptr_remote(pc, ci), buf, size); + percpu_traverse_exit(); + } + explicit_memset(buf, 0, size); + kmem_free(buf, size); + } else { + percpu_zero(pc, size); + } + return pc; } @@ -276,7 +316,27 @@ percpu_free(percpu_t *pc, size_t size) { ASSERT_SLEEPABLE(); + KASSERT(size == pc->pc_size); + + if (pc->pc_dtor) { + CPU_INFO_ITERATOR cii; + struct cpu_info *ci; + void *buf; + + buf = kmem_alloc(size, KM_SLEEP); + for (CPU_INFO_FOREACH(cii, ci)) { + percpu_traverse_enter(); + memcpy(buf, percpu_getptr_remote(pc, ci), size); + explicit_memset(percpu_getptr_remote(pc, ci), 0, size); + percpu_traverse_exit(); + (*pc->pc_dtor)(buf, pc->pc_cookie, ci); + } + explicit_memset(buf, 0, size); + kmem_free(buf, size); + } + vmem_free(percpu_offset_arena, (vmem_addr_t)percpu_offset(pc), size); + kmem_free(pc, sizeof(*pc)); } /* Index: src/sys/sys/percpu.h diff -u src/sys/sys/percpu.h:1.3 src/sys/sys/percpu.h:1.4 --- src/sys/sys/percpu.h:1.3 Wed Apr 9 05:11:20 2008 +++ src/sys/sys/percpu.h Sat Feb 1 12:49:02 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: percpu.h,v 1.3 2008/04/09 05:11:20 thorpej Exp $ */ +/* $NetBSD: percpu.h,v 1.4 2020/02/01 12:49:02 riastradh Exp $ */ /*- * Copyright (c)2007,2008 YAMAMOTO Takashi, @@ -41,6 +41,8 @@ void percpu_putref(percpu_t *); typedef void (*percpu_callback_t)(void *, void *, struct cpu_info *); void percpu_foreach(percpu_t *, percpu_callback_t, void *); +percpu_t *percpu_create(size_t, percpu_callback_t, percpu_callback_t, void *); + /* low-level api; don't use unless necessary */ void percpu_traverse_enter(void); void percpu_traverse_exit(void);