Index: kern/subr_pcpustat.c =================================================================== --- kern/subr_pcpustat.c (revision 0) +++ kern/subr_pcpustat.c (revision 0) @@ -0,0 +1,247 @@ +/*- + * Copyright (c) 2009 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * subr_pcpustat.c is a utility library for subsystems maintaining arrays (or + * array-like structures) of per-CPU statistics. It provides utility + * routines for managing storage, as well as summarizing and resetting + * per-CPU stats. Each struct or array uses only a single data type for all + * fields, as the framework will combine them arithmetically -- usually + * u_long or uint64_t. + * + * Note: currently we assume strong alignment properties on memory returned + * by malloc(9), and validate that assumption with KASSERT's. + * + * In the future, it may be desirable to add a pcpustat_stat(fieldnum, value) + * so that the pcpustat implementation can place statistics in non-contiguous + * memory. This would facilitate using CPU-local memory, rather than just + * CPU-local cache lines, on NUMA systems. + * + * ---- + * + * XXXRW: The following content will move to a man page: + * + * A typical consumer struct might be: + * + * struct foostat { + * u_long fs_counter1; + * u_long fs_counter2; + * } __aligned(CACHE_LINE_SIZE); + * + * In order to avoid additional overhead, it is expected that the consumer + * itself will update the values in the per-CPU array, permitting pointers to + * fields to be cached in registers, etc. The following usage is typical: + * + * // Definitions + * struct foostat *foostatp; + * void *foostat_psp; + * + * // Module load + * if (pcpustat_alloc(&foostat_psp, "foostat", sizeof(struct foostat), + * sizeof(u_long)) != 0) + * panic("foostat_init: pcpustat_alloc failed"); + * foostatp = pcpustat_getptr(foostat_psp); + * + * // Use the pointer for a statistic + * foostatp[curcpu].fs_counter1++; + * + * // Retrieve summary statistics + * struct foostat fs; + * pcpustat_fetch(foostat_psp, &fs); + * + * // Reset summary statistics. + * pcpustat_reset(foostat_psp); + * + * // Module unload + * pcpustat_free(foostat_psp); + * foostatp = foostat_psp = NULL; + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +MALLOC_DEFINE(M_STATSET, "pcpustat", "Per-CPU statistics block"); + +struct pcpustat { + void *ps_data; + const char *ps_name; + size_t ps_structsize; + size_t ps_fieldsize; +}; + +/* + * Allocate a new per-CPU stats set. + */ +int +pcpustat_alloc(struct pcpustat **pspp, const char *name, u_int structsize, + u_int fieldsize) +{ + struct pcpustat *psp; + + /* + * Structures must be an even multiple of field size so that we + * can treat them as arrays. + */ + KASSERT((structsize % fieldsize) != 0, + ("pcpustat_alloc(%s): struct size (%d) not a multiple of field " + "size (%d)", name, structsize, fieldsize)); + + /* + * We only support specific data types that we know how to add using + * unsigned integer arithmetic. Sorry, no signed types. + */ + KASSERT(fieldsize == sizeof(uint8_t) || fieldsize == sizeof(uint16_t) + || fieldsize == sizeof(uint32_t) || fieldsize == sizeof(uint64_t), + ("pcpustat_alloc(%s): unsupported field size (%d)", name, + fieldsize)); + + /* + * There's not much point in having per-CPU stats if they don't fill + * out cache lines, so catch with a run-time assertion on SMP. + */ +#ifdef SMP + KASSERT((structsize % CACHE_LINE_SIZE) == 0, + ("pcpustat_alloc(%s): struct size not cache-aligned: %d", name, + structsize)); +#endif + + psp = malloc(sizeof(*psp), M_STATSET, M_NOWAIT | M_ZERO); + if (psp == NULL) + return (ENOMEM); + psp->ps_name = name; + psp->ps_structsize = structsize; + psp->ps_fieldsize = fieldsize; + + psp->ps_data = malloc((mp_maxid + 1) * psp->ps_structsize, M_STATSET, + M_NOWAIT | M_ZERO); + if (psp->ps_data == NULL) { + free(psp, M_STATSET); + return (ENOMEM); + } + + /* + * We require that the returned structure memory be aligned to field + * size. + */ + KASSERT(((uintptr_t)psp->ps_data % psp->ps_fieldsize) == 0, + ("pcpustat_alloc(%s): field unaligned malloc of ps_data: %p", + name, psp->ps_data)); + + /* + * In order for cache line size padding to work, the head of each + * per-CPU structure must also be cache line-aligned. + */ +#ifdef SMP + KASSERT(((uintptr_t)psp->ps_data % CACHE_LINE_SIZE) == 0, + ("pcpustat_alloc(%s): cache unaligned malloc of ps_data: %p", + name, psp->ps_data)); +#endif + + *pspp = psp; + return (0); +} + +void * +pcpustat_getptr(struct pcpustat *psp) +{ + + return (psp->ps_data); +} + +void +pcpustat_free(struct pcpustat *psp) +{ + + free(psp->ps_data, M_STATSET); + free(psp, M_STATSET); +} + +/* + * Coalesce stats across per-CPU instances of the structure, returning them + * to the caller via a structure laid out in the same way. + */ +void +pcpustat_fetch(struct pcpustat *psp, void *structp) +{ + int cpu, fieldnum; + void *base; + + bzero(structp, psp->ps_structsize); + + /* + * Do this field at a time rather than CPU at a time in the hopes of + * catching the most atomic snapshot of each field across CPUs as we + * can. + */ + for (fieldnum = 0; fieldnum < psp->ps_structsize / psp->ps_fieldsize; + fieldnum++) { + for (cpu = 0; cpu < mp_maxid + 1; cpu++) { + base = (char *)psp->ps_data + + (cpu * psp->ps_structsize) + + (fieldnum * psp->ps_fieldsize); + switch (psp->ps_fieldsize) { + case sizeof(uint8_t): + ((uint8_t *)structp)[fieldnum] += + ((uint8_t *)base)[fieldnum]; + break; + + case sizeof(uint16_t): + ((uint16_t *)structp)[fieldnum] += + ((uint16_t *)base)[fieldnum]; + break; + + case sizeof(uint32_t): + ((uint32_t *)structp)[fieldnum] += + ((uint32_t *)base)[fieldnum]; + break; + + case sizeof(uint64_t): + ((uint64_t *)structp)[fieldnum] += + ((uint64_t *)base)[fieldnum]; + break; + + default: + panic("pcpustat_fetch(%s): field size " + "unknown (%d)", psp->ps_name, + psp->ps_fieldsize); + } + } + } +} + +void +pcpustat_reset(struct pcpustat *psp) +{ + + bzero(psp->ps_data, (mp_maxid + 1) * psp->ps_structsize); +} Property changes on: kern/subr_pcpustat.c ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: sys/pcpustat.h =================================================================== --- sys/pcpustat.h (revision 0) +++ sys/pcpustat.h (revision 0) @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2009 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _SYS_PCPUSTAT_H_ +#define _SYS_PCPUSTAT_H_ + +#ifndef _KERNEL +#error "no user-servicable parts inside" +#endif + +struct pcpustat; +int pcpustat_alloc(struct pcpustat **sspp, const char *ss_name, + u_int ss_structsize, u_int ss_fieldsize); +void *pcpustat_getptr(struct pcpustat *ssp); +void pcpustat_fetch(struct pcpustat *ssp, void *structp); +void pcpustat_free(struct pcpustat *ssp); +void pcpustat_reset(struct pcpustat *ssp); + +#endif /* !_SYS_PCPUSTAT_H_ */ Property changes on: sys/pcpustat.h ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native