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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/pcpustat.h>
+#include <sys/smp.h>
+#include <sys/systm.h>
+
+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

