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