1.\" $OpenBSD: counters_alloc.9,v 1.7 2016/10/25 08:09:22 jmc Exp $ 2.\" 3.\" Copyright (c) 2016 David Gwynne <dlg@openbsd.org> 4.\" 5.\" Permission to use, copy, modify, and distribute this software for any 6.\" purpose with or without fee is hereby granted, provided that the above 7.\" copyright notice and this permission notice appear in all copies. 8.\" 9.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16.\" 17.Dd $Mdocdate: October 25 2016 $ 18.Dt COUNTERS_ALLOC 9 19.Os 20.Sh NAME 21.Nm counters_alloc , 22.Nm counters_free , 23.Nm COUNTERS_BOOT_MEMORY , 24.Nm COUNTERS_BOOT_INITIALIZER , 25.Nm counters_alloc_ncpus , 26.Nm counters_enter , 27.Nm counters_leave , 28.Nm counters_read , 29.Nm counters_zero 30.Nd per CPU counters 31.Sh SYNOPSIS 32.In sys/percpu.h 33.Ft struct cpumem * 34.Fn counters_alloc "unsigned int ncounters" "int type" 35.Ft void 36.Fn counters_free "struct cpumem *cm" "unsigned int ncounters" "int type" 37.Fn COUNTERS_BOOT_MEMORY "NAME" "unsigned int ncounters" 38.Fn COUNTERS_BOOT_INITIALIZER "NAME" 39.Ft struct cpumemt * 40.Fo counters_alloc_ncpus 41.Fa "struct cpumem *cm" 42.Fa "unsigned int ncounters" 43.Fa "int type" 44.Fc 45.Ft uint64_t * 46.Fn counters_enter "struct counters_ref *ref" "struct cpumem *cm" 47.Ft void 48.Fn counters_leave "struct counters_ref *ref" "struct cpumem *cm" 49.Ft void 50.Fo counters_read 51.Fa "struct cpumem *cm" 52.Fa "uint64_t *counters" 53.Fa "unsigned int ncounters" 54.Fc 55.Ft void 56.Fn counters_zero "struct cpumem *cm" "unsigned int ncounters" 57.Sh DESCRIPTION 58The per CPU counter API builds on the per CPU memory API and provides 59access to a series of uint64_t counter values on each CPU. 60.Pp 61The API provides versioning of counter updates without using 62interlocked CPU instructions so they can all be read in a consistent 63state. 64Updates to counters should be limited to addition or subtraction 65of uint64_t values. 66.Pp 67An alternate implemention of the API is provided on uni-processor 68(i.e. when the kernel is not built with 69.Dv MULTIPROCESSOR 70defined) 71systems that provides no overhead compared to direct access to a 72data structure. 73This allows the API to be used without affecting the performance 74uni-processor systems. 75.Pp 76.Fn counters_alloc 77allocates memory for a series of uint64_t values on each CPU. 78.Fa ncounters 79specifies the number of counters to be allocated. 80The 81.Fa type 82argument specifies the type of memory that the counters will be 83allocated as via 84.Xr malloc 9 . 85The counters will be zeroed on allocation. 86.Pp 87.Fn counters_free 88deallocates each CPU's counters. 89The same 90.Fa ncounters 91and 92.Fa type 93arguments type originally provided to 94.Fn counters_alloc 95must be passed to 96.Fn counters_free . 97.Pp 98.Fn counters_alloc 99may only be used after all the CPUs in the system have been attached. 100If a set of CPU counters needs to be available during early boot, 101a cpumem pointer and counters for the boot CPU may be statically 102allocated. 103.Pp 104.Fn COUNTERS_BOOT_MEMORY 105statically allocates a set of counter for use on the boot CPU 106before the other CPUs in the system have been attached. 107The allocation is identified by 108.Fa NAME 109and provides memory for the number of counters specified by 110.Fa ncounters . 111The counters will be initialised to zero. 112.Pp 113.Fn COUNTERS_BOOT_INITIALIZER 114is used to initialise a cpumem pointer with the memory that was previously 115allocated using 116.Fn COUNTERS_BOOT_MEMORY 117and identified by 118.Fa NAME . 119.Pp 120.Fn counters_alloc_ncpus 121allocates additional sets of counters for the CPUs that were attached 122during boot. 123The cpumem structure 124.Fa cm 125must have been initialised with 126.Fn COUNTERS_BOOT_INITIALIZER . 127The same number of counters originally passed to 128.Fa COUNTERS_BOOT_MEMORY 129must be specified by 130.Fa ncounters . 131The 132.Fa type 133argument specifies the type of memory that the counters will be 134allocated as via 135.Xr malloc 9 . 136The counters on the boot CPU will be preserved, while the counters 137for the additional CPUs will be zeroed on allocation. 138.Pp 139Counters that have been allocated with 140.Fn COUNTERS_BOOT_MEMORY 141and 142.Fn counters_alloc_ncpus 143cannot be deallocated with 144.Fa counters_free . 145Any attempt to do so will lead to undefined behaviour. 146.Pp 147.Fn counters_enter 148provides access to the current CPU's set of counters referenced by 149.Fa cm . 150The caller's reference to the counters is held by 151.Fa ref . 152.Pp 153.Fn counters_leave 154indicates the end of access to the current CPU's set of counters referenced by 155.Fa cm . 156The reference held by 157.Fa ref 158is released by this call. 159.Pp 160.Fn counters_read 161iterates over each CPU's set of counters referenced by 162.Fa cm , 163takes a consistent snapshot of the counters, and then sums them together. 164The sum of the counters is written to the 165.Fa counters 166array. 167The number of counters is specified with 168.Fa ncounters . 169.Pp 170.Fn counters_zero 171iterates over each CPU's set of counters referenced by 172.Fa cm 173and zeroes them. 174The number of counters is specified with 175.Fa ncounters . 176.Fn counters_zero 177itself does not prevent concurrent updates of the counters; it is 178up to the caller to serialise this call with other actions. 179.Sh CONTEXT 180.Fn counters_alloc , 181.Fn counters_free , 182.Fn counters_alloc_ncpus , 183and 184.Fn counters_read 185may be called during autoconf, or from process context. 186.Pp 187.Fn counters_enter , 188.Fn counters_leave , 189and 190.Fn counters_zero 191may be called during autoconf, from process context, or from interrupt 192context. 193The per CPU counters API does not provide any locking or serialisation 194of access to each CPU's set of counters beyond isolating each CPU's 195update. 196It is up to the caller to provide appropriate locking or serialisation 197around calls to these functions to prevent concurrent access to the 198relevant data structures. 199.Sh RETURN VALUES 200.Fn counters_alloc 201and 202.Fn counters_alloc_ncpus 203will return an opaque cpumem pointer that references each CPU's 204set of counters. 205.Pp 206.Fn counters_enter 207returns a reference to the current CPU's set of counters. 208.Sh EXAMPLES 209The following is an example of providing per CPU counters at boot 210time based on the 211.Xr mbuf 9 212statistics code in 213.Pa sys/kern/uipc_mbuf.c . 214.Bd -literal 215/* mbuf stats */ 216COUNTERS_BOOT_MEMORY(mbstat_boot, MBSTAT_COUNT); 217struct cpumem *mbstat = COUNTERS_BOOT_INITIALIZER(mbstat_boot); 218 219/* 220 * this function is called from init_main.c after devices 221 * (including additional CPUs) have been attached 222 */ 223void 224mbcpuinit() 225{ 226 mbstat = counters_alloc_ncpus(mbstat, MBSTAT_COUNT, 227 M_DEVBUF); 228} 229 230struct mbuf * 231m_get(int nowait, int type) 232{ 233 ... 234 235 struct counters_ref cr; 236 uint64_t *counters; 237 int s; 238 239 ... 240 241 s = splnet(); 242 counters = counters_enter(&cr, mbstat); 243 counters[type]++; 244 counters_leave(&cr, mbstat); 245 splx(s); 246 247 ... 248} 249 250struct mbuf * 251m_free(struct mbuf *m) 252{ 253 ... 254 255 struct counters_ref cr; 256 uint64_t *counters; 257 int s; 258 259 ... 260 261 s = splnet(); 262 counters = counters_enter(&cr, mbstat); 263 counters[m->m_type]--; 264 counters_leave(&cr, mbstat); 265 splx(s); 266 267 ... 268} 269.Ed 270.Sh SEE ALSO 271.Xr cpumem_get 9 , 272.Xr malloc 9 273.Sh HISTORY 274The per CPU counter API first appeared in 275.Ox 6.1 . 276.Sh AUTHORS 277The per CPU counter API was written by 278.An David Gwynne Aq Mt dlg@openbsd.org . 279