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