NAME
counters_alloc
,
counters_free
,
COUNTERS_BOOT_MEMORY
,
COUNTERS_BOOT_INITIALIZER
,
counters_alloc_ncpus
,
counters_enter
,
counters_leave
,
counters_inc
, counters_add
,
counters_pkt
, counters_read
,
counters_zero
—
per CPU counters
SYNOPSIS
#include
<sys/percpu.h>
struct cpumem *
counters_alloc
(unsigned
int ncounters);
void
counters_free
(struct
cpumem *cm, unsigned int
ncounters);
COUNTERS_BOOT_MEMORY
(NAME,
unsigned int
ncounters);
COUNTERS_BOOT_INITIALIZER
(NAME);
struct cpumemt *
counters_alloc_ncpus
(struct cpumem
*cm, unsigned int ncounters);
uint64_t *
counters_enter
(struct
counters_ref *ref, struct
cpumem *cm);
void
counters_leave
(struct
counters_ref *ref, struct
cpumem *cm);
void
counters_inc
(struct
cpumem *cm, unsigned int
counter);
void
counters_add
(struct
cpumem *cm, unsigned int
counter, uint64_t
v);
void
counters_pkt
(struct cpumem *cm,
unsigned int pcounter, unsigned int
bcounter, uint64_t bytes);
void
counters_read
(struct cpumem *cm,
uint64_t *counters, unsigned int
ncounters, uint64_t *scratch);
void
counters_zero
(struct
cpumem *cm, unsigned int
ncounters);
DESCRIPTION
The per CPU counter API builds on the per CPU memory API and provides access to a series of uint64_t counter values on each CPU.
The API provides versioning of counter updates without using interlocked CPU instructions so they can all be read in a consistent state. Updates to counters should be limited to addition or subtraction of uint64_t values.
An alternate implementation of the API is provided on
uni-processor systems (i.e. when the kernel is not built with
MULTIPROCESSOR
defined) that provides no overhead
compared to direct access to a data structure. This allows the API to be
used without affecting the performance of uni-processor systems.
counters_alloc
()
allocates memory for a series of uint64_t values on each CPU.
ncounters specifies the number of counters to be
allocated. The counters will be zeroed on allocation.
counters_free
()
deallocates each CPU's counters. The same ncounters
argument originally provided to counters_alloc
()
must be passed to counters_free
().
counters_alloc
()
may only be used after all the CPUs in the system have been attached. If a
set of CPU counters needs to be available during early boot, a cpumem
pointer and counters for the boot CPU may be statically allocated.
COUNTERS_BOOT_MEMORY
()
statically allocates a set of counter for use on the boot CPU before the
other CPUs in the system have been attached. The allocation is identified by
NAME and provides memory for the number of counters
specified by ncounters. The counters will be
initialised to zero.
COUNTERS_BOOT_INITIALIZER
()
is used to initialise a cpumem pointer with the memory that was previously
allocated using COUNTERS_BOOT_MEMORY
() and
identified by NAME.
counters_alloc_ncpus
()
allocates additional sets of counters for the CPUs that were attached during
boot. The cpumem structure cm must have been
initialised with COUNTERS_BOOT_INITIALIZER
(). The
same number of counters originally passed to
COUNTERS_BOOT_MEMORY must be specified by
ncounters. The counters on the boot CPU will be
preserved, while the counters for the additional CPUs will be zeroed on
allocation.
Counters that have been allocated with
COUNTERS_BOOT_MEMORY
()
and counters_alloc_ncpus
() cannot be deallocated
with counters_free.
counters_enter
()
provides access to the current CPU's set of counters referenced by
cm. The caller's reference to the counters is held by
ref.
counters_leave
()
indicates the end of access to the current CPU's set of counters referenced
by cm. The reference held by ref
is released by this call.
counters_inc
()
increments the counter at the index specified by
counter in the array of counters referenced by
cm.
counters_add
()
adds the value of v to the counter at the index
specified by counter in the array of counters
referenced by cm.
counters_pkt
()
increments the value at the index specified by
pcounter and adds the value of
bytes to the counter at the index specified by
bcounter in the array of counters referenced by
cm.
counters_read
()
iterates over each CPU's set of counters referenced by
cm, takes a consistent snapshot of the counters, and
then sums them together. The sum of the counters is written to the
counters array. The number of counters is specified
with ncounters. scratch may
point to a buffer used to temporarily hold counters.
If NULL
, one will be dynamically allocated and
freed.
counters_zero
()
iterates over each CPU's set of counters referenced by
cm and zeroes them. The number of counters is
specified with ncounters.
counters_zero
() itself does not prevent concurrent
updates of the counters; it is up to the caller to serialise this call with
other actions.
CONTEXT
counters_alloc
(),
counters_free
(),
counters_alloc_ncpus
(), and
counters_read
() may be called during autoconf, or
from process context.
counters_enter
(),
counters_leave
(),
counters_inc
(),
counters_add
(),
counters_pkt
(), and
counters_zero
() may be called during autoconf, from
process context, or from interrupt context. The per CPU counters API does
not provide any locking or serialisation of access to each CPU's set of
counters beyond isolating each CPU's update. It is up to the caller to
provide appropriate locking or serialisation around calls to these functions
to prevent concurrent access to the relevant data structures.
RETURN VALUES
counters_alloc
() and
counters_alloc_ncpus
() will return an opaque cpumem
pointer that references each CPU's set of counters.
counters_enter
() returns a reference to
the current CPU's set of counters.
EXAMPLES
The following is an example of providing per CPU counters at boot time based on the mbuf(9) statistics code in sys/kern/uipc_mbuf.c.
/* mbuf stats */ COUNTERS_BOOT_MEMORY(mbstat_boot, MBSTAT_COUNT); struct cpumem *mbstat = COUNTERS_BOOT_INITIALIZER(mbstat_boot); /* * this function is called from init_main.c after devices * (including additional CPUs) have been attached */ void mbcpuinit() { mbstat = counters_alloc_ncpus(mbstat, MBSTAT_COUNT); } struct mbuf * m_get(int nowait, int type) { ... struct counters_ref cr; uint64_t *counters; int s; ... s = splnet(); counters = counters_enter(&cr, mbstat); counters[type]++; counters_leave(&cr, mbstat); splx(s); ... } struct mbuf * m_free(struct mbuf *m) { ... struct counters_ref cr; uint64_t *counters; int s; ... s = splnet(); counters = counters_enter(&cr, mbstat); counters[m->m_type]--; counters_leave(&cr, mbstat); splx(s); ... }
SEE ALSO
HISTORY
The per CPU counter API first appeared in OpenBSD 6.1.
AUTHORS
The per CPU counter API was written by David Gwynne <[email protected]>.