1.\" $OpenBSD: counters_alloc.9,v 1.13 2023/09/16 09:33:28 mpi 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: September 16 2023 $ 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_inc , 29.Nm counters_add , 30.Nm counters_pkt , 31.Nm counters_read , 32.Nm counters_zero 33.Nd per CPU counters 34.Sh SYNOPSIS 35.In sys/percpu.h 36.Ft struct cpumem * 37.Fn counters_alloc "unsigned int ncounters" 38.Ft void 39.Fn counters_free "struct cpumem *cm" "unsigned int ncounters" 40.Fn COUNTERS_BOOT_MEMORY "NAME" "unsigned int ncounters" 41.Fn COUNTERS_BOOT_INITIALIZER "NAME" 42.Ft struct cpumemt * 43.Fo counters_alloc_ncpus 44.Fa "struct cpumem *cm" 45.Fa "unsigned int ncounters" 46.Fc 47.Ft uint64_t * 48.Fn counters_enter "struct counters_ref *ref" "struct cpumem *cm" 49.Ft void 50.Fn counters_leave "struct counters_ref *ref" "struct cpumem *cm" 51.Ft void 52.Fn counters_inc "struct cpumem *cm" "unsigned int counter" 53.Ft void 54.Fn counters_add "struct cpumem *cm" "unsigned int counter" "uint64_t v" 55.Ft void 56.Fo counters_pkt 57.Fa "struct cpumem *cm" 58.Fa "unsigned int pcounter" 59.Fa "unsigned int bcounter" 60.Fa "uint64_t bytes" 61.Fc 62.Ft void 63.Fo counters_read 64.Fa "struct cpumem *cm" 65.Fa "uint64_t *counters" 66.Fa "unsigned int ncounters" 67.Fa "uint64_t *scratch" 68.Fc 69.Ft void 70.Fn counters_zero "struct cpumem *cm" "unsigned int ncounters" 71.Sh DESCRIPTION 72The per CPU counter API builds on the per CPU memory API and provides 73access to a series of uint64_t counter values on each CPU. 74.Pp 75The API provides versioning of counter updates without using 76interlocked CPU instructions so they can all be read in a consistent 77state. 78Updates to counters should be limited to addition or subtraction 79of uint64_t values. 80.Pp 81An alternate implementation of the API is provided on uni-processor 82systems 83(i.e. when the kernel is not built with 84.Dv MULTIPROCESSOR 85defined) 86that provides no overhead compared to direct access to a 87data structure. 88This allows the API to be used without affecting the performance 89of uni-processor systems. 90.Pp 91.Fn counters_alloc 92allocates memory for a series of uint64_t values on each CPU. 93.Fa ncounters 94specifies the number of counters to be allocated. 95The counters will be zeroed on allocation. 96.Pp 97.Fn counters_free 98deallocates each CPU's counters. 99The same 100.Fa ncounters 101argument originally provided to 102.Fn counters_alloc 103must be passed to 104.Fn counters_free . 105.Pp 106.Fn counters_alloc 107may only be used after all the CPUs in the system have been attached. 108If a set of CPU counters needs to be available during early boot, 109a cpumem pointer and counters for the boot CPU may be statically 110allocated. 111.Pp 112.Fn COUNTERS_BOOT_MEMORY 113statically allocates a set of counter for use on the boot CPU 114before the other CPUs in the system have been attached. 115The allocation is identified by 116.Fa NAME 117and provides memory for the number of counters specified by 118.Fa ncounters . 119The counters will be initialised to zero. 120.Pp 121.Fn COUNTERS_BOOT_INITIALIZER 122is used to initialise a cpumem pointer with the memory that was previously 123allocated using 124.Fn COUNTERS_BOOT_MEMORY 125and identified by 126.Fa NAME . 127.Pp 128.Fn counters_alloc_ncpus 129allocates additional sets of counters for the CPUs that were attached 130during boot. 131The cpumem structure 132.Fa cm 133must have been initialised with 134.Fn COUNTERS_BOOT_INITIALIZER . 135The same number of counters originally passed to 136.Fa COUNTERS_BOOT_MEMORY 137must be specified by 138.Fa ncounters . 139The counters on the boot CPU will be preserved, while the counters 140for the additional CPUs will be zeroed on allocation. 141.Pp 142Counters that have been allocated with 143.Fn COUNTERS_BOOT_MEMORY 144and 145.Fn counters_alloc_ncpus 146cannot be deallocated with 147.Fa counters_free . 148.Pp 149.Fn counters_enter 150provides access to the current CPU's set of counters referenced by 151.Fa cm . 152The caller's reference to the counters is held by 153.Fa ref . 154.Pp 155.Fn counters_leave 156indicates the end of access to the current CPU's set of counters referenced by 157.Fa cm . 158The reference held by 159.Fa ref 160is released by this call. 161.Pp 162.Fn counters_inc 163increments the counter at the index specified by 164.Fa counter 165in the array of counters referenced by 166.Fa cm . 167.Pp 168.Fn counters_add 169adds the value of 170.Fa v 171to the counter at the index specified by 172.Fa counter 173in the array of counters referenced by 174.Fa cm . 175.Pp 176.Fn counters_pkt 177increments the value at the index specified by 178.Fa pcounter 179and adds the value of 180.Fa bytes 181to the counter at the index specified by 182.Fa bcounter 183in the array of counters referenced by 184.Fa cm . 185.Pp 186.Fn counters_read 187iterates over each CPU's set of counters referenced by 188.Fa cm , 189takes a consistent snapshot of the counters, and then sums them together. 190The sum of the counters is written to the 191.Fa counters 192array. 193The number of counters is specified with 194.Fa ncounters . 195.Fa scratch 196may point to a buffer used to temporarily hold 197.Fa counters . 198If 199.Dv NULL , 200one will be dynamically allocated and freed. 201.Pp 202.Fn counters_zero 203iterates over each CPU's set of counters referenced by 204.Fa cm 205and zeroes them. 206The number of counters is specified with 207.Fa ncounters . 208.Fn counters_zero 209itself does not prevent concurrent updates of the counters; it is 210up to the caller to serialise this call with other actions. 211.Sh CONTEXT 212.Fn counters_alloc , 213.Fn counters_free , 214.Fn counters_alloc_ncpus , 215and 216.Fn counters_read 217may be called during autoconf, or from process context. 218.Pp 219.Fn counters_enter , 220.Fn counters_leave , 221.Fn counters_inc , 222.Fn counters_add , 223.Fn counters_pkt , 224and 225.Fn counters_zero 226may be called during autoconf, from process context, or from interrupt 227context. 228The per CPU counters API does not provide any locking or serialisation 229of access to each CPU's set of counters beyond isolating each CPU's 230update. 231It is up to the caller to provide appropriate locking or serialisation 232around calls to these functions to prevent concurrent access to the 233relevant data structures. 234.Sh RETURN VALUES 235.Fn counters_alloc 236and 237.Fn counters_alloc_ncpus 238will return an opaque cpumem pointer that references each CPU's 239set of counters. 240.Pp 241.Fn counters_enter 242returns a reference to the current CPU's set of counters. 243.Sh EXAMPLES 244The following is an example of providing per CPU counters at boot 245time based on the 246.Xr mbuf 9 247statistics code in 248.Pa sys/kern/uipc_mbuf.c . 249.Bd -literal 250/* mbuf stats */ 251COUNTERS_BOOT_MEMORY(mbstat_boot, MBSTAT_COUNT); 252struct cpumem *mbstat = COUNTERS_BOOT_INITIALIZER(mbstat_boot); 253 254/* 255 * this function is called from init_main.c after devices 256 * (including additional CPUs) have been attached 257 */ 258void 259mbcpuinit() 260{ 261 mbstat = counters_alloc_ncpus(mbstat, MBSTAT_COUNT); 262} 263 264struct mbuf * 265m_get(int nowait, int type) 266{ 267 ... 268 269 struct counters_ref cr; 270 uint64_t *counters; 271 int s; 272 273 ... 274 275 s = splnet(); 276 counters = counters_enter(&cr, mbstat); 277 counters[type]++; 278 counters_leave(&cr, mbstat); 279 splx(s); 280 281 ... 282} 283 284struct mbuf * 285m_free(struct mbuf *m) 286{ 287 ... 288 289 struct counters_ref cr; 290 uint64_t *counters; 291 int s; 292 293 ... 294 295 s = splnet(); 296 counters = counters_enter(&cr, mbstat); 297 counters[m->m_type]--; 298 counters_leave(&cr, mbstat); 299 splx(s); 300 301 ... 302} 303.Ed 304.Sh SEE ALSO 305.Xr cpumem_get 9 , 306.Xr malloc 9 307.Sh HISTORY 308The per CPU counter API first appeared in 309.Ox 6.1 . 310.Sh AUTHORS 311The per CPU counter API was written by 312.An David Gwynne Aq Mt dlg@openbsd.org . 313