xref: /freebsd/sys/kern/subr_stats.c (revision cd0d51baaa4509a1db83251a601d34404d20c990)
1 /*-
2  * Copyright (c) 2014-2018 Netflix, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 /*
30  * Author: Lawrence Stewart <lstewart@netflix.com>
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/param.h>
37 #include <sys/arb.h>
38 #include <sys/ctype.h>
39 #include <sys/errno.h>
40 #include <sys/hash.h>
41 #include <sys/limits.h>
42 #include <sys/malloc.h>
43 #include <sys/qmath.h>
44 #include <sys/sbuf.h>
45 #if defined(DIAGNOSTIC)
46 #include <sys/tree.h>
47 #endif
48 #include <sys/stats.h> /* Must come after qmath.h and arb.h */
49 #include <sys/stddef.h>
50 #include <sys/stdint.h>
51 #include <sys/time.h>
52 
53 #ifdef _KERNEL
54 #include <sys/kernel.h>
55 #include <sys/lock.h>
56 #include <sys/rwlock.h>
57 #include <sys/sysctl.h>
58 #include <sys/systm.h>
59 #else /* ! _KERNEL */
60 #include <pthread.h>
61 #include <stdbool.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #endif /* _KERNEL */
66 
67 struct voistatdata_voistate {
68 	/* Previous VOI value for diff calculation. */
69 	struct voistatdata_numeric prev;
70 };
71 
72 #define	VS_VSDVALID	0x0001	/* Stat's voistatdata updated at least once. */
73 struct voistat {
74 	int8_t		stype;		/* Type of stat e.g. VS_STYPE_SUM. */
75 	enum vsd_dtype	dtype : 8;	/* Data type of this stat's data. */
76 	uint16_t	data_off;	/* Blob offset for this stat's data. */
77 	uint16_t	dsz;		/* Size of stat's data. */
78 #define	VS_EBITS 8
79 	uint16_t	errs : VS_EBITS;/* Non-wrapping error count. */
80 	uint16_t	flags : 16 - VS_EBITS;
81 };
82 /* The voistat error count is capped to avoid wrapping. */
83 #define	VS_INCERRS(vs) do {						\
84 	if ((vs)->errs < (1U << VS_EBITS) - 1)				\
85 		(vs)->errs++;						\
86 } while (0)
87 
88 /*
89  * Ideas for flags:
90  *   - Global or entity specific (global would imply use of counter(9)?)
91  *   - Whether to reset stats on read or not
92  *   - Signal an overflow?
93  *   - Compressed voistat array
94  */
95 #define	VOI_REQSTATE	0x0001	/* VOI requires VS_STYPE_VOISTATE. */
96 struct voi {
97 	int16_t		id;		/* VOI id. */
98 	enum vsd_dtype	dtype : 8;	/* Data type of the VOI itself. */
99 	int8_t		voistatmaxid;	/* Largest allocated voistat index. */
100 	uint16_t	stats_off;	/* Blob offset for this VOIs stats. */
101 	uint16_t	flags;
102 };
103 
104 /*
105  * Memory for the entire blob is allocated as a slab and then offsets are
106  * maintained to carve up the slab into sections holding different data types.
107  *
108  * Ideas for flags:
109  * - Compressed voi array (trade off memory usage vs search time)
110  * - Units of offsets (default bytes, flag for e.g. vm_page/KiB/Mib)
111  */
112 struct statsblobv1 {
113 	uint8_t		abi;
114 	uint8_t		endian;
115 	uint16_t	flags;
116 	uint16_t	maxsz;
117 	uint16_t	cursz;
118 	/* Fields from here down are opaque to consumers. */
119 	uint32_t	tplhash;	/* Base template hash ID. */
120 	uint16_t	stats_off;	/* voistat array blob offset. */
121 	uint16_t	statsdata_off;	/* voistatdata array blob offset. */
122 	sbintime_t	created;	/* Blob creation time. */
123 	sbintime_t	lastrst;	/* Time of last reset. */
124 	struct voi	vois[];		/* Array indexed by [voi_id]. */
125 } __aligned(sizeof(void *));
126 _Static_assert(offsetof(struct statsblobv1, cursz) +
127     SIZEOF_MEMBER(struct statsblobv1, cursz) ==
128     offsetof(struct statsblob, opaque),
129     "statsblobv1 ABI mismatch");
130 
131 struct statsblobv1_tpl {
132 	struct metablob		*mb;
133 	struct statsblobv1	*sb;
134 };
135 
136 /* Context passed to iterator callbacks. */
137 struct sb_iter_ctx {
138 	void		*usrctx;	/* Caller supplied context. */
139 	uint32_t	flags;		/* Flags for current iteration. */
140 	int16_t		vslot;		/* struct voi slot index. */
141 	int8_t		vsslot;		/* struct voistat slot index. */
142 };
143 
144 struct sb_tostrcb_ctx {
145 	struct sbuf		*buf;
146 	struct statsblob_tpl	*tpl;
147 	enum sb_str_fmt	fmt;
148 	uint32_t		flags;
149 };
150 
151 struct sb_visitcb_ctx {
152 	stats_blob_visitcb_t	cb;
153 	void			*usrctx;
154 };
155 
156 /* Stats blob iterator callback. */
157 typedef int (*stats_v1_blob_itercb_t)(struct statsblobv1 *sb, struct voi *v,
158     struct voistat *vs, struct sb_iter_ctx *ctx);
159 
160 #ifdef _KERNEL
161 static struct rwlock tpllistlock;
162 RW_SYSINIT(stats_tpl_list, &tpllistlock, "Stat template list lock");
163 #define	TPL_LIST_RLOCK() rw_rlock(&tpllistlock)
164 #define	TPL_LIST_RUNLOCK() rw_runlock(&tpllistlock)
165 #define	TPL_LIST_WLOCK() rw_wlock(&tpllistlock)
166 #define	TPL_LIST_WUNLOCK() rw_wunlock(&tpllistlock)
167 #define	TPL_LIST_LOCK_ASSERT() rw_assert(&tpllistlock, RA_LOCKED)
168 #define	TPL_LIST_RLOCK_ASSERT() rw_assert(&tpllistlock, RA_RLOCKED)
169 #define	TPL_LIST_WLOCK_ASSERT() rw_assert(&tpllistlock, RA_WLOCKED)
170 MALLOC_DEFINE(M_STATS, "stats(9) related memory", "stats(9) related memory");
171 #define	stats_free(ptr) free((ptr), M_STATS)
172 #else /* ! _KERNEL */
173 static void stats_constructor(void);
174 static void stats_destructor(void);
175 static pthread_rwlock_t tpllistlock;
176 #define	TPL_LIST_UNLOCK() pthread_rwlock_unlock(&tpllistlock)
177 #define	TPL_LIST_RLOCK() pthread_rwlock_rdlock(&tpllistlock)
178 #define	TPL_LIST_RUNLOCK() TPL_LIST_UNLOCK()
179 #define	TPL_LIST_WLOCK() pthread_rwlock_wrlock(&tpllistlock)
180 #define	TPL_LIST_WUNLOCK() TPL_LIST_UNLOCK()
181 #define	TPL_LIST_LOCK_ASSERT() do { } while (0)
182 #define	TPL_LIST_RLOCK_ASSERT() do { } while (0)
183 #define	TPL_LIST_WLOCK_ASSERT() do { } while (0)
184 #ifdef NDEBUG
185 #define	KASSERT(cond, msg) do {} while (0)
186 #define	stats_abort() do {} while (0)
187 #else /* ! NDEBUG */
188 #define	KASSERT(cond, msg) do { \
189 	if (!(cond)) { \
190 		panic msg; \
191 	} \
192 } while (0)
193 #define	stats_abort() abort()
194 #endif /* NDEBUG */
195 #define	stats_free(ptr) free(ptr)
196 #define	panic(fmt, ...) do { \
197 	fprintf(stderr, (fmt), ##__VA_ARGS__); \
198 	stats_abort(); \
199 } while (0)
200 #endif /* _KERNEL */
201 
202 #define	SB_V1_MAXSZ 65535
203 
204 /* Obtain a blob offset pointer. */
205 #define	BLOB_OFFSET(sb, off) ((void *)(((uint8_t *)(sb)) + (off)))
206 
207 /*
208  * Number of VOIs in the blob's vois[] array. By virtue of struct voi being a
209  * power of 2 size, we can shift instead of divide. The shift amount must be
210  * updated if sizeof(struct voi) ever changes, which the assert should catch.
211  */
212 #define	NVOIS(sb) ((int32_t)((((struct statsblobv1 *)(sb))->stats_off - \
213     sizeof(struct statsblobv1)) >> 3))
214 _Static_assert(sizeof(struct voi) == 8, "statsblobv1 voi ABI mismatch");
215 
216 /* Try restrict names to alphanumeric and underscore to simplify JSON compat. */
217 const char *vs_stype2name[VS_NUM_STYPES] = {
218 	[VS_STYPE_VOISTATE] = "VOISTATE",
219 	[VS_STYPE_SUM] = "SUM",
220 	[VS_STYPE_MAX] = "MAX",
221 	[VS_STYPE_MIN] = "MIN",
222 	[VS_STYPE_HIST] = "HIST",
223 	[VS_STYPE_TDGST] = "TDGST",
224 };
225 
226 const char *vs_stype2desc[VS_NUM_STYPES] = {
227 	[VS_STYPE_VOISTATE] = "VOI related state data (not a real stat)",
228 	[VS_STYPE_SUM] = "Simple arithmetic accumulator",
229 	[VS_STYPE_MAX] = "Maximum observed VOI value",
230 	[VS_STYPE_MIN] = "Minimum observed VOI value",
231 	[VS_STYPE_HIST] = "Histogram of observed VOI values",
232 	[VS_STYPE_TDGST] = "t-digest of observed VOI values",
233 };
234 
235 const char *vsd_dtype2name[VSD_NUM_DTYPES] = {
236 	[VSD_DTYPE_VOISTATE] = "VOISTATE",
237 	[VSD_DTYPE_INT_S32] = "INT_S32",
238 	[VSD_DTYPE_INT_U32] = "INT_U32",
239 	[VSD_DTYPE_INT_S64] = "INT_S64",
240 	[VSD_DTYPE_INT_U64] = "INT_U64",
241 	[VSD_DTYPE_INT_SLONG] = "INT_SLONG",
242 	[VSD_DTYPE_INT_ULONG] = "INT_ULONG",
243 	[VSD_DTYPE_Q_S32] = "Q_S32",
244 	[VSD_DTYPE_Q_U32] = "Q_U32",
245 	[VSD_DTYPE_Q_S64] = "Q_S64",
246 	[VSD_DTYPE_Q_U64] = "Q_U64",
247 	[VSD_DTYPE_CRHIST32] = "CRHIST32",
248 	[VSD_DTYPE_DRHIST32] = "DRHIST32",
249 	[VSD_DTYPE_DVHIST32] = "DVHIST32",
250 	[VSD_DTYPE_CRHIST64] = "CRHIST64",
251 	[VSD_DTYPE_DRHIST64] = "DRHIST64",
252 	[VSD_DTYPE_DVHIST64] = "DVHIST64",
253 	[VSD_DTYPE_TDGSTCLUST32] = "TDGSTCLUST32",
254 	[VSD_DTYPE_TDGSTCLUST64] = "TDGSTCLUST64",
255 };
256 
257 const size_t vsd_dtype2size[VSD_NUM_DTYPES] = {
258 	[VSD_DTYPE_VOISTATE] = sizeof(struct voistatdata_voistate),
259 	[VSD_DTYPE_INT_S32] = sizeof(struct voistatdata_int32),
260 	[VSD_DTYPE_INT_U32] = sizeof(struct voistatdata_int32),
261 	[VSD_DTYPE_INT_S64] = sizeof(struct voistatdata_int64),
262 	[VSD_DTYPE_INT_U64] = sizeof(struct voistatdata_int64),
263 	[VSD_DTYPE_INT_SLONG] = sizeof(struct voistatdata_intlong),
264 	[VSD_DTYPE_INT_ULONG] = sizeof(struct voistatdata_intlong),
265 	[VSD_DTYPE_Q_S32] = sizeof(struct voistatdata_q32),
266 	[VSD_DTYPE_Q_U32] = sizeof(struct voistatdata_q32),
267 	[VSD_DTYPE_Q_S64] = sizeof(struct voistatdata_q64),
268 	[VSD_DTYPE_Q_U64] = sizeof(struct voistatdata_q64),
269 	[VSD_DTYPE_CRHIST32] = sizeof(struct voistatdata_crhist32),
270 	[VSD_DTYPE_DRHIST32] = sizeof(struct voistatdata_drhist32),
271 	[VSD_DTYPE_DVHIST32] = sizeof(struct voistatdata_dvhist32),
272 	[VSD_DTYPE_CRHIST64] = sizeof(struct voistatdata_crhist64),
273 	[VSD_DTYPE_DRHIST64] = sizeof(struct voistatdata_drhist64),
274 	[VSD_DTYPE_DVHIST64] = sizeof(struct voistatdata_dvhist64),
275 	[VSD_DTYPE_TDGSTCLUST32] = sizeof(struct voistatdata_tdgstclust32),
276 	[VSD_DTYPE_TDGSTCLUST64] = sizeof(struct voistatdata_tdgstclust64),
277 };
278 
279 static const bool vsd_compoundtype[VSD_NUM_DTYPES] = {
280 	[VSD_DTYPE_VOISTATE] = true,
281 	[VSD_DTYPE_INT_S32] = false,
282 	[VSD_DTYPE_INT_U32] = false,
283 	[VSD_DTYPE_INT_S64] = false,
284 	[VSD_DTYPE_INT_U64] = false,
285 	[VSD_DTYPE_INT_SLONG] = false,
286 	[VSD_DTYPE_INT_ULONG] = false,
287 	[VSD_DTYPE_Q_S32] = false,
288 	[VSD_DTYPE_Q_U32] = false,
289 	[VSD_DTYPE_Q_S64] = false,
290 	[VSD_DTYPE_Q_U64] = false,
291 	[VSD_DTYPE_CRHIST32] = true,
292 	[VSD_DTYPE_DRHIST32] = true,
293 	[VSD_DTYPE_DVHIST32] = true,
294 	[VSD_DTYPE_CRHIST64] = true,
295 	[VSD_DTYPE_DRHIST64] = true,
296 	[VSD_DTYPE_DVHIST64] = true,
297 	[VSD_DTYPE_TDGSTCLUST32] = true,
298 	[VSD_DTYPE_TDGSTCLUST64] = true,
299 };
300 
301 const struct voistatdata_numeric numeric_limits[2][VSD_DTYPE_Q_U64 + 1] = {
302 	[LIM_MIN] = {
303 		[VSD_DTYPE_VOISTATE] = {0},
304 		[VSD_DTYPE_INT_S32] = {.int32 = {.s32 = INT32_MIN}},
305 		[VSD_DTYPE_INT_U32] = {.int32 = {.u32 = 0}},
306 		[VSD_DTYPE_INT_S64] = {.int64 = {.s64 = INT64_MIN}},
307 		[VSD_DTYPE_INT_U64] = {.int64 = {.u64 = 0}},
308 		[VSD_DTYPE_INT_SLONG] = {.intlong = {.slong = LONG_MIN}},
309 		[VSD_DTYPE_INT_ULONG] = {.intlong = {.ulong = 0}},
310 		[VSD_DTYPE_Q_S32] = {.q32 = {.sq32 = Q_IFMINVAL(INT32_MIN)}},
311 		[VSD_DTYPE_Q_U32] = {.q32 = {.uq32 = 0}},
312 		[VSD_DTYPE_Q_S64] = {.q64 = {.sq64 = Q_IFMINVAL(INT64_MIN)}},
313 		[VSD_DTYPE_Q_U64] = {.q64 = {.uq64 = 0}},
314 	},
315 	[LIM_MAX] = {
316 		[VSD_DTYPE_VOISTATE] = {0},
317 		[VSD_DTYPE_INT_S32] = {.int32 = {.s32 = INT32_MAX}},
318 		[VSD_DTYPE_INT_U32] = {.int32 = {.u32 = UINT32_MAX}},
319 		[VSD_DTYPE_INT_S64] = {.int64 = {.s64 = INT64_MAX}},
320 		[VSD_DTYPE_INT_U64] = {.int64 = {.u64 = UINT64_MAX}},
321 		[VSD_DTYPE_INT_SLONG] = {.intlong = {.slong = LONG_MAX}},
322 		[VSD_DTYPE_INT_ULONG] = {.intlong = {.ulong = ULONG_MAX}},
323 		[VSD_DTYPE_Q_S32] = {.q32 = {.sq32 = Q_IFMAXVAL(INT32_MAX)}},
324 		[VSD_DTYPE_Q_U32] = {.q32 = {.uq32 = Q_IFMAXVAL(UINT32_MAX)}},
325 		[VSD_DTYPE_Q_S64] = {.q64 = {.sq64 = Q_IFMAXVAL(INT64_MAX)}},
326 		[VSD_DTYPE_Q_U64] = {.q64 = {.uq64 = Q_IFMAXVAL(UINT64_MAX)}},
327 	}
328 };
329 
330 /* tpllistlock protects tpllist and ntpl */
331 static uint32_t ntpl;
332 static struct statsblob_tpl **tpllist;
333 
334 static inline void * stats_realloc(void *ptr, size_t oldsz, size_t newsz,
335     int flags);
336 //static void stats_v1_blob_finalise(struct statsblobv1 *sb);
337 static int stats_v1_blob_init_locked(struct statsblobv1 *sb, uint32_t tpl_id,
338     uint32_t flags);
339 static int stats_v1_blob_expand(struct statsblobv1 **sbpp, int newvoibytes,
340     int newvoistatbytes, int newvoistatdatabytes);
341 static void stats_v1_blob_iter(struct statsblobv1 *sb,
342     stats_v1_blob_itercb_t icb, void *usrctx, uint32_t flags);
343 static inline int stats_v1_vsd_tdgst_add(enum vsd_dtype vs_dtype,
344     struct voistatdata_tdgst *tdgst, s64q_t x, uint64_t weight, int attempt);
345 
346 static inline int
347 ctd32cmp(const struct voistatdata_tdgstctd32 *c1, const struct voistatdata_tdgstctd32 *c2)
348 {
349 
350 	KASSERT(Q_PRECEQ(c1->mu, c2->mu),
351 	    ("%s: Q_RELPREC(c1->mu,c2->mu)=%d", __func__,
352 	    Q_RELPREC(c1->mu, c2->mu)));
353 
354        return (Q_QLTQ(c1->mu, c2->mu) ? -1 : 1);
355 }
356 ARB_GENERATE_STATIC(ctdth32, voistatdata_tdgstctd32, ctdlnk, ctd32cmp);
357 
358 static inline int
359 ctd64cmp(const struct voistatdata_tdgstctd64 *c1, const struct voistatdata_tdgstctd64 *c2)
360 {
361 
362 	KASSERT(Q_PRECEQ(c1->mu, c2->mu),
363 	    ("%s: Q_RELPREC(c1->mu,c2->mu)=%d", __func__,
364 	    Q_RELPREC(c1->mu, c2->mu)));
365 
366        return (Q_QLTQ(c1->mu, c2->mu) ? -1 : 1);
367 }
368 ARB_GENERATE_STATIC(ctdth64, voistatdata_tdgstctd64, ctdlnk, ctd64cmp);
369 
370 #ifdef DIAGNOSTIC
371 RB_GENERATE_STATIC(rbctdth32, voistatdata_tdgstctd32, rblnk, ctd32cmp);
372 RB_GENERATE_STATIC(rbctdth64, voistatdata_tdgstctd64, rblnk, ctd64cmp);
373 #endif
374 
375 static inline sbintime_t
376 stats_sbinuptime(void)
377 {
378 	sbintime_t sbt;
379 #ifdef _KERNEL
380 
381 	sbt = sbinuptime();
382 #else /* ! _KERNEL */
383 	struct timespec tp;
384 
385 	clock_gettime(CLOCK_MONOTONIC_FAST, &tp);
386 	sbt = tstosbt(tp);
387 #endif /* _KERNEL */
388 
389 	return (sbt);
390 }
391 
392 static inline void *
393 stats_realloc(void *ptr, size_t oldsz, size_t newsz, int flags)
394 {
395 
396 #ifdef _KERNEL
397 	/* Default to M_NOWAIT if neither M_NOWAIT or M_WAITOK are set. */
398 	if (!(flags & (M_WAITOK | M_NOWAIT)))
399 		flags |= M_NOWAIT;
400 	ptr = realloc(ptr, newsz, M_STATS, flags);
401 #else /* ! _KERNEL */
402 	ptr = realloc(ptr, newsz);
403 	if ((flags & M_ZERO) && ptr != NULL) {
404 		if (oldsz == 0)
405 			memset(ptr, '\0', newsz);
406 		else if (newsz > oldsz)
407 			memset(BLOB_OFFSET(ptr, oldsz), '\0', newsz - oldsz);
408 	}
409 #endif /* _KERNEL */
410 
411 	return (ptr);
412 }
413 
414 static inline char *
415 stats_strdup(const char *s,
416 #ifdef _KERNEL
417     int flags)
418 {
419 	char *copy;
420 	size_t len;
421 
422 	if (!(flags & (M_WAITOK | M_NOWAIT)))
423 		flags |= M_NOWAIT;
424 
425 	len = strlen(s) + 1;
426 	if ((copy = malloc(len, M_STATS, flags)) != NULL)
427 		bcopy(s, copy, len);
428 
429 	return (copy);
430 #else
431     int flags __unused)
432 {
433 	return (strdup(s));
434 #endif
435 }
436 
437 static inline void
438 stats_tpl_update_hash(struct statsblob_tpl *tpl)
439 {
440 
441 	TPL_LIST_WLOCK_ASSERT();
442 	tpl->mb->tplhash = hash32_str(tpl->mb->tplname, 0);
443 	for (int voi_id = 0; voi_id < NVOIS(tpl->sb); voi_id++) {
444 		if (tpl->mb->voi_meta[voi_id].name != NULL)
445 			tpl->mb->tplhash = hash32_str(
446 			    tpl->mb->voi_meta[voi_id].name, tpl->mb->tplhash);
447 	}
448 	tpl->mb->tplhash = hash32_buf(tpl->sb, tpl->sb->cursz,
449 	    tpl->mb->tplhash);
450 }
451 
452 static inline uint64_t
453 stats_pow_u64(uint64_t base, uint64_t exp)
454 {
455 	uint64_t result = 1;
456 
457 	while (exp) {
458 		if (exp & 1)
459 			result *= base;
460 		exp >>= 1;
461 		base *= base;
462 	}
463 
464 	return (result);
465 }
466 
467 static inline int
468 stats_vss_hist_bkt_hlpr(struct vss_hist_hlpr_info *info, uint32_t curbkt,
469     struct voistatdata_numeric *bkt_lb, struct voistatdata_numeric *bkt_ub)
470 {
471 	uint64_t step = 0;
472 	int error = 0;
473 
474 	switch (info->scheme) {
475 	case BKT_LIN:
476 		step = info->lin.stepinc;
477 		break;
478 	case BKT_EXP:
479 		step = stats_pow_u64(info->exp.stepbase,
480 		    info->exp.stepexp + curbkt);
481 		break;
482 	case BKT_LINEXP:
483 		{
484 		uint64_t curstepexp = 1;
485 
486 		switch (info->voi_dtype) {
487 		case VSD_DTYPE_INT_S32:
488 			while ((int32_t)stats_pow_u64(info->linexp.stepbase,
489 			    curstepexp) <= bkt_lb->int32.s32)
490 				curstepexp++;
491 			break;
492 		case VSD_DTYPE_INT_U32:
493 			while ((uint32_t)stats_pow_u64(info->linexp.stepbase,
494 			    curstepexp) <= bkt_lb->int32.u32)
495 				curstepexp++;
496 			break;
497 		case VSD_DTYPE_INT_S64:
498 			while ((int64_t)stats_pow_u64(info->linexp.stepbase,
499 			    curstepexp) <= bkt_lb->int64.s64)
500 				curstepexp++;
501 			break;
502 		case VSD_DTYPE_INT_U64:
503 			while ((uint64_t)stats_pow_u64(info->linexp.stepbase,
504 			    curstepexp) <= bkt_lb->int64.u64)
505 				curstepexp++;
506 			break;
507 		case VSD_DTYPE_INT_SLONG:
508 			while ((long)stats_pow_u64(info->linexp.stepbase,
509 			    curstepexp) <= bkt_lb->intlong.slong)
510 				curstepexp++;
511 			break;
512 		case VSD_DTYPE_INT_ULONG:
513 			while ((unsigned long)stats_pow_u64(info->linexp.stepbase,
514 			    curstepexp) <= bkt_lb->intlong.ulong)
515 				curstepexp++;
516 			break;
517 		case VSD_DTYPE_Q_S32:
518 			while ((s32q_t)stats_pow_u64(info->linexp.stepbase,
519 			    curstepexp) <= Q_GIVAL(bkt_lb->q32.sq32))
520 			break;
521 		case VSD_DTYPE_Q_U32:
522 			while ((u32q_t)stats_pow_u64(info->linexp.stepbase,
523 			    curstepexp) <= Q_GIVAL(bkt_lb->q32.uq32))
524 			break;
525 		case VSD_DTYPE_Q_S64:
526 			while ((s64q_t)stats_pow_u64(info->linexp.stepbase,
527 			    curstepexp) <= Q_GIVAL(bkt_lb->q64.sq64))
528 				curstepexp++;
529 			break;
530 		case VSD_DTYPE_Q_U64:
531 			while ((u64q_t)stats_pow_u64(info->linexp.stepbase,
532 			    curstepexp) <= Q_GIVAL(bkt_lb->q64.uq64))
533 				curstepexp++;
534 			break;
535 		default:
536 			break;
537 		}
538 
539 		step = stats_pow_u64(info->linexp.stepbase, curstepexp) /
540 		    info->linexp.linstepdiv;
541 		if (step == 0)
542 			step = 1;
543 		break;
544 		}
545 	default:
546 		break;
547 	}
548 
549 	if (info->scheme == BKT_USR) {
550 		*bkt_lb = info->usr.bkts[curbkt].lb;
551 		*bkt_ub = info->usr.bkts[curbkt].ub;
552 	} else if (step != 0) {
553 		switch (info->voi_dtype) {
554 		case VSD_DTYPE_INT_S32:
555 			bkt_ub->int32.s32 += (int32_t)step;
556 			break;
557 		case VSD_DTYPE_INT_U32:
558 			bkt_ub->int32.u32 += (uint32_t)step;
559 			break;
560 		case VSD_DTYPE_INT_S64:
561 			bkt_ub->int64.s64 += (int64_t)step;
562 			break;
563 		case VSD_DTYPE_INT_U64:
564 			bkt_ub->int64.u64 += (uint64_t)step;
565 			break;
566 		case VSD_DTYPE_INT_SLONG:
567 			bkt_ub->intlong.slong += (long)step;
568 			break;
569 		case VSD_DTYPE_INT_ULONG:
570 			bkt_ub->intlong.ulong += (unsigned long)step;
571 			break;
572 		case VSD_DTYPE_Q_S32:
573 			error = Q_QADDI(&bkt_ub->q32.sq32, step);
574 			break;
575 		case VSD_DTYPE_Q_U32:
576 			error = Q_QADDI(&bkt_ub->q32.uq32, step);
577 			break;
578 		case VSD_DTYPE_Q_S64:
579 			error = Q_QADDI(&bkt_ub->q64.sq64, step);
580 			break;
581 		case VSD_DTYPE_Q_U64:
582 			error = Q_QADDI(&bkt_ub->q64.uq64, step);
583 			break;
584 		default:
585 			break;
586 		}
587 	} else { /* info->scheme != BKT_USR && step == 0 */
588 		return (EINVAL);
589 	}
590 
591 	return (error);
592 }
593 
594 static uint32_t
595 stats_vss_hist_nbkts_hlpr(struct vss_hist_hlpr_info *info)
596 {
597 	struct voistatdata_numeric bkt_lb, bkt_ub;
598 	uint32_t nbkts;
599 	int done;
600 
601 	if (info->scheme == BKT_USR) {
602 		/* XXXLAS: Setting info->{lb,ub} from macro is tricky. */
603 		info->lb = info->usr.bkts[0].lb;
604 		info->ub = info->usr.bkts[info->usr.nbkts - 1].lb;
605 	}
606 
607 	nbkts = 0;
608 	done = 0;
609 	bkt_ub = info->lb;
610 
611 	do {
612 		bkt_lb = bkt_ub;
613 		if (stats_vss_hist_bkt_hlpr(info, nbkts++, &bkt_lb, &bkt_ub))
614 			return (0);
615 
616 		if (info->scheme == BKT_USR)
617 			done = (nbkts == info->usr.nbkts);
618 		else {
619 			switch (info->voi_dtype) {
620 			case VSD_DTYPE_INT_S32:
621 				done = (bkt_ub.int32.s32 > info->ub.int32.s32);
622 				break;
623 			case VSD_DTYPE_INT_U32:
624 				done = (bkt_ub.int32.u32 > info->ub.int32.u32);
625 				break;
626 			case VSD_DTYPE_INT_S64:
627 				done = (bkt_ub.int64.s64 > info->ub.int64.s64);
628 				break;
629 			case VSD_DTYPE_INT_U64:
630 				done = (bkt_ub.int64.u64 > info->ub.int64.u64);
631 				break;
632 			case VSD_DTYPE_INT_SLONG:
633 				done = (bkt_ub.intlong.slong >
634 				    info->ub.intlong.slong);
635 				break;
636 			case VSD_DTYPE_INT_ULONG:
637 				done = (bkt_ub.intlong.ulong >
638 				    info->ub.intlong.ulong);
639 				break;
640 			case VSD_DTYPE_Q_S32:
641 				done = Q_QGTQ(bkt_ub.q32.sq32,
642 				    info->ub.q32.sq32);
643 				break;
644 			case VSD_DTYPE_Q_U32:
645 				done = Q_QGTQ(bkt_ub.q32.uq32,
646 				    info->ub.q32.uq32);
647 				break;
648 			case VSD_DTYPE_Q_S64:
649 				done = Q_QGTQ(bkt_ub.q64.sq64,
650 				    info->ub.q64.sq64);
651 				break;
652 			case VSD_DTYPE_Q_U64:
653 				done = Q_QGTQ(bkt_ub.q64.uq64,
654 				    info->ub.q64.uq64);
655 				break;
656 			default:
657 				return (0);
658 			}
659 		}
660 	} while (!done);
661 
662 	if (info->flags & VSD_HIST_LBOUND_INF)
663 		nbkts++;
664 	if (info->flags & VSD_HIST_UBOUND_INF)
665 		nbkts++;
666 
667 	return (nbkts);
668 }
669 
670 int
671 stats_vss_hist_hlpr(enum vsd_dtype voi_dtype, struct voistatspec *vss,
672     struct vss_hist_hlpr_info *info)
673 {
674 	struct voistatdata_hist *hist;
675 	struct voistatdata_numeric bkt_lb, bkt_ub, *lbinfbktlb, *lbinfbktub,
676 	    *ubinfbktlb, *ubinfbktub;
677 	uint32_t bkt, nbkts, nloop;
678 
679 	if (vss == NULL || info == NULL || (info->flags &
680 	(VSD_HIST_LBOUND_INF|VSD_HIST_UBOUND_INF) && (info->hist_dtype ==
681 	VSD_DTYPE_DVHIST32 || info->hist_dtype == VSD_DTYPE_DVHIST64)))
682 		return (EINVAL);
683 
684 	info->voi_dtype = voi_dtype;
685 
686 	if ((nbkts = stats_vss_hist_nbkts_hlpr(info)) == 0)
687 		return (EINVAL);
688 
689 	switch (info->hist_dtype) {
690 	case VSD_DTYPE_CRHIST32:
691 		vss->vsdsz = HIST_NBKTS2VSDSZ(crhist32, nbkts);
692 		break;
693 	case VSD_DTYPE_DRHIST32:
694 		vss->vsdsz = HIST_NBKTS2VSDSZ(drhist32, nbkts);
695 		break;
696 	case VSD_DTYPE_DVHIST32:
697 		vss->vsdsz = HIST_NBKTS2VSDSZ(dvhist32, nbkts);
698 		break;
699 	case VSD_DTYPE_CRHIST64:
700 		vss->vsdsz = HIST_NBKTS2VSDSZ(crhist64, nbkts);
701 		break;
702 	case VSD_DTYPE_DRHIST64:
703 		vss->vsdsz = HIST_NBKTS2VSDSZ(drhist64, nbkts);
704 		break;
705 	case VSD_DTYPE_DVHIST64:
706 		vss->vsdsz = HIST_NBKTS2VSDSZ(dvhist64, nbkts);
707 		break;
708 	default:
709 		return (EINVAL);
710 	}
711 
712 	vss->iv = stats_realloc(NULL, 0, vss->vsdsz, M_ZERO);
713 	if (vss->iv == NULL)
714 		return (ENOMEM);
715 
716 	hist = (struct voistatdata_hist *)vss->iv;
717 	bkt_ub = info->lb;
718 
719 	for (bkt = (info->flags & VSD_HIST_LBOUND_INF), nloop = 0;
720 	    bkt < nbkts;
721 	    bkt++, nloop++) {
722 		bkt_lb = bkt_ub;
723 		if (stats_vss_hist_bkt_hlpr(info, nloop, &bkt_lb, &bkt_ub))
724 			return (EINVAL);
725 
726 		switch (info->hist_dtype) {
727 		case VSD_DTYPE_CRHIST32:
728 			VSD(crhist32, hist)->bkts[bkt].lb = bkt_lb;
729 			break;
730 		case VSD_DTYPE_DRHIST32:
731 			VSD(drhist32, hist)->bkts[bkt].lb = bkt_lb;
732 			VSD(drhist32, hist)->bkts[bkt].ub = bkt_ub;
733 			break;
734 		case VSD_DTYPE_DVHIST32:
735 			VSD(dvhist32, hist)->bkts[bkt].val = bkt_lb;
736 			break;
737 		case VSD_DTYPE_CRHIST64:
738 			VSD(crhist64, hist)->bkts[bkt].lb = bkt_lb;
739 			break;
740 		case VSD_DTYPE_DRHIST64:
741 			VSD(drhist64, hist)->bkts[bkt].lb = bkt_lb;
742 			VSD(drhist64, hist)->bkts[bkt].ub = bkt_ub;
743 			break;
744 		case VSD_DTYPE_DVHIST64:
745 			VSD(dvhist64, hist)->bkts[bkt].val = bkt_lb;
746 			break;
747 		default:
748 			return (EINVAL);
749 		}
750 	}
751 
752 	lbinfbktlb = lbinfbktub = ubinfbktlb = ubinfbktub = NULL;
753 
754 	switch (info->hist_dtype) {
755 	case VSD_DTYPE_CRHIST32:
756 		lbinfbktlb = &VSD(crhist32, hist)->bkts[0].lb;
757 		ubinfbktlb = &VSD(crhist32, hist)->bkts[nbkts - 1].lb;
758 		break;
759 	case VSD_DTYPE_DRHIST32:
760 		lbinfbktlb = &VSD(drhist32, hist)->bkts[0].lb;
761 		lbinfbktub = &VSD(drhist32, hist)->bkts[0].ub;
762 		ubinfbktlb = &VSD(drhist32, hist)->bkts[nbkts - 1].lb;
763 		ubinfbktub = &VSD(drhist32, hist)->bkts[nbkts - 1].ub;
764 		break;
765 	case VSD_DTYPE_CRHIST64:
766 		lbinfbktlb = &VSD(crhist64, hist)->bkts[0].lb;
767 		ubinfbktlb = &VSD(crhist64, hist)->bkts[nbkts - 1].lb;
768 		break;
769 	case VSD_DTYPE_DRHIST64:
770 		lbinfbktlb = &VSD(drhist64, hist)->bkts[0].lb;
771 		lbinfbktub = &VSD(drhist64, hist)->bkts[0].ub;
772 		ubinfbktlb = &VSD(drhist64, hist)->bkts[nbkts - 1].lb;
773 		ubinfbktub = &VSD(drhist64, hist)->bkts[nbkts - 1].ub;
774 		break;
775 	case VSD_DTYPE_DVHIST32:
776 	case VSD_DTYPE_DVHIST64:
777 		break;
778 	default:
779 		return (EINVAL);
780 	}
781 
782 	if ((info->flags & VSD_HIST_LBOUND_INF) && lbinfbktlb) {
783 		*lbinfbktlb = numeric_limits[LIM_MIN][info->voi_dtype];
784 		/*
785 		 * Assignment from numeric_limit array for Q types assigns max
786 		 * possible integral/fractional value for underlying data type,
787 		 * but we must set control bits for this specific histogram per
788 		 * the user's choice of fractional bits, which we extract from
789 		 * info->lb.
790 		 */
791 		if (info->voi_dtype == VSD_DTYPE_Q_S32 ||
792 		    info->voi_dtype == VSD_DTYPE_Q_U32) {
793 			/* Signedness doesn't matter for setting control bits. */
794 			Q_SCVAL(lbinfbktlb->q32.sq32,
795 			    Q_GCVAL(info->lb.q32.sq32));
796 		} else if (info->voi_dtype == VSD_DTYPE_Q_S64 ||
797 		    info->voi_dtype == VSD_DTYPE_Q_U64) {
798 			/* Signedness doesn't matter for setting control bits. */
799 			Q_SCVAL(lbinfbktlb->q64.sq64,
800 			    Q_GCVAL(info->lb.q64.sq64));
801 		}
802 		if (lbinfbktub)
803 			*lbinfbktub = info->lb;
804 	}
805 	if ((info->flags & VSD_HIST_UBOUND_INF) && ubinfbktlb) {
806 		*ubinfbktlb = bkt_lb;
807 		if (ubinfbktub) {
808 			*ubinfbktub = numeric_limits[LIM_MAX][info->voi_dtype];
809 			if (info->voi_dtype == VSD_DTYPE_Q_S32 ||
810 			    info->voi_dtype == VSD_DTYPE_Q_U32) {
811 				Q_SCVAL(ubinfbktub->q32.sq32,
812 				    Q_GCVAL(info->lb.q32.sq32));
813 			} else if (info->voi_dtype == VSD_DTYPE_Q_S64 ||
814 			    info->voi_dtype == VSD_DTYPE_Q_U64) {
815 				Q_SCVAL(ubinfbktub->q64.sq64,
816 				    Q_GCVAL(info->lb.q64.sq64));
817 			}
818 		}
819 	}
820 
821 	return (0);
822 }
823 
824 int
825 stats_vss_tdgst_hlpr(enum vsd_dtype voi_dtype, struct voistatspec *vss,
826     struct vss_tdgst_hlpr_info *info)
827 {
828 	struct voistatdata_tdgst *tdgst;
829 	struct ctdth32 *ctd32tree;
830 	struct ctdth64 *ctd64tree;
831 	struct voistatdata_tdgstctd32 *ctd32;
832 	struct voistatdata_tdgstctd64 *ctd64;
833 
834 	info->voi_dtype = voi_dtype;
835 
836 	switch (info->tdgst_dtype) {
837 	case VSD_DTYPE_TDGSTCLUST32:
838 		vss->vsdsz = TDGST_NCTRS2VSDSZ(tdgstclust32, info->nctds);
839 		break;
840 	case VSD_DTYPE_TDGSTCLUST64:
841 		vss->vsdsz = TDGST_NCTRS2VSDSZ(tdgstclust64, info->nctds);
842 		break;
843 	default:
844 		return (EINVAL);
845 	}
846 
847 	vss->iv = stats_realloc(NULL, 0, vss->vsdsz, M_ZERO);
848 	if (vss->iv == NULL)
849 		return (ENOMEM);
850 
851 	tdgst = (struct voistatdata_tdgst *)vss->iv;
852 
853 	switch (info->tdgst_dtype) {
854 	case VSD_DTYPE_TDGSTCLUST32:
855 		ctd32tree = &VSD(tdgstclust32, tdgst)->ctdtree;
856 		ARB_INIT(ctd32, ctdlnk, ctd32tree, info->nctds) {
857 			Q_INI(&ctd32->mu, 0, 0, info->prec);
858 		}
859 		break;
860 	case VSD_DTYPE_TDGSTCLUST64:
861 		ctd64tree = &VSD(tdgstclust64, tdgst)->ctdtree;
862 		ARB_INIT(ctd64, ctdlnk, ctd64tree, info->nctds) {
863 			Q_INI(&ctd64->mu, 0, 0, info->prec);
864 		}
865 		break;
866 	default:
867 		return (EINVAL);
868 	}
869 
870 	return (0);
871 }
872 
873 int
874 stats_vss_numeric_hlpr(enum vsd_dtype voi_dtype, struct voistatspec *vss,
875     struct vss_numeric_hlpr_info *info)
876 {
877 	struct voistatdata_numeric iv;
878 
879 	switch (vss->stype) {
880 	case VS_STYPE_SUM:
881 		iv = stats_ctor_vsd_numeric(0);
882 		break;
883 	case VS_STYPE_MIN:
884 		iv = numeric_limits[LIM_MAX][voi_dtype];
885 		break;
886 	case VS_STYPE_MAX:
887 		iv = numeric_limits[LIM_MIN][voi_dtype];
888 		break;
889 	default:
890 		return (EINVAL);
891 	}
892 
893 	vss->iv = stats_realloc(NULL, 0, vsd_dtype2size[voi_dtype], 0);
894 	if (vss->iv == NULL)
895 		return (ENOMEM);
896 
897 	vss->vs_dtype = voi_dtype;
898 	vss->vsdsz = vsd_dtype2size[voi_dtype];
899 	switch (voi_dtype) {
900 	case VSD_DTYPE_INT_S32:
901 		*((int32_t *)vss->iv) = iv.int32.s32;
902 		break;
903 	case VSD_DTYPE_INT_U32:
904 		*((uint32_t *)vss->iv) = iv.int32.u32;
905 		break;
906 	case VSD_DTYPE_INT_S64:
907 		*((int64_t *)vss->iv) = iv.int64.s64;
908 		break;
909 	case VSD_DTYPE_INT_U64:
910 		*((uint64_t *)vss->iv) = iv.int64.u64;
911 		break;
912 	case VSD_DTYPE_INT_SLONG:
913 		*((long *)vss->iv) = iv.intlong.slong;
914 		break;
915 	case VSD_DTYPE_INT_ULONG:
916 		*((unsigned long *)vss->iv) = iv.intlong.ulong;
917 		break;
918 	case VSD_DTYPE_Q_S32:
919 		*((s32q_t *)vss->iv) = Q_SCVAL(iv.q32.sq32,
920 		    Q_CTRLINI(info->prec));
921 		break;
922 	case VSD_DTYPE_Q_U32:
923 		*((u32q_t *)vss->iv) = Q_SCVAL(iv.q32.uq32,
924 		    Q_CTRLINI(info->prec));
925 		break;
926 	case VSD_DTYPE_Q_S64:
927 		*((s64q_t *)vss->iv) = Q_SCVAL(iv.q64.sq64,
928 		    Q_CTRLINI(info->prec));
929 		break;
930 	case VSD_DTYPE_Q_U64:
931 		*((u64q_t *)vss->iv) = Q_SCVAL(iv.q64.uq64,
932 		    Q_CTRLINI(info->prec));
933 		break;
934 	default:
935 		break;
936 	}
937 
938 	return (0);
939 }
940 
941 int
942 stats_vss_hlpr_init(enum vsd_dtype voi_dtype, uint32_t nvss,
943     struct voistatspec *vss)
944 {
945 	int i, ret;
946 
947 	for (i = nvss - 1; i >= 0; i--) {
948 		if (vss[i].hlpr && (ret = vss[i].hlpr(voi_dtype, &vss[i],
949 		    vss[i].hlprinfo)) != 0)
950 			return (ret);
951 	}
952 
953 	return (0);
954 }
955 
956 void
957 stats_vss_hlpr_cleanup(uint32_t nvss, struct voistatspec *vss)
958 {
959 	int i;
960 
961 	for (i = nvss - 1; i >= 0; i--) {
962 		if (vss[i].hlpr) {
963 			stats_free((void *)vss[i].iv);
964 			vss[i].iv = NULL;
965 		}
966 	}
967 }
968 
969 int
970 stats_tpl_fetch(int tpl_id, struct statsblob_tpl **tpl)
971 {
972 	int error;
973 
974 	error = 0;
975 
976 	TPL_LIST_WLOCK();
977 	if (tpl_id < 0 || tpl_id >= (int)ntpl) {
978 		error = ENOENT;
979 	} else {
980 		*tpl = tpllist[tpl_id];
981 		/* XXXLAS: Acquire refcount on tpl. */
982 	}
983 	TPL_LIST_WUNLOCK();
984 
985 	return (error);
986 }
987 
988 int
989 stats_tpl_fetch_allocid(const char *name, uint32_t hash)
990 {
991 	int i, tpl_id;
992 
993 	tpl_id = -ESRCH;
994 
995 	TPL_LIST_RLOCK();
996 	for (i = ntpl - 1; i >= 0; i--) {
997 		if (name != NULL) {
998 			if (strlen(name) == strlen(tpllist[i]->mb->tplname) &&
999 			    strncmp(name, tpllist[i]->mb->tplname,
1000 			    TPL_MAX_NAME_LEN) == 0 && (!hash || hash ==
1001 			    tpllist[i]->mb->tplhash)) {
1002 				tpl_id = i;
1003 				break;
1004 			}
1005 		} else if (hash == tpllist[i]->mb->tplhash) {
1006 			tpl_id = i;
1007 			break;
1008 		}
1009 	}
1010 	TPL_LIST_RUNLOCK();
1011 
1012 	return (tpl_id);
1013 }
1014 
1015 int
1016 stats_tpl_id2name(uint32_t tpl_id, char *buf, size_t len)
1017 {
1018 	int error;
1019 
1020 	error = 0;
1021 
1022 	TPL_LIST_RLOCK();
1023 	if (tpl_id < ntpl) {
1024 		if (buf != NULL && len > strlen(tpllist[tpl_id]->mb->tplname))
1025 			strlcpy(buf, tpllist[tpl_id]->mb->tplname, len);
1026 		else
1027 			error = EOVERFLOW;
1028 	} else
1029 		error = ENOENT;
1030 	TPL_LIST_RUNLOCK();
1031 
1032 	return (error);
1033 }
1034 
1035 int
1036 stats_tpl_sample_rollthedice(struct stats_tpl_sample_rate *rates, int nrates,
1037     void *seed_bytes, size_t seed_len)
1038 {
1039 	uint32_t cum_pct, rnd_pct;
1040 	int i;
1041 
1042 	cum_pct = 0;
1043 
1044 	/*
1045 	 * Choose a pseudorandom or seeded number in range [0,100] and use
1046 	 * it to make a sampling decision and template selection where required.
1047 	 * If no seed is supplied, a PRNG is used to generate a pseudorandom
1048 	 * number so that every selection is independent. If a seed is supplied,
1049 	 * the caller desires random selection across different seeds, but
1050 	 * deterministic selection given the same seed. This is achieved by
1051 	 * hashing the seed and using the hash as the random number source.
1052 	 *
1053 	 * XXXLAS: Characterise hash function output distribution.
1054 	 */
1055 	if (seed_bytes == NULL)
1056 		rnd_pct = random() / (INT32_MAX / 100);
1057 	else
1058 		rnd_pct = hash32_buf(seed_bytes, seed_len, 0) /
1059 		    (UINT32_MAX / 100U);
1060 
1061 	/*
1062 	 * We map the randomly selected percentage on to the interval [0,100]
1063 	 * consisting of the cumulatively summed template sampling percentages.
1064 	 * The difference between the cumulative sum of all template sampling
1065 	 * percentages and 100 is treated as a NULL assignment i.e. no stats
1066 	 * template will be assigned, and -1 returned instead.
1067 	 */
1068 	for (i = 0; i < nrates; i++) {
1069 		cum_pct += rates[i].tpl_sample_pct;
1070 
1071 		KASSERT(cum_pct <= 100, ("%s cum_pct %u > 100", __func__,
1072 		    cum_pct));
1073 		if (rnd_pct > cum_pct || rates[i].tpl_sample_pct == 0)
1074 			continue;
1075 
1076 		return (rates[i].tpl_slot_id);
1077 	}
1078 
1079 	return (-1);
1080 }
1081 
1082 int
1083 stats_v1_blob_clone(struct statsblobv1 **dst, size_t dstmaxsz,
1084     struct statsblobv1 *src, uint32_t flags)
1085 {
1086 	int error;
1087 
1088 	error = 0;
1089 
1090 	if (src == NULL || dst == NULL ||
1091 	    src->cursz < sizeof(struct statsblob) ||
1092 	    ((flags & SB_CLONE_ALLOCDST) &&
1093 	    (flags & (SB_CLONE_USRDSTNOFAULT | SB_CLONE_USRDST)))) {
1094 		error = EINVAL;
1095 	} else if (flags & SB_CLONE_ALLOCDST) {
1096 		*dst = stats_realloc(NULL, 0, src->cursz, 0);
1097 		if (*dst)
1098 			(*dst)->maxsz = dstmaxsz = src->cursz;
1099 		else
1100 			error = ENOMEM;
1101 	} else if (*dst == NULL || dstmaxsz < sizeof(struct statsblob)) {
1102 		error = EINVAL;
1103 	}
1104 
1105 	if (!error) {
1106 		size_t postcurszlen;
1107 
1108 		/*
1109 		 * Clone src into dst except for the maxsz field. If dst is too
1110 		 * small to hold all of src, only copy src's header and return
1111 		 * EOVERFLOW.
1112 		 */
1113 #ifdef _KERNEL
1114 		if (flags & SB_CLONE_USRDSTNOFAULT)
1115 			copyout_nofault(src, *dst,
1116 			    offsetof(struct statsblob, maxsz));
1117 		else if (flags & SB_CLONE_USRDST)
1118 			copyout(src, *dst, offsetof(struct statsblob, maxsz));
1119 		else
1120 #endif
1121 			memcpy(*dst, src, offsetof(struct statsblob, maxsz));
1122 
1123 		if (dstmaxsz >= src->cursz) {
1124 			postcurszlen = src->cursz -
1125 			    offsetof(struct statsblob, cursz);
1126 		} else {
1127 			error = EOVERFLOW;
1128 			postcurszlen = sizeof(struct statsblob) -
1129 			    offsetof(struct statsblob, cursz);
1130 		}
1131 #ifdef _KERNEL
1132 		if (flags & SB_CLONE_USRDSTNOFAULT)
1133 			copyout_nofault(&(src->cursz), &((*dst)->cursz),
1134 			    postcurszlen);
1135 		else if (flags & SB_CLONE_USRDST)
1136 			copyout(&(src->cursz), &((*dst)->cursz), postcurszlen);
1137 		else
1138 #endif
1139 			memcpy(&((*dst)->cursz), &(src->cursz), postcurszlen);
1140 	}
1141 
1142 	return (error);
1143 }
1144 
1145 int
1146 stats_v1_tpl_alloc(const char *name, uint32_t flags __unused)
1147 {
1148 	struct statsblobv1_tpl *tpl, **newtpllist;
1149 	struct statsblobv1 *tpl_sb;
1150 	struct metablob *tpl_mb;
1151 	int tpl_id;
1152 
1153 	if (name != NULL && strlen(name) > TPL_MAX_NAME_LEN)
1154 		return (-EINVAL);
1155 
1156 	if (name != NULL && stats_tpl_fetch_allocid(name, 0) >= 0)
1157 		return (-EEXIST);
1158 
1159 	tpl = stats_realloc(NULL, 0, sizeof(struct statsblobv1_tpl), M_ZERO);
1160 	tpl_mb = stats_realloc(NULL, 0, sizeof(struct metablob), M_ZERO);
1161 	tpl_sb = stats_realloc(NULL, 0, sizeof(struct statsblobv1), M_ZERO);
1162 
1163 	if (tpl_mb != NULL && name != NULL)
1164 		tpl_mb->tplname = stats_strdup(name, 0);
1165 
1166 	if (tpl == NULL || tpl_sb == NULL || tpl_mb == NULL ||
1167 	    tpl_mb->tplname == NULL) {
1168 		stats_free(tpl);
1169 		stats_free(tpl_sb);
1170 		if (tpl_mb != NULL) {
1171 			stats_free(tpl_mb->tplname);
1172 			stats_free(tpl_mb);
1173 		}
1174 		return (-ENOMEM);
1175 	}
1176 
1177 	tpl->mb = tpl_mb;
1178 	tpl->sb = tpl_sb;
1179 
1180 	tpl_sb->abi = STATS_ABI_V1;
1181 	tpl_sb->endian =
1182 #if BYTE_ORDER == LITTLE_ENDIAN
1183 	    SB_LE;
1184 #elif BYTE_ORDER == BIG_ENDIAN
1185 	    SB_BE;
1186 #else
1187 	    SB_UE;
1188 #endif
1189 	tpl_sb->cursz = tpl_sb->maxsz = sizeof(struct statsblobv1);
1190 	tpl_sb->stats_off = tpl_sb->statsdata_off = sizeof(struct statsblobv1);
1191 
1192 	TPL_LIST_WLOCK();
1193 	newtpllist = stats_realloc(tpllist, ntpl * sizeof(void *),
1194 	    (ntpl + 1) * sizeof(void *), 0);
1195 	if (newtpllist != NULL) {
1196 		tpl_id = ntpl++;
1197 		tpllist = (struct statsblob_tpl **)newtpllist;
1198 		tpllist[tpl_id] = (struct statsblob_tpl *)tpl;
1199 		stats_tpl_update_hash(tpllist[tpl_id]);
1200 	} else {
1201 		stats_free(tpl);
1202 		stats_free(tpl_sb);
1203 		if (tpl_mb != NULL) {
1204 			stats_free(tpl_mb->tplname);
1205 			stats_free(tpl_mb);
1206 		}
1207 		tpl_id = -ENOMEM;
1208 	}
1209 	TPL_LIST_WUNLOCK();
1210 
1211 	return (tpl_id);
1212 }
1213 
1214 int
1215 stats_v1_tpl_add_voistats(uint32_t tpl_id, int32_t voi_id, const char *voi_name,
1216     enum vsd_dtype voi_dtype, uint32_t nvss, struct voistatspec *vss,
1217     uint32_t flags)
1218 {
1219 	struct voi *voi;
1220 	struct voistat *tmpstat;
1221 	struct statsblobv1 *tpl_sb;
1222 	struct metablob *tpl_mb;
1223 	int error, i, newstatdataidx, newvoibytes, newvoistatbytes,
1224 	    newvoistatdatabytes, newvoistatmaxid;
1225 	uint32_t nbytes;
1226 
1227 	if (voi_id < 0 || voi_dtype == 0 || voi_dtype >= VSD_NUM_DTYPES ||
1228 	    nvss == 0 || vss == NULL)
1229 		return (EINVAL);
1230 
1231 	error = nbytes = newvoibytes = newvoistatbytes =
1232 	    newvoistatdatabytes = 0;
1233 	newvoistatmaxid = -1;
1234 
1235 	/* Calculate the number of bytes required for the new voistats. */
1236 	for (i = nvss - 1; i >= 0; i--) {
1237 		if (vss[i].stype == 0 || vss[i].stype >= VS_NUM_STYPES ||
1238 		    vss[i].vs_dtype == 0 || vss[i].vs_dtype >= VSD_NUM_DTYPES ||
1239 		    vss[i].iv == NULL || vss[i].vsdsz == 0)
1240 			return (EINVAL);
1241 		if ((int)vss[i].stype > newvoistatmaxid)
1242 			newvoistatmaxid = vss[i].stype;
1243 		newvoistatdatabytes += vss[i].vsdsz;
1244 	}
1245 
1246 	if (flags & SB_VOI_RELUPDATE) {
1247 		/* XXXLAS: VOI state bytes may need to vary based on stat types. */
1248 		newvoistatdatabytes += sizeof(struct voistatdata_voistate);
1249 	}
1250 	nbytes += newvoistatdatabytes;
1251 
1252 	TPL_LIST_WLOCK();
1253 	if (tpl_id < ntpl) {
1254 		tpl_sb = (struct statsblobv1 *)tpllist[tpl_id]->sb;
1255 		tpl_mb = tpllist[tpl_id]->mb;
1256 
1257 		if (voi_id >= NVOIS(tpl_sb) || tpl_sb->vois[voi_id].id == -1) {
1258 			/* Adding a new VOI and associated stats. */
1259 			if (voi_id >= NVOIS(tpl_sb)) {
1260 				/* We need to grow the tpl_sb->vois array. */
1261 				newvoibytes = (voi_id - (NVOIS(tpl_sb) - 1)) *
1262 				    sizeof(struct voi);
1263 				nbytes += newvoibytes;
1264 			}
1265 			newvoistatbytes =
1266 			    (newvoistatmaxid + 1) * sizeof(struct voistat);
1267 		} else {
1268 			/* Adding stats to an existing VOI. */
1269 			if (newvoistatmaxid >
1270 			    tpl_sb->vois[voi_id].voistatmaxid) {
1271 				newvoistatbytes = (newvoistatmaxid -
1272 				    tpl_sb->vois[voi_id].voistatmaxid) *
1273 				    sizeof(struct voistat);
1274 			}
1275 			/* XXXLAS: KPI does not yet support expanding VOIs. */
1276 			error = EOPNOTSUPP;
1277 		}
1278 		nbytes += newvoistatbytes;
1279 
1280 		if (!error && newvoibytes > 0) {
1281 			struct voi_meta *voi_meta = tpl_mb->voi_meta;
1282 
1283 			voi_meta = stats_realloc(voi_meta, voi_meta == NULL ?
1284 			    0 : NVOIS(tpl_sb) * sizeof(struct voi_meta),
1285 			    (1 + voi_id) * sizeof(struct voi_meta),
1286 			    M_ZERO);
1287 
1288 			if (voi_meta == NULL)
1289 				error = ENOMEM;
1290 			else
1291 				tpl_mb->voi_meta = voi_meta;
1292 		}
1293 
1294 		if (!error) {
1295 			/* NB: Resizing can change where tpl_sb points. */
1296 			error = stats_v1_blob_expand(&tpl_sb, newvoibytes,
1297 			    newvoistatbytes, newvoistatdatabytes);
1298 		}
1299 
1300 		if (!error) {
1301 			tpl_mb->voi_meta[voi_id].name = stats_strdup(voi_name,
1302 			    0);
1303 			if (tpl_mb->voi_meta[voi_id].name == NULL)
1304 				error = ENOMEM;
1305 		}
1306 
1307 		if (!error) {
1308 			/* Update the template list with the resized pointer. */
1309 			tpllist[tpl_id]->sb = (struct statsblob *)tpl_sb;
1310 
1311 			/* Update the template. */
1312 			voi = &tpl_sb->vois[voi_id];
1313 
1314 			if (voi->id < 0) {
1315 				/* VOI is new and needs to be initialised. */
1316 				voi->id = voi_id;
1317 				voi->dtype = voi_dtype;
1318 				voi->stats_off = tpl_sb->stats_off;
1319 				if (flags & SB_VOI_RELUPDATE)
1320 					voi->flags |= VOI_REQSTATE;
1321 			} else {
1322 				/*
1323 				 * XXXLAS: When this else block is written, the
1324 				 * "KPI does not yet support expanding VOIs"
1325 				 * error earlier in this function can be
1326 				 * removed. What is required here is to shuffle
1327 				 * the voistat array such that the new stats for
1328 				 * the voi are contiguous, which will displace
1329 				 * stats for other vois that reside after the
1330 				 * voi being updated. The other vois then need
1331 				 * to have their stats_off adjusted post
1332 				 * shuffle.
1333 				 */
1334 			}
1335 
1336 			voi->voistatmaxid = newvoistatmaxid;
1337 			newstatdataidx = 0;
1338 
1339 			if (voi->flags & VOI_REQSTATE) {
1340 				/* Initialise the voistate stat in slot 0. */
1341 				tmpstat = BLOB_OFFSET(tpl_sb, voi->stats_off);
1342 				tmpstat->stype = VS_STYPE_VOISTATE;
1343 				tmpstat->flags = 0;
1344 				tmpstat->dtype = VSD_DTYPE_VOISTATE;
1345 				newstatdataidx = tmpstat->dsz =
1346 				    sizeof(struct voistatdata_numeric);
1347 				tmpstat->data_off = tpl_sb->statsdata_off;
1348 			}
1349 
1350 			for (i = 0; (uint32_t)i < nvss; i++) {
1351 				tmpstat = BLOB_OFFSET(tpl_sb, voi->stats_off +
1352 				    (vss[i].stype * sizeof(struct voistat)));
1353 				KASSERT(tmpstat->stype < 0, ("voistat %p "
1354 				    "already initialised", tmpstat));
1355 				tmpstat->stype = vss[i].stype;
1356 				tmpstat->flags = vss[i].flags;
1357 				tmpstat->dtype = vss[i].vs_dtype;
1358 				tmpstat->dsz = vss[i].vsdsz;
1359 				tmpstat->data_off = tpl_sb->statsdata_off +
1360 				    newstatdataidx;
1361 				memcpy(BLOB_OFFSET(tpl_sb, tmpstat->data_off),
1362 				    vss[i].iv, vss[i].vsdsz);
1363 				newstatdataidx += vss[i].vsdsz;
1364 			}
1365 
1366 			/* Update the template version hash. */
1367 			stats_tpl_update_hash(tpllist[tpl_id]);
1368 			/* XXXLAS: Confirm tpl name/hash pair remains unique. */
1369 		}
1370 	} else
1371 		error = EINVAL;
1372 	TPL_LIST_WUNLOCK();
1373 
1374 	return (error);
1375 }
1376 
1377 struct statsblobv1 *
1378 stats_v1_blob_alloc(uint32_t tpl_id, uint32_t flags __unused)
1379 {
1380 	struct statsblobv1 *sb;
1381 	int error;
1382 
1383 	sb = NULL;
1384 
1385 	TPL_LIST_RLOCK();
1386 	if (tpl_id < ntpl) {
1387 		sb = stats_realloc(NULL, 0, tpllist[tpl_id]->sb->maxsz, 0);
1388 		if (sb != NULL) {
1389 			sb->maxsz = tpllist[tpl_id]->sb->maxsz;
1390 			error = stats_v1_blob_init_locked(sb, tpl_id, 0);
1391 		} else
1392 			error = ENOMEM;
1393 
1394 		if (error) {
1395 			stats_free(sb);
1396 			sb = NULL;
1397 		}
1398 	}
1399 	TPL_LIST_RUNLOCK();
1400 
1401 	return (sb);
1402 }
1403 
1404 void
1405 stats_v1_blob_destroy(struct statsblobv1 *sb)
1406 {
1407 
1408 	stats_free(sb);
1409 }
1410 
1411 int
1412 stats_v1_voistat_fetch_dptr(struct statsblobv1 *sb, int32_t voi_id,
1413     enum voi_stype stype, enum vsd_dtype *retdtype, struct voistatdata **retvsd,
1414     size_t *retvsdsz)
1415 {
1416 	struct voi *v;
1417 	struct voistat *vs;
1418 
1419 	if (retvsd == NULL || sb == NULL || sb->abi != STATS_ABI_V1 ||
1420 	    voi_id >= NVOIS(sb))
1421 		return (EINVAL);
1422 
1423 	v = &sb->vois[voi_id];
1424 	if ((__typeof(v->voistatmaxid))stype > v->voistatmaxid)
1425 		return (EINVAL);
1426 
1427 	vs = BLOB_OFFSET(sb, v->stats_off + (stype * sizeof(struct voistat)));
1428 	*retvsd = BLOB_OFFSET(sb, vs->data_off);
1429 	if (retdtype != NULL)
1430 		*retdtype = vs->dtype;
1431 	if (retvsdsz != NULL)
1432 		*retvsdsz = vs->dsz;
1433 
1434 	return (0);
1435 }
1436 
1437 int
1438 stats_v1_blob_init(struct statsblobv1 *sb, uint32_t tpl_id, uint32_t flags)
1439 {
1440 	int error;
1441 
1442 	error = 0;
1443 
1444 	TPL_LIST_RLOCK();
1445 	if (sb == NULL || tpl_id >= ntpl) {
1446 		error = EINVAL;
1447 	} else {
1448 		error = stats_v1_blob_init_locked(sb, tpl_id, flags);
1449 	}
1450 	TPL_LIST_RUNLOCK();
1451 
1452 	return (error);
1453 }
1454 
1455 static inline int
1456 stats_v1_blob_init_locked(struct statsblobv1 *sb, uint32_t tpl_id,
1457     uint32_t flags __unused)
1458 {
1459 	int error;
1460 
1461 	TPL_LIST_RLOCK_ASSERT();
1462 	error = (sb->maxsz >= tpllist[tpl_id]->sb->cursz) ? 0 : EOVERFLOW;
1463 	KASSERT(!error,
1464 	    ("sb %d instead of %d bytes", sb->maxsz, tpllist[tpl_id]->sb->cursz));
1465 
1466 	if (!error) {
1467 		memcpy(sb, tpllist[tpl_id]->sb, tpllist[tpl_id]->sb->cursz);
1468 		sb->created = sb->lastrst = stats_sbinuptime();
1469 		sb->tplhash = tpllist[tpl_id]->mb->tplhash;
1470 	}
1471 
1472 	return (error);
1473 }
1474 
1475 static int
1476 stats_v1_blob_expand(struct statsblobv1 **sbpp, int newvoibytes,
1477     int newvoistatbytes, int newvoistatdatabytes)
1478 {
1479 	struct statsblobv1 *sb;
1480 	struct voi *tmpvoi;
1481 	struct voistat *tmpvoistat, *voistat_array;
1482 	int error, i, idxnewvois, idxnewvoistats, nbytes, nvoistats;
1483 
1484 	KASSERT(newvoibytes % sizeof(struct voi) == 0,
1485 	    ("Bad newvoibytes %d", newvoibytes));
1486 	KASSERT(newvoistatbytes % sizeof(struct voistat) == 0,
1487 	    ("Bad newvoistatbytes %d", newvoistatbytes));
1488 
1489 	error = ((newvoibytes % sizeof(struct voi) == 0) &&
1490 	    (newvoistatbytes % sizeof(struct voistat) == 0)) ? 0 : EINVAL;
1491 	sb = *sbpp;
1492 	nbytes = newvoibytes + newvoistatbytes + newvoistatdatabytes;
1493 
1494 	/*
1495 	 * XXXLAS: Required until we gain support for flags which alter the
1496 	 * units of size/offset fields in key structs.
1497 	 */
1498 	if (!error && ((((int)sb->cursz) + nbytes) > SB_V1_MAXSZ))
1499 		error = EFBIG;
1500 
1501 	if (!error && (sb->cursz + nbytes > sb->maxsz)) {
1502 		/* Need to expand our blob. */
1503 		sb = stats_realloc(sb, sb->maxsz, sb->cursz + nbytes, M_ZERO);
1504 		if (sb != NULL) {
1505 			sb->maxsz = sb->cursz + nbytes;
1506 			*sbpp = sb;
1507 		} else
1508 		    error = ENOMEM;
1509 	}
1510 
1511 	if (!error) {
1512 		/*
1513 		 * Shuffle memory within the expanded blob working from the end
1514 		 * backwards, leaving gaps for the new voistat and voistatdata
1515 		 * structs at the beginning of their respective blob regions,
1516 		 * and for the new voi structs at the end of their blob region.
1517 		 */
1518 		memmove(BLOB_OFFSET(sb, sb->statsdata_off + nbytes),
1519 		    BLOB_OFFSET(sb, sb->statsdata_off),
1520 		    sb->cursz - sb->statsdata_off);
1521 		memmove(BLOB_OFFSET(sb, sb->stats_off + newvoibytes +
1522 		    newvoistatbytes), BLOB_OFFSET(sb, sb->stats_off),
1523 		    sb->statsdata_off - sb->stats_off);
1524 
1525 		/* First index of new voi/voistat structs to be initialised. */
1526 		idxnewvois = NVOIS(sb);
1527 		idxnewvoistats = (newvoistatbytes / sizeof(struct voistat)) - 1;
1528 
1529 		/* Update housekeeping variables and offsets. */
1530 		sb->cursz += nbytes;
1531 		sb->stats_off += newvoibytes;
1532 		sb->statsdata_off += newvoibytes + newvoistatbytes;
1533 
1534 		/* XXXLAS: Zeroing not strictly needed but aids debugging. */
1535 		memset(&sb->vois[idxnewvois], '\0', newvoibytes);
1536 		memset(BLOB_OFFSET(sb, sb->stats_off), '\0',
1537 		    newvoistatbytes);
1538 		memset(BLOB_OFFSET(sb, sb->statsdata_off), '\0',
1539 		    newvoistatdatabytes);
1540 
1541 		/* Initialise new voi array members and update offsets. */
1542 		for (i = 0; i < NVOIS(sb); i++) {
1543 			tmpvoi = &sb->vois[i];
1544 			if (i >= idxnewvois) {
1545 				tmpvoi->id = tmpvoi->voistatmaxid = -1;
1546 			} else if (tmpvoi->id > -1) {
1547 				tmpvoi->stats_off += newvoibytes +
1548 				    newvoistatbytes;
1549 			}
1550 		}
1551 
1552 		/* Initialise new voistat array members and update offsets. */
1553 		nvoistats = (sb->statsdata_off - sb->stats_off) /
1554 		    sizeof(struct voistat);
1555 		voistat_array = BLOB_OFFSET(sb, sb->stats_off);
1556 		for (i = 0; i < nvoistats; i++) {
1557 			tmpvoistat = &voistat_array[i];
1558 			if (i <= idxnewvoistats) {
1559 				tmpvoistat->stype = -1;
1560 			} else if (tmpvoistat->stype > -1) {
1561 				tmpvoistat->data_off += nbytes;
1562 			}
1563 		}
1564 	}
1565 
1566 	return (error);
1567 }
1568 
1569 static void
1570 stats_v1_blob_finalise(struct statsblobv1 *sb __unused)
1571 {
1572 
1573 	/* XXXLAS: Fill this in. */
1574 }
1575 
1576 static void
1577 stats_v1_blob_iter(struct statsblobv1 *sb, stats_v1_blob_itercb_t icb,
1578     void *usrctx, uint32_t flags)
1579 {
1580 	struct voi *v;
1581 	struct voistat *vs;
1582 	struct sb_iter_ctx ctx;
1583 	int i, j, firstvoi;
1584 
1585 	ctx.usrctx = usrctx;
1586 	ctx.flags |= SB_IT_FIRST_CB;
1587 	ctx.flags &= ~(SB_IT_FIRST_VOI | SB_IT_LAST_VOI | SB_IT_FIRST_VOISTAT |
1588 	    SB_IT_LAST_VOISTAT);
1589 	firstvoi = 1;
1590 
1591 	for (i = 0; i < NVOIS(sb); i++) {
1592 		v = &sb->vois[i];
1593 		ctx.vslot = i;
1594 		ctx.vsslot = -1;
1595 		ctx.flags |= SB_IT_FIRST_VOISTAT;
1596 
1597 		if (firstvoi)
1598 			ctx.flags |= SB_IT_FIRST_VOI;
1599 		else if (i == (NVOIS(sb) - 1))
1600 			ctx.flags |= SB_IT_LAST_VOI | SB_IT_LAST_CB;
1601 
1602 		if (v->id < 0 && (flags & SB_IT_NULLVOI)) {
1603 			if (icb(sb, v, NULL, &ctx))
1604 				return;
1605 			firstvoi = 0;
1606 			ctx.flags &= ~SB_IT_FIRST_CB;
1607 		}
1608 
1609 		/* If NULL voi, v->voistatmaxid == -1 */
1610 		for (j = 0; j <= v->voistatmaxid; j++) {
1611 			vs = &((struct voistat *)BLOB_OFFSET(sb,
1612 			    v->stats_off))[j];
1613 			if (vs->stype < 0 &&
1614 			    !(flags & SB_IT_NULLVOISTAT))
1615 				continue;
1616 
1617 			if (j == v->voistatmaxid) {
1618 				ctx.flags |= SB_IT_LAST_VOISTAT;
1619 				if (i == (NVOIS(sb) - 1))
1620 					ctx.flags |=
1621 					    SB_IT_LAST_CB;
1622 			} else
1623 				ctx.flags &= ~SB_IT_LAST_CB;
1624 
1625 			ctx.vsslot = j;
1626 			if (icb(sb, v, vs, &ctx))
1627 				return;
1628 
1629 			ctx.flags &= ~(SB_IT_FIRST_CB | SB_IT_FIRST_VOISTAT |
1630 			    SB_IT_LAST_VOISTAT);
1631 		}
1632 		ctx.flags &= ~(SB_IT_FIRST_VOI | SB_IT_LAST_VOI);
1633 	}
1634 }
1635 
1636 static inline void
1637 stats_voistatdata_tdgst_tostr(enum vsd_dtype voi_dtype __unused,
1638     const struct voistatdata_tdgst *tdgst, enum vsd_dtype tdgst_dtype,
1639     size_t tdgst_dsz __unused, enum sb_str_fmt fmt, struct sbuf *buf, int objdump)
1640 {
1641 	const struct ctdth32 *ctd32tree;
1642 	const struct ctdth64 *ctd64tree;
1643 	const struct voistatdata_tdgstctd32 *ctd32;
1644 	const struct voistatdata_tdgstctd64 *ctd64;
1645 	const char *fmtstr;
1646 	uint64_t smplcnt, compcnt;
1647 	int is32bit, qmaxstrlen;
1648 	uint16_t maxctds, curctds;
1649 
1650 	switch (tdgst_dtype) {
1651 	case VSD_DTYPE_TDGSTCLUST32:
1652 		smplcnt = CONSTVSD(tdgstclust32, tdgst)->smplcnt;
1653 		compcnt = CONSTVSD(tdgstclust32, tdgst)->compcnt;
1654 		maxctds = ARB_MAXNODES(&CONSTVSD(tdgstclust32, tdgst)->ctdtree);
1655 		curctds = ARB_CURNODES(&CONSTVSD(tdgstclust32, tdgst)->ctdtree);
1656 		ctd32tree = &CONSTVSD(tdgstclust32, tdgst)->ctdtree;
1657 		ctd32 = (objdump ? ARB_CNODE(ctd32tree, 0) :
1658 		    ARB_CMIN(ctdth32, ctd32tree));
1659 		qmaxstrlen = (ctd32 == NULL) ? 1 : Q_MAXSTRLEN(ctd32->mu, 10);
1660 		is32bit = 1;
1661 		ctd64tree = NULL;
1662 		ctd64 = NULL;
1663 		break;
1664 	case VSD_DTYPE_TDGSTCLUST64:
1665 		smplcnt = CONSTVSD(tdgstclust64, tdgst)->smplcnt;
1666 		compcnt = CONSTVSD(tdgstclust64, tdgst)->compcnt;
1667 		maxctds = ARB_MAXNODES(&CONSTVSD(tdgstclust64, tdgst)->ctdtree);
1668 		curctds = ARB_CURNODES(&CONSTVSD(tdgstclust64, tdgst)->ctdtree);
1669 		ctd64tree = &CONSTVSD(tdgstclust64, tdgst)->ctdtree;
1670 		ctd64 = (objdump ? ARB_CNODE(ctd64tree, 0) :
1671 		    ARB_CMIN(ctdth64, ctd64tree));
1672 		qmaxstrlen = (ctd64 == NULL) ? 1 : Q_MAXSTRLEN(ctd64->mu, 10);
1673 		is32bit = 0;
1674 		ctd32tree = NULL;
1675 		ctd32 = NULL;
1676 		break;
1677 	default:
1678 		return;
1679 	}
1680 
1681 	switch (fmt) {
1682 	case SB_STRFMT_FREEFORM:
1683 		fmtstr = "smplcnt=%ju, compcnt=%ju, maxctds=%hu, nctds=%hu";
1684 		break;
1685 	case SB_STRFMT_JSON:
1686 	default:
1687 		fmtstr =
1688 		    "\"smplcnt\":%ju,\"compcnt\":%ju,\"maxctds\":%hu,"
1689 		    "\"nctds\":%hu,\"ctds\":[";
1690 		break;
1691 	}
1692 	sbuf_printf(buf, fmtstr, (uintmax_t)smplcnt, (uintmax_t)compcnt,
1693 	    maxctds, curctds);
1694 
1695 	while ((is32bit ? NULL != ctd32 : NULL != ctd64)) {
1696 		char qstr[qmaxstrlen];
1697 
1698 		switch (fmt) {
1699 		case SB_STRFMT_FREEFORM:
1700 			fmtstr = "\n\t\t\t\t";
1701 			break;
1702 		case SB_STRFMT_JSON:
1703 		default:
1704 			fmtstr = "{";
1705 			break;
1706 		}
1707 		sbuf_cat(buf, fmtstr);
1708 
1709 		if (objdump) {
1710 			switch (fmt) {
1711 			case SB_STRFMT_FREEFORM:
1712 				fmtstr = "ctd[%hu].";
1713 				break;
1714 			case SB_STRFMT_JSON:
1715 			default:
1716 				fmtstr = "\"ctd\":%hu,";
1717 				break;
1718 			}
1719 			sbuf_printf(buf, fmtstr, is32bit ?
1720 			    ARB_SELFIDX(ctd32tree, ctd32) :
1721 			    ARB_SELFIDX(ctd64tree, ctd64));
1722 		}
1723 
1724 		switch (fmt) {
1725 		case SB_STRFMT_FREEFORM:
1726 			fmtstr = "{mu=";
1727 			break;
1728 		case SB_STRFMT_JSON:
1729 		default:
1730 			fmtstr = "\"mu\":";
1731 			break;
1732 		}
1733 		sbuf_cat(buf, fmtstr);
1734 		Q_TOSTR((is32bit ? ctd32->mu : ctd64->mu), -1, 10, qstr,
1735 		    sizeof(qstr));
1736 		sbuf_cat(buf, qstr);
1737 
1738 
1739 		switch (fmt) {
1740 		case SB_STRFMT_FREEFORM:
1741 			fmtstr = is32bit ? ",cnt=%u}" : ",cnt=%ju}";
1742 			break;
1743 		case SB_STRFMT_JSON:
1744 		default:
1745 			fmtstr = is32bit ? ",\"cnt\":%u}" : ",\"cnt\":%ju}";
1746 			break;
1747 		}
1748 		sbuf_printf(buf, fmtstr,
1749 		    is32bit ? ctd32->cnt : (uintmax_t)ctd64->cnt);
1750 
1751 		if (is32bit)
1752 			ctd32 = (objdump ? ARB_CNODE(ctd32tree,
1753 			    ARB_SELFIDX(ctd32tree, ctd32) + 1) :
1754 			    ARB_CNEXT(ctdth32, ctd32tree, ctd32));
1755 		else
1756 			ctd64 = (objdump ? ARB_CNODE(ctd64tree,
1757 			    ARB_SELFIDX(ctd64tree, ctd64) + 1) :
1758 			    ARB_CNEXT(ctdth64, ctd64tree, ctd64));
1759 
1760 		if (fmt == SB_STRFMT_JSON &&
1761 		    (is32bit ? NULL != ctd32 : NULL != ctd64))
1762 			sbuf_putc(buf, ',');
1763 	}
1764 	if (fmt == SB_STRFMT_JSON)
1765 		sbuf_cat(buf, "]");
1766 }
1767 
1768 static inline void
1769 stats_voistatdata_hist_tostr(enum vsd_dtype voi_dtype,
1770     const struct voistatdata_hist *hist, enum vsd_dtype hist_dtype,
1771     size_t hist_dsz, enum sb_str_fmt fmt, struct sbuf *buf, int objdump)
1772 {
1773 	const struct voistatdata_numeric *bkt_lb, *bkt_ub;
1774 	const char *fmtstr;
1775 	int is32bit;
1776 	uint16_t i, nbkts;
1777 
1778 	switch (hist_dtype) {
1779 	case VSD_DTYPE_CRHIST32:
1780 		nbkts = HIST_VSDSZ2NBKTS(crhist32, hist_dsz);
1781 		is32bit = 1;
1782 		break;
1783 	case VSD_DTYPE_DRHIST32:
1784 		nbkts = HIST_VSDSZ2NBKTS(drhist32, hist_dsz);
1785 		is32bit = 1;
1786 		break;
1787 	case VSD_DTYPE_DVHIST32:
1788 		nbkts = HIST_VSDSZ2NBKTS(dvhist32, hist_dsz);
1789 		is32bit = 1;
1790 		break;
1791 	case VSD_DTYPE_CRHIST64:
1792 		nbkts = HIST_VSDSZ2NBKTS(crhist64, hist_dsz);
1793 		is32bit = 0;
1794 		break;
1795 	case VSD_DTYPE_DRHIST64:
1796 		nbkts = HIST_VSDSZ2NBKTS(drhist64, hist_dsz);
1797 		is32bit = 0;
1798 		break;
1799 	case VSD_DTYPE_DVHIST64:
1800 		nbkts = HIST_VSDSZ2NBKTS(dvhist64, hist_dsz);
1801 		is32bit = 0;
1802 		break;
1803 	default:
1804 		return;
1805 	}
1806 
1807 	switch (fmt) {
1808 	case SB_STRFMT_FREEFORM:
1809 		fmtstr = "nbkts=%hu, ";
1810 		break;
1811 	case SB_STRFMT_JSON:
1812 	default:
1813 		fmtstr = "\"nbkts\":%hu,";
1814 		break;
1815 	}
1816 	sbuf_printf(buf, fmtstr, nbkts);
1817 
1818 	switch (fmt) {
1819 		case SB_STRFMT_FREEFORM:
1820 			fmtstr = (is32bit ? "oob=%u" : "oob=%ju");
1821 			break;
1822 		case SB_STRFMT_JSON:
1823 		default:
1824 			fmtstr = (is32bit ? "\"oob\":%u,\"bkts\":[" :
1825 			    "\"oob\":%ju,\"bkts\":[");
1826 			break;
1827 	}
1828 	sbuf_printf(buf, fmtstr, is32bit ? VSD_CONSTHIST_FIELDVAL(hist,
1829 	    hist_dtype, oob) : (uintmax_t)VSD_CONSTHIST_FIELDVAL(hist,
1830 	    hist_dtype, oob));
1831 
1832 	for (i = 0; i < nbkts; i++) {
1833 		switch (hist_dtype) {
1834 		case VSD_DTYPE_CRHIST32:
1835 		case VSD_DTYPE_CRHIST64:
1836 			bkt_lb = VSD_CONSTCRHIST_FIELDPTR(hist, hist_dtype,
1837 			    bkts[i].lb);
1838 			if (i < nbkts - 1)
1839 				bkt_ub = VSD_CONSTCRHIST_FIELDPTR(hist,
1840 				    hist_dtype, bkts[i + 1].lb);
1841 			else
1842 				bkt_ub = &numeric_limits[LIM_MAX][voi_dtype];
1843 			break;
1844 		case VSD_DTYPE_DRHIST32:
1845 		case VSD_DTYPE_DRHIST64:
1846 			bkt_lb = VSD_CONSTDRHIST_FIELDPTR(hist, hist_dtype,
1847 			    bkts[i].lb);
1848 			bkt_ub = VSD_CONSTDRHIST_FIELDPTR(hist, hist_dtype,
1849 			    bkts[i].ub);
1850 			break;
1851 		case VSD_DTYPE_DVHIST32:
1852 		case VSD_DTYPE_DVHIST64:
1853 			bkt_lb = bkt_ub = VSD_CONSTDVHIST_FIELDPTR(hist,
1854 			    hist_dtype, bkts[i].val);
1855 			break;
1856 		default:
1857 			break;
1858 		}
1859 
1860 		switch (fmt) {
1861 		case SB_STRFMT_FREEFORM:
1862 			fmtstr = "\n\t\t\t\t";
1863 			break;
1864 		case SB_STRFMT_JSON:
1865 		default:
1866 			fmtstr = "{";
1867 			break;
1868 		}
1869 		sbuf_cat(buf, fmtstr);
1870 
1871 		if (objdump) {
1872 			switch (fmt) {
1873 			case SB_STRFMT_FREEFORM:
1874 				fmtstr = "bkt[%hu].";
1875 				break;
1876 			case SB_STRFMT_JSON:
1877 			default:
1878 				fmtstr = "\"bkt\":%hu,";
1879 				break;
1880 			}
1881 			sbuf_printf(buf, fmtstr, i);
1882 		}
1883 
1884 		switch (fmt) {
1885 		case SB_STRFMT_FREEFORM:
1886 			fmtstr = "{lb=";
1887 			break;
1888 		case SB_STRFMT_JSON:
1889 		default:
1890 			fmtstr = "\"lb\":";
1891 			break;
1892 		}
1893 		sbuf_cat(buf, fmtstr);
1894 		stats_voistatdata_tostr((const struct voistatdata *)bkt_lb,
1895 		    voi_dtype, voi_dtype, sizeof(struct voistatdata_numeric),
1896 		    fmt, buf, objdump);
1897 
1898 		switch (fmt) {
1899 		case SB_STRFMT_FREEFORM:
1900 			fmtstr = ",ub=";
1901 			break;
1902 		case SB_STRFMT_JSON:
1903 		default:
1904 			fmtstr = ",\"ub\":";
1905 			break;
1906 		}
1907 		sbuf_cat(buf, fmtstr);
1908 		stats_voistatdata_tostr((const struct voistatdata *)bkt_ub,
1909 		    voi_dtype, voi_dtype, sizeof(struct voistatdata_numeric),
1910 		    fmt, buf, objdump);
1911 
1912 		switch (fmt) {
1913 		case SB_STRFMT_FREEFORM:
1914 			fmtstr = is32bit ? ",cnt=%u}" : ",cnt=%ju}";
1915 			break;
1916 		case SB_STRFMT_JSON:
1917 		default:
1918 			fmtstr = is32bit ? ",\"cnt\":%u}" : ",\"cnt\":%ju}";
1919 			break;
1920 		}
1921 		sbuf_printf(buf, fmtstr, is32bit ?
1922 		    VSD_CONSTHIST_FIELDVAL(hist, hist_dtype, bkts[i].cnt) :
1923 		    (uintmax_t)VSD_CONSTHIST_FIELDVAL(hist, hist_dtype,
1924 		    bkts[i].cnt));
1925 
1926 		if (fmt == SB_STRFMT_JSON && i < nbkts - 1)
1927 			sbuf_putc(buf, ',');
1928 	}
1929 	if (fmt == SB_STRFMT_JSON)
1930 		sbuf_cat(buf, "]");
1931 }
1932 
1933 int
1934 stats_voistatdata_tostr(const struct voistatdata *vsd, enum vsd_dtype voi_dtype,
1935     enum vsd_dtype vsd_dtype, size_t vsd_sz, enum sb_str_fmt fmt,
1936     struct sbuf *buf, int objdump)
1937 {
1938 	const char *fmtstr;
1939 
1940 	if (vsd == NULL || buf == NULL || voi_dtype >= VSD_NUM_DTYPES ||
1941 	    vsd_dtype >= VSD_NUM_DTYPES || fmt >= SB_STRFMT_NUM_FMTS)
1942 		return (EINVAL);
1943 
1944 	switch (vsd_dtype) {
1945 	case VSD_DTYPE_VOISTATE:
1946 		switch (fmt) {
1947 		case SB_STRFMT_FREEFORM:
1948 			fmtstr = "prev=";
1949 			break;
1950 		case SB_STRFMT_JSON:
1951 		default:
1952 			fmtstr = "\"prev\":";
1953 			break;
1954 		}
1955 		sbuf_cat(buf, fmtstr);
1956 		/*
1957 		 * Render prev by passing it as *vsd and voi_dtype as vsd_dtype.
1958 		 */
1959 		stats_voistatdata_tostr(
1960 		    (const struct voistatdata *)&CONSTVSD(voistate, vsd)->prev,
1961 		    voi_dtype, voi_dtype, vsd_sz, fmt, buf, objdump);
1962 		break;
1963 	case VSD_DTYPE_INT_S32:
1964 		sbuf_printf(buf, "%d", vsd->int32.s32);
1965 		break;
1966 	case VSD_DTYPE_INT_U32:
1967 		sbuf_printf(buf, "%u", vsd->int32.u32);
1968 		break;
1969 	case VSD_DTYPE_INT_S64:
1970 		sbuf_printf(buf, "%jd", (intmax_t)vsd->int64.s64);
1971 		break;
1972 	case VSD_DTYPE_INT_U64:
1973 		sbuf_printf(buf, "%ju", (uintmax_t)vsd->int64.u64);
1974 		break;
1975 	case VSD_DTYPE_INT_SLONG:
1976 		sbuf_printf(buf, "%ld", vsd->intlong.slong);
1977 		break;
1978 	case VSD_DTYPE_INT_ULONG:
1979 		sbuf_printf(buf, "%lu", vsd->intlong.ulong);
1980 		break;
1981 	case VSD_DTYPE_Q_S32:
1982 		{
1983 		char qstr[Q_MAXSTRLEN(vsd->q32.sq32, 10)];
1984 		Q_TOSTR((s32q_t)vsd->q32.sq32, -1, 10, qstr, sizeof(qstr));
1985 		sbuf_cat(buf, qstr);
1986 		}
1987 		break;
1988 	case VSD_DTYPE_Q_U32:
1989 		{
1990 		char qstr[Q_MAXSTRLEN(vsd->q32.uq32, 10)];
1991 		Q_TOSTR((u32q_t)vsd->q32.uq32, -1, 10, qstr, sizeof(qstr));
1992 		sbuf_cat(buf, qstr);
1993 		}
1994 		break;
1995 	case VSD_DTYPE_Q_S64:
1996 		{
1997 		char qstr[Q_MAXSTRLEN(vsd->q64.sq64, 10)];
1998 		Q_TOSTR((s64q_t)vsd->q64.sq64, -1, 10, qstr, sizeof(qstr));
1999 		sbuf_cat(buf, qstr);
2000 		}
2001 		break;
2002 	case VSD_DTYPE_Q_U64:
2003 		{
2004 		char qstr[Q_MAXSTRLEN(vsd->q64.uq64, 10)];
2005 		Q_TOSTR((u64q_t)vsd->q64.uq64, -1, 10, qstr, sizeof(qstr));
2006 		sbuf_cat(buf, qstr);
2007 		}
2008 		break;
2009 	case VSD_DTYPE_CRHIST32:
2010 	case VSD_DTYPE_DRHIST32:
2011 	case VSD_DTYPE_DVHIST32:
2012 	case VSD_DTYPE_CRHIST64:
2013 	case VSD_DTYPE_DRHIST64:
2014 	case VSD_DTYPE_DVHIST64:
2015 		stats_voistatdata_hist_tostr(voi_dtype, CONSTVSD(hist, vsd),
2016 		    vsd_dtype, vsd_sz, fmt, buf, objdump);
2017 		break;
2018 	case VSD_DTYPE_TDGSTCLUST32:
2019 	case VSD_DTYPE_TDGSTCLUST64:
2020 		stats_voistatdata_tdgst_tostr(voi_dtype,
2021 		    CONSTVSD(tdgst, vsd), vsd_dtype, vsd_sz, fmt, buf,
2022 		    objdump);
2023 		break;
2024 	default:
2025 		break;
2026 	}
2027 
2028 	return (sbuf_error(buf));
2029 }
2030 
2031 static void
2032 stats_v1_itercb_tostr_freeform(struct statsblobv1 *sb, struct voi *v,
2033     struct voistat *vs, struct sb_iter_ctx *ctx)
2034 {
2035 	struct sb_tostrcb_ctx *sctx;
2036 	struct metablob *tpl_mb;
2037 	struct sbuf *buf;
2038 	void *vsd;
2039 	uint8_t dump;
2040 
2041 	sctx = ctx->usrctx;
2042 	buf = sctx->buf;
2043 	tpl_mb = sctx->tpl ? sctx->tpl->mb : NULL;
2044 	dump = ((sctx->flags & SB_TOSTR_OBJDUMP) != 0);
2045 
2046 	if (ctx->flags & SB_IT_FIRST_CB) {
2047 		sbuf_printf(buf, "struct statsblobv1@%p", sb);
2048 		if (dump) {
2049 			sbuf_printf(buf, ", abi=%hhu, endian=%hhu, maxsz=%hu, "
2050 			    "cursz=%hu, created=%jd, lastrst=%jd, flags=0x%04hx, "
2051 			    "stats_off=%hu, statsdata_off=%hu",
2052 			    sb->abi, sb->endian, sb->maxsz, sb->cursz,
2053 			    sb->created, sb->lastrst, sb->flags, sb->stats_off,
2054 			    sb->statsdata_off);
2055 		}
2056 		sbuf_printf(buf, ", tplhash=%u", sb->tplhash);
2057 	}
2058 
2059 	if (ctx->flags & SB_IT_FIRST_VOISTAT) {
2060 		sbuf_printf(buf, "\n\tvois[%hd]: id=%hd", ctx->vslot, v->id);
2061 		if (v->id < 0)
2062 			return;
2063 		sbuf_printf(buf, ", name=\"%s\"", (tpl_mb == NULL) ? "" :
2064 		    tpl_mb->voi_meta[v->id].name);
2065 		if (dump)
2066 		    sbuf_printf(buf, ", flags=0x%04hx, dtype=%s, "
2067 		    "voistatmaxid=%hhd, stats_off=%hu", v->flags,
2068 		    vsd_dtype2name[v->dtype], v->voistatmaxid, v->stats_off);
2069 	}
2070 
2071 	if (!dump && vs->stype <= 0)
2072 		return;
2073 
2074 	sbuf_printf(buf, "\n\t\tvois[%hd]stat[%hhd]: stype=", v->id, ctx->vsslot);
2075 	if (vs->stype < 0) {
2076 		sbuf_printf(buf, "%hhd", vs->stype);
2077 		return;
2078 	} else
2079 		sbuf_printf(buf, "%s, errs=%hu", vs_stype2name[vs->stype],
2080 		    vs->errs);
2081 	vsd = BLOB_OFFSET(sb, vs->data_off);
2082 	if (dump)
2083 		sbuf_printf(buf, ", flags=0x%04x, dtype=%s, dsz=%hu, "
2084 		    "data_off=%hu", vs->flags, vsd_dtype2name[vs->dtype],
2085 		    vs->dsz, vs->data_off);
2086 
2087 	sbuf_printf(buf, "\n\t\t\tvoistatdata: ");
2088 	stats_voistatdata_tostr(vsd, v->dtype, vs->dtype, vs->dsz,
2089 	    sctx->fmt, buf, dump);
2090 }
2091 
2092 static void
2093 stats_v1_itercb_tostr_json(struct statsblobv1 *sb, struct voi *v, struct voistat *vs,
2094     struct sb_iter_ctx *ctx)
2095 {
2096 	struct sb_tostrcb_ctx *sctx;
2097 	struct metablob *tpl_mb;
2098 	struct sbuf *buf;
2099 	const char *fmtstr;
2100 	void *vsd;
2101 	uint8_t dump;
2102 
2103 	sctx = ctx->usrctx;
2104 	buf = sctx->buf;
2105 	tpl_mb = sctx->tpl ? sctx->tpl->mb : NULL;
2106 	dump = ((sctx->flags & SB_TOSTR_OBJDUMP) != 0);
2107 
2108 	if (ctx->flags & SB_IT_FIRST_CB) {
2109 		sbuf_putc(buf, '{');
2110 		if (dump) {
2111 			sbuf_printf(buf, "\"abi\":%hhu,\"endian\":%hhu,"
2112 			    "\"maxsz\":%hu,\"cursz\":%hu,\"created\":%jd,"
2113 			    "\"lastrst\":%jd,\"flags\":%hu,\"stats_off\":%hu,"
2114 			    "\"statsdata_off\":%hu,", sb->abi,
2115 			    sb->endian, sb->maxsz, sb->cursz, sb->created,
2116 			    sb->lastrst, sb->flags, sb->stats_off,
2117 			    sb->statsdata_off);
2118 		}
2119 
2120 		if (tpl_mb == NULL)
2121 			fmtstr = "\"tplname\":%s,\"tplhash\":%u,\"vois\":{";
2122 		else
2123 			fmtstr = "\"tplname\":\"%s\",\"tplhash\":%u,\"vois\":{";
2124 
2125 		sbuf_printf(buf, fmtstr, tpl_mb ? tpl_mb->tplname : "null",
2126 		    sb->tplhash);
2127 	}
2128 
2129 	if (ctx->flags & SB_IT_FIRST_VOISTAT) {
2130 		if (dump) {
2131 			sbuf_printf(buf, "\"[%d]\":{\"id\":%d", ctx->vslot,
2132 			    v->id);
2133 			if (v->id < 0) {
2134 				sbuf_printf(buf, "},");
2135 				return;
2136 			}
2137 
2138 			if (tpl_mb == NULL)
2139 				fmtstr = ",\"name\":%s,\"flags\":%hu,"
2140 				    "\"dtype\":\"%s\",\"voistatmaxid\":%hhd,"
2141 				    "\"stats_off\":%hu,";
2142 			else
2143 				fmtstr = ",\"name\":\"%s\",\"flags\":%hu,"
2144 				    "\"dtype\":\"%s\",\"voistatmaxid\":%hhd,"
2145 				    "\"stats_off\":%hu,";
2146 
2147 			sbuf_printf(buf, fmtstr, tpl_mb ?
2148 			    tpl_mb->voi_meta[v->id].name : "null", v->flags,
2149 			    vsd_dtype2name[v->dtype], v->voistatmaxid,
2150 			    v->stats_off);
2151 		} else {
2152 			if (tpl_mb == NULL) {
2153 				sbuf_printf(buf, "\"[%hd]\":{", v->id);
2154 			} else {
2155 				sbuf_printf(buf, "\"%s\":{",
2156 				    tpl_mb->voi_meta[v->id].name);
2157 			}
2158 		}
2159 		sbuf_cat(buf, "\"stats\":{");
2160 	}
2161 
2162 	vsd = BLOB_OFFSET(sb, vs->data_off);
2163 	if (dump) {
2164 		sbuf_printf(buf, "\"[%hhd]\":", ctx->vsslot);
2165 		if (vs->stype < 0) {
2166 			sbuf_printf(buf, "{\"stype\":-1},");
2167 			return;
2168 		}
2169 		sbuf_printf(buf, "{\"stype\":\"%s\",\"errs\":%hu,\"flags\":%hu,"
2170 		    "\"dtype\":\"%s\",\"data_off\":%hu,\"voistatdata\":{",
2171 		    vs_stype2name[vs->stype], vs->errs, vs->flags,
2172 		    vsd_dtype2name[vs->dtype], vs->data_off);
2173 	} else if (vs->stype > 0) {
2174 		if (tpl_mb == NULL)
2175 			sbuf_printf(buf, "\"[%hhd]\":", vs->stype);
2176 		else
2177 			sbuf_printf(buf, "\"%s\":", vs_stype2name[vs->stype]);
2178 	} else
2179 		return;
2180 
2181 	if ((vs->flags & VS_VSDVALID) || dump) {
2182 		if (!dump)
2183 			sbuf_printf(buf, "{\"errs\":%hu,", vs->errs);
2184 		/* Simple non-compound VSD types need a key. */
2185 		if (!vsd_compoundtype[vs->dtype])
2186 			sbuf_cat(buf, "\"val\":");
2187 		stats_voistatdata_tostr(vsd, v->dtype, vs->dtype, vs->dsz,
2188 		    sctx->fmt, buf, dump);
2189 		sbuf_cat(buf, dump ? "}}" : "}");
2190 	} else
2191 		sbuf_cat(buf, dump ? "null}" : "null");
2192 
2193 	if (ctx->flags & SB_IT_LAST_VOISTAT)
2194 		sbuf_cat(buf, "}}");
2195 
2196 	if (ctx->flags & SB_IT_LAST_CB)
2197 		sbuf_cat(buf, "}}");
2198 	else
2199 		sbuf_putc(buf, ',');
2200 }
2201 
2202 static int
2203 stats_v1_itercb_tostr(struct statsblobv1 *sb, struct voi *v, struct voistat *vs,
2204     struct sb_iter_ctx *ctx)
2205 {
2206 	struct sb_tostrcb_ctx *sctx;
2207 
2208 	sctx = ctx->usrctx;
2209 
2210 	switch (sctx->fmt) {
2211 	case SB_STRFMT_FREEFORM:
2212 		stats_v1_itercb_tostr_freeform(sb, v, vs, ctx);
2213 		break;
2214 	case SB_STRFMT_JSON:
2215 		stats_v1_itercb_tostr_json(sb, v, vs, ctx);
2216 		break;
2217 	default:
2218 		break;
2219 	}
2220 
2221 	return (sbuf_error(sctx->buf));
2222 }
2223 
2224 int
2225 stats_v1_blob_tostr(struct statsblobv1 *sb, struct sbuf *buf,
2226     enum sb_str_fmt fmt, uint32_t flags)
2227 {
2228 	struct sb_tostrcb_ctx sctx;
2229 	uint32_t iflags;
2230 
2231 	if (sb == NULL || sb->abi != STATS_ABI_V1 || buf == NULL ||
2232 	    fmt >= SB_STRFMT_NUM_FMTS)
2233 		return (EINVAL);
2234 
2235 	sctx.buf = buf;
2236 	sctx.fmt = fmt;
2237 	sctx.flags = flags;
2238 
2239 	if (flags & SB_TOSTR_META) {
2240 		if (stats_tpl_fetch(stats_tpl_fetch_allocid(NULL, sb->tplhash),
2241 		    &sctx.tpl))
2242 			return (EINVAL);
2243 	} else
2244 		sctx.tpl = NULL;
2245 
2246 	iflags = 0;
2247 	if (flags & SB_TOSTR_OBJDUMP)
2248 		iflags |= (SB_IT_NULLVOI | SB_IT_NULLVOISTAT);
2249 	stats_v1_blob_iter(sb, stats_v1_itercb_tostr, &sctx, iflags);
2250 
2251 	return (sbuf_error(buf));
2252 }
2253 
2254 static int
2255 stats_v1_itercb_visit(struct statsblobv1 *sb, struct voi *v,
2256     struct voistat *vs, struct sb_iter_ctx *ctx)
2257 {
2258 	struct sb_visitcb_ctx *vctx;
2259 	struct sb_visit sbv;
2260 
2261 	vctx = ctx->usrctx;
2262 
2263 	sbv.tplhash = sb->tplhash;
2264 	sbv.voi_id = v->id;
2265 	sbv.voi_dtype = v->dtype;
2266 	sbv.vs_stype = vs->stype;
2267 	sbv.vs_dtype = vs->dtype;
2268 	sbv.vs_dsz = vs->dsz;
2269 	sbv.vs_data = BLOB_OFFSET(sb, vs->data_off);
2270 	sbv.vs_errs = vs->errs;
2271 	sbv.flags = ctx->flags & (SB_IT_FIRST_CB | SB_IT_LAST_CB |
2272 	    SB_IT_FIRST_VOI | SB_IT_LAST_VOI | SB_IT_FIRST_VOISTAT |
2273 	    SB_IT_LAST_VOISTAT);
2274 
2275 	return (vctx->cb(&sbv, vctx->usrctx));
2276 }
2277 
2278 int
2279 stats_v1_blob_visit(struct statsblobv1 *sb, stats_blob_visitcb_t func,
2280     void *usrctx)
2281 {
2282 	struct sb_visitcb_ctx vctx;
2283 
2284 	if (sb == NULL || sb->abi != STATS_ABI_V1 || func == NULL)
2285 		return (EINVAL);
2286 
2287 	vctx.cb = func;
2288 	vctx.usrctx = usrctx;
2289 
2290 	stats_v1_blob_iter(sb, stats_v1_itercb_visit, &vctx, 0);
2291 
2292 	return (0);
2293 }
2294 
2295 static int
2296 stats_v1_icb_reset_voistat(struct statsblobv1 *sb, struct voi *v __unused,
2297     struct voistat *vs, struct sb_iter_ctx *ctx __unused)
2298 {
2299 	void *vsd;
2300 
2301 	if (vs->stype == VS_STYPE_VOISTATE)
2302 		return (0);
2303 
2304 	vsd = BLOB_OFFSET(sb, vs->data_off);
2305 
2306 	/* Perform the stat type's default reset action. */
2307 	switch (vs->stype) {
2308 	case VS_STYPE_SUM:
2309 		switch (vs->dtype) {
2310 		case VSD_DTYPE_Q_S32:
2311 			Q_SIFVAL(VSD(q32, vsd)->sq32, 0);
2312 			break;
2313 		case VSD_DTYPE_Q_U32:
2314 			Q_SIFVAL(VSD(q32, vsd)->uq32, 0);
2315 			break;
2316 		case VSD_DTYPE_Q_S64:
2317 			Q_SIFVAL(VSD(q64, vsd)->sq64, 0);
2318 			break;
2319 		case VSD_DTYPE_Q_U64:
2320 			Q_SIFVAL(VSD(q64, vsd)->uq64, 0);
2321 			break;
2322 		default:
2323 			bzero(vsd, vs->dsz);
2324 			break;
2325 		}
2326 		break;
2327 	case VS_STYPE_MAX:
2328 		switch (vs->dtype) {
2329 		case VSD_DTYPE_Q_S32:
2330 			Q_SIFVAL(VSD(q32, vsd)->sq32,
2331 			    Q_IFMINVAL(VSD(q32, vsd)->sq32));
2332 			break;
2333 		case VSD_DTYPE_Q_U32:
2334 			Q_SIFVAL(VSD(q32, vsd)->uq32,
2335 			    Q_IFMINVAL(VSD(q32, vsd)->uq32));
2336 			break;
2337 		case VSD_DTYPE_Q_S64:
2338 			Q_SIFVAL(VSD(q64, vsd)->sq64,
2339 			    Q_IFMINVAL(VSD(q64, vsd)->sq64));
2340 			break;
2341 		case VSD_DTYPE_Q_U64:
2342 			Q_SIFVAL(VSD(q64, vsd)->uq64,
2343 			    Q_IFMINVAL(VSD(q64, vsd)->uq64));
2344 			break;
2345 		default:
2346 			memcpy(vsd, &numeric_limits[LIM_MIN][vs->dtype],
2347 			    vs->dsz);
2348 			break;
2349 		}
2350 		break;
2351 	case VS_STYPE_MIN:
2352 		switch (vs->dtype) {
2353 		case VSD_DTYPE_Q_S32:
2354 			Q_SIFVAL(VSD(q32, vsd)->sq32,
2355 			    Q_IFMAXVAL(VSD(q32, vsd)->sq32));
2356 			break;
2357 		case VSD_DTYPE_Q_U32:
2358 			Q_SIFVAL(VSD(q32, vsd)->uq32,
2359 			    Q_IFMAXVAL(VSD(q32, vsd)->uq32));
2360 			break;
2361 		case VSD_DTYPE_Q_S64:
2362 			Q_SIFVAL(VSD(q64, vsd)->sq64,
2363 			    Q_IFMAXVAL(VSD(q64, vsd)->sq64));
2364 			break;
2365 		case VSD_DTYPE_Q_U64:
2366 			Q_SIFVAL(VSD(q64, vsd)->uq64,
2367 			    Q_IFMAXVAL(VSD(q64, vsd)->uq64));
2368 			break;
2369 		default:
2370 			memcpy(vsd, &numeric_limits[LIM_MAX][vs->dtype],
2371 			    vs->dsz);
2372 			break;
2373 		}
2374 		break;
2375 	case VS_STYPE_HIST:
2376 		{
2377 		/* Reset bucket counts. */
2378 		struct voistatdata_hist *hist;
2379 		int i, is32bit;
2380 		uint16_t nbkts;
2381 
2382 		hist = VSD(hist, vsd);
2383 		switch (vs->dtype) {
2384 		case VSD_DTYPE_CRHIST32:
2385 			nbkts = HIST_VSDSZ2NBKTS(crhist32, vs->dsz);
2386 			is32bit = 1;
2387 			break;
2388 		case VSD_DTYPE_DRHIST32:
2389 			nbkts = HIST_VSDSZ2NBKTS(drhist32, vs->dsz);
2390 			is32bit = 1;
2391 			break;
2392 		case VSD_DTYPE_DVHIST32:
2393 			nbkts = HIST_VSDSZ2NBKTS(dvhist32, vs->dsz);
2394 			is32bit = 1;
2395 			break;
2396 		case VSD_DTYPE_CRHIST64:
2397 			nbkts = HIST_VSDSZ2NBKTS(crhist64, vs->dsz);
2398 			is32bit = 0;
2399 			break;
2400 		case VSD_DTYPE_DRHIST64:
2401 			nbkts = HIST_VSDSZ2NBKTS(drhist64, vs->dsz);
2402 			is32bit = 0;
2403 			break;
2404 		case VSD_DTYPE_DVHIST64:
2405 			nbkts = HIST_VSDSZ2NBKTS(dvhist64, vs->dsz);
2406 			is32bit = 0;
2407 			break;
2408 		default:
2409 			return (0);
2410 		}
2411 
2412 		bzero(VSD_HIST_FIELDPTR(hist, vs->dtype, oob),
2413 		    is32bit ? sizeof(uint32_t) : sizeof(uint64_t));
2414 		for (i = nbkts - 1; i >= 0; i--) {
2415 			bzero(VSD_HIST_FIELDPTR(hist, vs->dtype,
2416 			    bkts[i].cnt), is32bit ? sizeof(uint32_t) :
2417 			    sizeof(uint64_t));
2418 		}
2419 		break;
2420 		}
2421 	case VS_STYPE_TDGST:
2422 		{
2423 		/* Reset sample count centroids array/tree. */
2424 		struct voistatdata_tdgst *tdgst;
2425 		struct ctdth32 *ctd32tree;
2426 		struct ctdth64 *ctd64tree;
2427 		struct voistatdata_tdgstctd32 *ctd32;
2428 		struct voistatdata_tdgstctd64 *ctd64;
2429 
2430 		tdgst = VSD(tdgst, vsd);
2431 		switch (vs->dtype) {
2432 		case VSD_DTYPE_TDGSTCLUST32:
2433 			VSD(tdgstclust32, tdgst)->smplcnt = 0;
2434 			VSD(tdgstclust32, tdgst)->compcnt = 0;
2435 			ctd32tree = &VSD(tdgstclust32, tdgst)->ctdtree;
2436 			ARB_INIT(ctd32, ctdlnk, ctd32tree,
2437 			    ARB_MAXNODES(ctd32tree)) {
2438 				ctd32->cnt = 0;
2439 				Q_SIFVAL(ctd32->mu, 0);
2440 			}
2441 #ifdef DIAGNOSTIC
2442 			RB_INIT(&VSD(tdgstclust32, tdgst)->rbctdtree);
2443 #endif
2444 		break;
2445 		case VSD_DTYPE_TDGSTCLUST64:
2446 			VSD(tdgstclust64, tdgst)->smplcnt = 0;
2447 			VSD(tdgstclust64, tdgst)->compcnt = 0;
2448 			ctd64tree = &VSD(tdgstclust64, tdgst)->ctdtree;
2449 			ARB_INIT(ctd64, ctdlnk, ctd64tree,
2450 			    ARB_MAXNODES(ctd64tree)) {
2451 				ctd64->cnt = 0;
2452 				Q_SIFVAL(ctd64->mu, 0);
2453 			}
2454 #ifdef DIAGNOSTIC
2455 			RB_INIT(&VSD(tdgstclust64, tdgst)->rbctdtree);
2456 #endif
2457 		break;
2458 		default:
2459 			return (0);
2460 		}
2461 		break;
2462 		}
2463 	default:
2464 		KASSERT(0, ("Unknown VOI stat type %d", vs->stype));
2465 		break;
2466 	}
2467 
2468 	vs->errs = 0;
2469 	vs->flags &= ~VS_VSDVALID;
2470 
2471 	return (0);
2472 }
2473 
2474 int
2475 stats_v1_blob_snapshot(struct statsblobv1 **dst, size_t dstmaxsz,
2476     struct statsblobv1 *src, uint32_t flags)
2477 {
2478 	int error;
2479 
2480 	if (src != NULL && src->abi == STATS_ABI_V1) {
2481 		error = stats_v1_blob_clone(dst, dstmaxsz, src, flags);
2482 		if (!error) {
2483 			if (flags & SB_CLONE_RSTSRC) {
2484 				stats_v1_blob_iter(src,
2485 				    stats_v1_icb_reset_voistat, NULL, 0);
2486 				src->lastrst = stats_sbinuptime();
2487 			}
2488 			stats_v1_blob_finalise(*dst);
2489 		}
2490 	} else
2491 		error = EINVAL;
2492 
2493 	return (error);
2494 }
2495 
2496 static inline int
2497 stats_v1_voi_update_max(enum vsd_dtype voi_dtype __unused,
2498     struct voistatdata *voival, struct voistat *vs, void *vsd)
2499 {
2500 	int error;
2501 
2502 	KASSERT(vs->dtype < VSD_NUM_DTYPES,
2503 	    ("Unknown VSD dtype %d", vs->dtype));
2504 
2505 	error = 0;
2506 
2507 	switch (vs->dtype) {
2508 	case VSD_DTYPE_INT_S32:
2509 		if (VSD(int32, vsd)->s32 < voival->int32.s32) {
2510 			VSD(int32, vsd)->s32 = voival->int32.s32;
2511 			vs->flags |= VS_VSDVALID;
2512 		}
2513 		break;
2514 	case VSD_DTYPE_INT_U32:
2515 		if (VSD(int32, vsd)->u32 < voival->int32.u32) {
2516 			VSD(int32, vsd)->u32 = voival->int32.u32;
2517 			vs->flags |= VS_VSDVALID;
2518 		}
2519 		break;
2520 	case VSD_DTYPE_INT_S64:
2521 		if (VSD(int64, vsd)->s64 < voival->int64.s64) {
2522 			VSD(int64, vsd)->s64 = voival->int64.s64;
2523 			vs->flags |= VS_VSDVALID;
2524 		}
2525 		break;
2526 	case VSD_DTYPE_INT_U64:
2527 		if (VSD(int64, vsd)->u64 < voival->int64.u64) {
2528 			VSD(int64, vsd)->u64 = voival->int64.u64;
2529 			vs->flags |= VS_VSDVALID;
2530 		}
2531 		break;
2532 	case VSD_DTYPE_INT_SLONG:
2533 		if (VSD(intlong, vsd)->slong < voival->intlong.slong) {
2534 			VSD(intlong, vsd)->slong = voival->intlong.slong;
2535 			vs->flags |= VS_VSDVALID;
2536 		}
2537 		break;
2538 	case VSD_DTYPE_INT_ULONG:
2539 		if (VSD(intlong, vsd)->ulong < voival->intlong.ulong) {
2540 			VSD(intlong, vsd)->ulong = voival->intlong.ulong;
2541 			vs->flags |= VS_VSDVALID;
2542 		}
2543 		break;
2544 	case VSD_DTYPE_Q_S32:
2545 		if (Q_QLTQ(VSD(q32, vsd)->sq32, voival->q32.sq32) &&
2546 		    (0 == (error = Q_QCPYVALQ(&VSD(q32, vsd)->sq32,
2547 		    voival->q32.sq32)))) {
2548 			vs->flags |= VS_VSDVALID;
2549 		}
2550 		break;
2551 	case VSD_DTYPE_Q_U32:
2552 		if (Q_QLTQ(VSD(q32, vsd)->uq32, voival->q32.uq32) &&
2553 		    (0 == (error = Q_QCPYVALQ(&VSD(q32, vsd)->uq32,
2554 		    voival->q32.uq32)))) {
2555 			vs->flags |= VS_VSDVALID;
2556 		}
2557 		break;
2558 	case VSD_DTYPE_Q_S64:
2559 		if (Q_QLTQ(VSD(q64, vsd)->sq64, voival->q64.sq64) &&
2560 		    (0 == (error = Q_QCPYVALQ(&VSD(q64, vsd)->sq64,
2561 		    voival->q64.sq64)))) {
2562 			vs->flags |= VS_VSDVALID;
2563 		}
2564 		break;
2565 	case VSD_DTYPE_Q_U64:
2566 		if (Q_QLTQ(VSD(q64, vsd)->uq64, voival->q64.uq64) &&
2567 		    (0 == (error = Q_QCPYVALQ(&VSD(q64, vsd)->uq64,
2568 		    voival->q64.uq64)))) {
2569 			vs->flags |= VS_VSDVALID;
2570 		}
2571 		break;
2572 	default:
2573 		error = EINVAL;
2574 		break;
2575 	}
2576 
2577 	return (error);
2578 }
2579 
2580 static inline int
2581 stats_v1_voi_update_min(enum vsd_dtype voi_dtype __unused,
2582     struct voistatdata *voival, struct voistat *vs, void *vsd)
2583 {
2584 	int error;
2585 
2586 	KASSERT(vs->dtype < VSD_NUM_DTYPES,
2587 	    ("Unknown VSD dtype %d", vs->dtype));
2588 
2589 	error = 0;
2590 
2591 	switch (vs->dtype) {
2592 	case VSD_DTYPE_INT_S32:
2593 		if (VSD(int32, vsd)->s32 > voival->int32.s32) {
2594 			VSD(int32, vsd)->s32 = voival->int32.s32;
2595 			vs->flags |= VS_VSDVALID;
2596 		}
2597 		break;
2598 	case VSD_DTYPE_INT_U32:
2599 		if (VSD(int32, vsd)->u32 > voival->int32.u32) {
2600 			VSD(int32, vsd)->u32 = voival->int32.u32;
2601 			vs->flags |= VS_VSDVALID;
2602 		}
2603 		break;
2604 	case VSD_DTYPE_INT_S64:
2605 		if (VSD(int64, vsd)->s64 > voival->int64.s64) {
2606 			VSD(int64, vsd)->s64 = voival->int64.s64;
2607 			vs->flags |= VS_VSDVALID;
2608 		}
2609 		break;
2610 	case VSD_DTYPE_INT_U64:
2611 		if (VSD(int64, vsd)->u64 > voival->int64.u64) {
2612 			VSD(int64, vsd)->u64 = voival->int64.u64;
2613 			vs->flags |= VS_VSDVALID;
2614 		}
2615 		break;
2616 	case VSD_DTYPE_INT_SLONG:
2617 		if (VSD(intlong, vsd)->slong > voival->intlong.slong) {
2618 			VSD(intlong, vsd)->slong = voival->intlong.slong;
2619 			vs->flags |= VS_VSDVALID;
2620 		}
2621 		break;
2622 	case VSD_DTYPE_INT_ULONG:
2623 		if (VSD(intlong, vsd)->ulong > voival->intlong.ulong) {
2624 			VSD(intlong, vsd)->ulong = voival->intlong.ulong;
2625 			vs->flags |= VS_VSDVALID;
2626 		}
2627 		break;
2628 	case VSD_DTYPE_Q_S32:
2629 		if (Q_QGTQ(VSD(q32, vsd)->sq32, voival->q32.sq32) &&
2630 		    (0 == (error = Q_QCPYVALQ(&VSD(q32, vsd)->sq32,
2631 		    voival->q32.sq32)))) {
2632 			vs->flags |= VS_VSDVALID;
2633 		}
2634 		break;
2635 	case VSD_DTYPE_Q_U32:
2636 		if (Q_QGTQ(VSD(q32, vsd)->uq32, voival->q32.uq32) &&
2637 		    (0 == (error = Q_QCPYVALQ(&VSD(q32, vsd)->uq32,
2638 		    voival->q32.uq32)))) {
2639 			vs->flags |= VS_VSDVALID;
2640 		}
2641 		break;
2642 	case VSD_DTYPE_Q_S64:
2643 		if (Q_QGTQ(VSD(q64, vsd)->sq64, voival->q64.sq64) &&
2644 		    (0 == (error = Q_QCPYVALQ(&VSD(q64, vsd)->sq64,
2645 		    voival->q64.sq64)))) {
2646 			vs->flags |= VS_VSDVALID;
2647 		}
2648 		break;
2649 	case VSD_DTYPE_Q_U64:
2650 		if (Q_QGTQ(VSD(q64, vsd)->uq64, voival->q64.uq64) &&
2651 		    (0 == (error = Q_QCPYVALQ(&VSD(q64, vsd)->uq64,
2652 		    voival->q64.uq64)))) {
2653 			vs->flags |= VS_VSDVALID;
2654 		}
2655 		break;
2656 	default:
2657 		error = EINVAL;
2658 		break;
2659 	}
2660 
2661 	return (error);
2662 }
2663 
2664 static inline int
2665 stats_v1_voi_update_sum(enum vsd_dtype voi_dtype __unused,
2666     struct voistatdata *voival, struct voistat *vs, void *vsd)
2667 {
2668 	int error;
2669 
2670 	KASSERT(vs->dtype < VSD_NUM_DTYPES,
2671 	    ("Unknown VSD dtype %d", vs->dtype));
2672 
2673 	error = 0;
2674 
2675 	switch (vs->dtype) {
2676 	case VSD_DTYPE_INT_S32:
2677 		VSD(int32, vsd)->s32 += voival->int32.s32;
2678 		break;
2679 	case VSD_DTYPE_INT_U32:
2680 		VSD(int32, vsd)->u32 += voival->int32.u32;
2681 		break;
2682 	case VSD_DTYPE_INT_S64:
2683 		VSD(int64, vsd)->s64 += voival->int64.s64;
2684 		break;
2685 	case VSD_DTYPE_INT_U64:
2686 		VSD(int64, vsd)->u64 += voival->int64.u64;
2687 		break;
2688 	case VSD_DTYPE_INT_SLONG:
2689 		VSD(intlong, vsd)->slong += voival->intlong.slong;
2690 		break;
2691 	case VSD_DTYPE_INT_ULONG:
2692 		VSD(intlong, vsd)->ulong += voival->intlong.ulong;
2693 		break;
2694 	case VSD_DTYPE_Q_S32:
2695 		error = Q_QADDQ(&VSD(q32, vsd)->sq32, voival->q32.sq32);
2696 		break;
2697 	case VSD_DTYPE_Q_U32:
2698 		error = Q_QADDQ(&VSD(q32, vsd)->uq32, voival->q32.uq32);
2699 		break;
2700 	case VSD_DTYPE_Q_S64:
2701 		error = Q_QADDQ(&VSD(q64, vsd)->sq64, voival->q64.sq64);
2702 		break;
2703 	case VSD_DTYPE_Q_U64:
2704 		error = Q_QADDQ(&VSD(q64, vsd)->uq64, voival->q64.uq64);
2705 		break;
2706 	default:
2707 		error = EINVAL;
2708 		break;
2709 	}
2710 
2711 	if (!error)
2712 		vs->flags |= VS_VSDVALID;
2713 
2714 	return (error);
2715 }
2716 
2717 static inline int
2718 stats_v1_voi_update_hist(enum vsd_dtype voi_dtype, struct voistatdata *voival,
2719     struct voistat *vs, struct voistatdata_hist *hist)
2720 {
2721 	struct voistatdata_numeric *bkt_lb, *bkt_ub;
2722 	uint64_t *oob64, *cnt64;
2723 	uint32_t *oob32, *cnt32;
2724 	int error, i, found, is32bit, has_ub, eq_only;
2725 
2726 	error = 0;
2727 
2728 	switch (vs->dtype) {
2729 	case VSD_DTYPE_CRHIST32:
2730 		i = HIST_VSDSZ2NBKTS(crhist32, vs->dsz);
2731 		is32bit = 1;
2732 		has_ub = eq_only = 0;
2733 		oob32 = &VSD(crhist32, hist)->oob;
2734 		break;
2735 	case VSD_DTYPE_DRHIST32:
2736 		i = HIST_VSDSZ2NBKTS(drhist32, vs->dsz);
2737 		is32bit = has_ub = 1;
2738 		eq_only = 0;
2739 		oob32 = &VSD(drhist32, hist)->oob;
2740 		break;
2741 	case VSD_DTYPE_DVHIST32:
2742 		i = HIST_VSDSZ2NBKTS(dvhist32, vs->dsz);
2743 		is32bit = eq_only = 1;
2744 		has_ub = 0;
2745 		oob32 = &VSD(dvhist32, hist)->oob;
2746 		break;
2747 	case VSD_DTYPE_CRHIST64:
2748 		i = HIST_VSDSZ2NBKTS(crhist64, vs->dsz);
2749 		is32bit = has_ub = eq_only = 0;
2750 		oob64 = &VSD(crhist64, hist)->oob;
2751 		break;
2752 	case VSD_DTYPE_DRHIST64:
2753 		i = HIST_VSDSZ2NBKTS(drhist64, vs->dsz);
2754 		is32bit = eq_only = 0;
2755 		has_ub = 1;
2756 		oob64 = &VSD(drhist64, hist)->oob;
2757 		break;
2758 	case VSD_DTYPE_DVHIST64:
2759 		i = HIST_VSDSZ2NBKTS(dvhist64, vs->dsz);
2760 		is32bit = has_ub = 0;
2761 		eq_only = 1;
2762 		oob64 = &VSD(dvhist64, hist)->oob;
2763 		break;
2764 	default:
2765 		return (EINVAL);
2766 	}
2767 	i--; /* Adjust for 0-based array index. */
2768 
2769 	/* XXXLAS: Should probably use a better bucket search algorithm. ARB? */
2770 	for (found = 0; i >= 0 && !found; i--) {
2771 		switch (vs->dtype) {
2772 		case VSD_DTYPE_CRHIST32:
2773 			bkt_lb = &VSD(crhist32, hist)->bkts[i].lb;
2774 			cnt32 = &VSD(crhist32, hist)->bkts[i].cnt;
2775 			break;
2776 		case VSD_DTYPE_DRHIST32:
2777 			bkt_lb = &VSD(drhist32, hist)->bkts[i].lb;
2778 			bkt_ub = &VSD(drhist32, hist)->bkts[i].ub;
2779 			cnt32 = &VSD(drhist32, hist)->bkts[i].cnt;
2780 			break;
2781 		case VSD_DTYPE_DVHIST32:
2782 			bkt_lb = &VSD(dvhist32, hist)->bkts[i].val;
2783 			cnt32 = &VSD(dvhist32, hist)->bkts[i].cnt;
2784 			break;
2785 		case VSD_DTYPE_CRHIST64:
2786 			bkt_lb = &VSD(crhist64, hist)->bkts[i].lb;
2787 			cnt64 = &VSD(crhist64, hist)->bkts[i].cnt;
2788 			break;
2789 		case VSD_DTYPE_DRHIST64:
2790 			bkt_lb = &VSD(drhist64, hist)->bkts[i].lb;
2791 			bkt_ub = &VSD(drhist64, hist)->bkts[i].ub;
2792 			cnt64 = &VSD(drhist64, hist)->bkts[i].cnt;
2793 			break;
2794 		case VSD_DTYPE_DVHIST64:
2795 			bkt_lb = &VSD(dvhist64, hist)->bkts[i].val;
2796 			cnt64 = &VSD(dvhist64, hist)->bkts[i].cnt;
2797 			break;
2798 		default:
2799 			return (EINVAL);
2800 		}
2801 
2802 		switch (voi_dtype) {
2803 		case VSD_DTYPE_INT_S32:
2804 			if (voival->int32.s32 >= bkt_lb->int32.s32) {
2805 				if ((eq_only && voival->int32.s32 ==
2806 				    bkt_lb->int32.s32) ||
2807 				    (!eq_only && (!has_ub ||
2808 				    voival->int32.s32 < bkt_ub->int32.s32)))
2809 					found = 1;
2810 			}
2811 			break;
2812 		case VSD_DTYPE_INT_U32:
2813 			if (voival->int32.u32 >= bkt_lb->int32.u32) {
2814 				if ((eq_only && voival->int32.u32 ==
2815 				    bkt_lb->int32.u32) ||
2816 				    (!eq_only && (!has_ub ||
2817 				    voival->int32.u32 < bkt_ub->int32.u32)))
2818 					found = 1;
2819 			}
2820 			break;
2821 		case VSD_DTYPE_INT_S64:
2822 			if (voival->int64.s64 >= bkt_lb->int64.s64)
2823 				if ((eq_only && voival->int64.s64 ==
2824 				    bkt_lb->int64.s64) ||
2825 				    (!eq_only && (!has_ub ||
2826 				    voival->int64.s64 < bkt_ub->int64.s64)))
2827 					found = 1;
2828 			break;
2829 		case VSD_DTYPE_INT_U64:
2830 			if (voival->int64.u64 >= bkt_lb->int64.u64)
2831 				if ((eq_only && voival->int64.u64 ==
2832 				    bkt_lb->int64.u64) ||
2833 				    (!eq_only && (!has_ub ||
2834 				    voival->int64.u64 < bkt_ub->int64.u64)))
2835 					found = 1;
2836 			break;
2837 		case VSD_DTYPE_INT_SLONG:
2838 			if (voival->intlong.slong >= bkt_lb->intlong.slong)
2839 				if ((eq_only && voival->intlong.slong ==
2840 				    bkt_lb->intlong.slong) ||
2841 				    (!eq_only && (!has_ub ||
2842 				    voival->intlong.slong <
2843 				    bkt_ub->intlong.slong)))
2844 					found = 1;
2845 			break;
2846 		case VSD_DTYPE_INT_ULONG:
2847 			if (voival->intlong.ulong >= bkt_lb->intlong.ulong)
2848 				if ((eq_only && voival->intlong.ulong ==
2849 				    bkt_lb->intlong.ulong) ||
2850 				    (!eq_only && (!has_ub ||
2851 				    voival->intlong.ulong <
2852 				    bkt_ub->intlong.ulong)))
2853 					found = 1;
2854 			break;
2855 		case VSD_DTYPE_Q_S32:
2856 			if (Q_QGEQ(voival->q32.sq32, bkt_lb->q32.sq32))
2857 				if ((eq_only && Q_QEQ(voival->q32.sq32,
2858 				    bkt_lb->q32.sq32)) ||
2859 				    (!eq_only && (!has_ub ||
2860 				    Q_QLTQ(voival->q32.sq32,
2861 				    bkt_ub->q32.sq32))))
2862 					found = 1;
2863 			break;
2864 		case VSD_DTYPE_Q_U32:
2865 			if (Q_QGEQ(voival->q32.uq32, bkt_lb->q32.uq32))
2866 				if ((eq_only && Q_QEQ(voival->q32.uq32,
2867 				    bkt_lb->q32.uq32)) ||
2868 				    (!eq_only && (!has_ub ||
2869 				    Q_QLTQ(voival->q32.uq32,
2870 				    bkt_ub->q32.uq32))))
2871 					found = 1;
2872 			break;
2873 		case VSD_DTYPE_Q_S64:
2874 			if (Q_QGEQ(voival->q64.sq64, bkt_lb->q64.sq64))
2875 				if ((eq_only && Q_QEQ(voival->q64.sq64,
2876 				    bkt_lb->q64.sq64)) ||
2877 				    (!eq_only && (!has_ub ||
2878 				    Q_QLTQ(voival->q64.sq64,
2879 				    bkt_ub->q64.sq64))))
2880 					found = 1;
2881 			break;
2882 		case VSD_DTYPE_Q_U64:
2883 			if (Q_QGEQ(voival->q64.uq64, bkt_lb->q64.uq64))
2884 				if ((eq_only && Q_QEQ(voival->q64.uq64,
2885 				    bkt_lb->q64.uq64)) ||
2886 				    (!eq_only && (!has_ub ||
2887 				    Q_QLTQ(voival->q64.uq64,
2888 				    bkt_ub->q64.uq64))))
2889 					found = 1;
2890 			break;
2891 		default:
2892 			break;
2893 		}
2894 	}
2895 
2896 	if (found) {
2897 		if (is32bit)
2898 			*cnt32 += 1;
2899 		else
2900 			*cnt64 += 1;
2901 	} else {
2902 		if (is32bit)
2903 			*oob32 += 1;
2904 		else
2905 			*oob64 += 1;
2906 	}
2907 
2908 	vs->flags |= VS_VSDVALID;
2909 	return (error);
2910 }
2911 
2912 static inline int
2913 stats_v1_vsd_tdgst_compress(enum vsd_dtype vs_dtype,
2914     struct voistatdata_tdgst *tdgst, int attempt)
2915 {
2916 	struct ctdth32 *ctd32tree;
2917 	struct ctdth64 *ctd64tree;
2918 	struct voistatdata_tdgstctd32 *ctd32;
2919 	struct voistatdata_tdgstctd64 *ctd64;
2920 	uint64_t ebits, idxmask;
2921 	uint32_t bitsperidx, nebits;
2922 	int error, idx, is32bit, maxctds, remctds, tmperr;
2923 
2924 	error = 0;
2925 
2926 	switch (vs_dtype) {
2927 	case VSD_DTYPE_TDGSTCLUST32:
2928 		ctd32tree = &VSD(tdgstclust32, tdgst)->ctdtree;
2929 		if (!ARB_FULL(ctd32tree))
2930 			return (0);
2931 		VSD(tdgstclust32, tdgst)->compcnt++;
2932 		maxctds = remctds = ARB_MAXNODES(ctd32tree);
2933 		ARB_RESET_TREE(ctd32tree, ctdth32, maxctds);
2934 		VSD(tdgstclust32, tdgst)->smplcnt = 0;
2935 		is32bit = 1;
2936 		ctd64tree = NULL;
2937 		ctd64 = NULL;
2938 #ifdef DIAGNOSTIC
2939 		RB_INIT(&VSD(tdgstclust32, tdgst)->rbctdtree);
2940 #endif
2941 		break;
2942 	case VSD_DTYPE_TDGSTCLUST64:
2943 		ctd64tree = &VSD(tdgstclust64, tdgst)->ctdtree;
2944 		if (!ARB_FULL(ctd64tree))
2945 			return (0);
2946 		VSD(tdgstclust64, tdgst)->compcnt++;
2947 		maxctds = remctds = ARB_MAXNODES(ctd64tree);
2948 		ARB_RESET_TREE(ctd64tree, ctdth64, maxctds);
2949 		VSD(tdgstclust64, tdgst)->smplcnt = 0;
2950 		is32bit = 0;
2951 		ctd32tree = NULL;
2952 		ctd32 = NULL;
2953 #ifdef DIAGNOSTIC
2954 		RB_INIT(&VSD(tdgstclust64, tdgst)->rbctdtree);
2955 #endif
2956 		break;
2957 	default:
2958 		return (EINVAL);
2959 	}
2960 
2961 	/*
2962 	 * Rebuild the t-digest ARB by pseudorandomly selecting centroids and
2963 	 * re-inserting the mu/cnt of each as a value and corresponding weight.
2964 	 */
2965 
2966 	/*
2967 	 * XXXCEM: random(9) is currently rand(3), not random(3).  rand(3)
2968 	 * RAND_MAX happens to be approximately 31 bits (range [0,
2969 	 * 0x7ffffffd]), so the math kinda works out.  When/if this portion of
2970 	 * the code is compiled in userspace, it gets the random(3) behavior,
2971 	 * which has expected range [0, 0x7fffffff].
2972 	 */
2973 #define	bitsperrand 31
2974 	ebits = 0;
2975 	nebits = 0;
2976 	bitsperidx = fls(maxctds);
2977 	KASSERT(bitsperidx <= sizeof(ebits) << 3,
2978 	    ("%s: bitsperidx=%d, ebits=%d",
2979 	    __func__, bitsperidx, (int)(sizeof(ebits) << 3)));
2980 	idxmask = (UINT64_C(1) << bitsperidx) - 1;
2981 
2982 	/* Initialise the free list with randomised centroid indices. */
2983 	for (; remctds > 0; remctds--) {
2984 		while (nebits < bitsperidx) {
2985 			ebits |= ((uint64_t)random()) << nebits;
2986 			nebits += bitsperrand;
2987 			if (nebits > (sizeof(ebits) << 3))
2988 				nebits = sizeof(ebits) << 3;
2989 		}
2990 		idx = ebits & idxmask;
2991 		nebits -= bitsperidx;
2992 		ebits >>= bitsperidx;
2993 
2994 		/*
2995 		 * Select the next centroid to put on the ARB free list. We
2996 		 * start with the centroid at our randomly selected array index,
2997 		 * and work our way forwards until finding one (the latter
2998 		 * aspect reduces re-insertion randomness, but is good enough).
2999 		 */
3000 		do {
3001 			if (idx >= maxctds)
3002 				idx %= maxctds;
3003 
3004 			if (is32bit)
3005 				ctd32 = ARB_NODE(ctd32tree, idx);
3006 			else
3007 				ctd64 = ARB_NODE(ctd64tree, idx);
3008 		} while ((is32bit ? ARB_ISFREE(ctd32, ctdlnk) :
3009 		    ARB_ISFREE(ctd64, ctdlnk)) && ++idx);
3010 
3011 		/* Put the centroid on the ARB free list. */
3012 		if (is32bit)
3013 			ARB_RETURNFREE(ctd32tree, ctd32, ctdlnk);
3014 		else
3015 			ARB_RETURNFREE(ctd64tree, ctd64, ctdlnk);
3016 	}
3017 
3018 	/*
3019 	 * The free list now contains the randomised indices of every centroid.
3020 	 * Walk the free list from start to end, re-inserting each centroid's
3021 	 * mu/cnt. The tdgst_add() call may or may not consume the free centroid
3022 	 * we re-insert values from during each loop iteration, so we must latch
3023 	 * the index of the next free list centroid before the re-insertion
3024 	 * call. The previous loop above should have left the centroid pointer
3025 	 * pointing to the element at the head of the free list.
3026 	 */
3027 	KASSERT((is32bit ?
3028 	    ARB_FREEIDX(ctd32tree) == ARB_SELFIDX(ctd32tree, ctd32) :
3029 	    ARB_FREEIDX(ctd64tree) == ARB_SELFIDX(ctd64tree, ctd64)),
3030 	    ("%s: t-digest ARB@%p free list bug", __func__,
3031 	    (is32bit ? (void *)ctd32tree : (void *)ctd64tree)));
3032 	remctds = maxctds;
3033 	while ((is32bit ? ctd32 != NULL : ctd64 != NULL)) {
3034 		tmperr = 0;
3035 		if (is32bit) {
3036 			s64q_t x;
3037 
3038 			idx = ARB_NEXTFREEIDX(ctd32, ctdlnk);
3039 			/* Cloning a s32q_t into a s64q_t should never fail. */
3040 			tmperr = Q_QCLONEQ(&x, ctd32->mu);
3041 			tmperr = tmperr ? tmperr : stats_v1_vsd_tdgst_add(
3042 			    vs_dtype, tdgst, x, ctd32->cnt, attempt);
3043 			ctd32 = ARB_NODE(ctd32tree, idx);
3044 			KASSERT(ctd32 == NULL || ARB_ISFREE(ctd32, ctdlnk),
3045 			    ("%s: t-digest ARB@%p free list bug", __func__,
3046 			    ctd32tree));
3047 		} else {
3048 			idx = ARB_NEXTFREEIDX(ctd64, ctdlnk);
3049 			tmperr = stats_v1_vsd_tdgst_add(vs_dtype, tdgst,
3050 			    ctd64->mu, ctd64->cnt, attempt);
3051 			ctd64 = ARB_NODE(ctd64tree, idx);
3052 			KASSERT(ctd64 == NULL || ARB_ISFREE(ctd64, ctdlnk),
3053 			    ("%s: t-digest ARB@%p free list bug", __func__,
3054 			    ctd64tree));
3055 		}
3056 		/*
3057 		 * This process should not produce errors, bugs notwithstanding.
3058 		 * Just in case, latch any errors and attempt all re-insertions.
3059 		 */
3060 		error = tmperr ? tmperr : error;
3061 		remctds--;
3062 	}
3063 
3064 	KASSERT(remctds == 0, ("%s: t-digest ARB@%p free list bug", __func__,
3065 	    (is32bit ? (void *)ctd32tree : (void *)ctd64tree)));
3066 
3067 	return (error);
3068 }
3069 
3070 static inline int
3071 stats_v1_vsd_tdgst_add(enum vsd_dtype vs_dtype, struct voistatdata_tdgst *tdgst,
3072     s64q_t x, uint64_t weight, int attempt)
3073 {
3074 #ifdef DIAGNOSTIC
3075 	char qstr[Q_MAXSTRLEN(x, 10)];
3076 #endif
3077 	struct ctdth32 *ctd32tree;
3078 	struct ctdth64 *ctd64tree;
3079 	void *closest, *cur, *lb, *ub;
3080 	struct voistatdata_tdgstctd32 *ctd32;
3081 	struct voistatdata_tdgstctd64 *ctd64;
3082 	uint64_t cnt, smplcnt, sum, tmpsum;
3083 	s64q_t k, minz, q, z;
3084 	int error, is32bit, n;
3085 
3086 	error = 0;
3087 	minz = Q_INI(&z, 0, 0, Q_NFBITS(x));
3088 
3089 	switch (vs_dtype) {
3090 	case VSD_DTYPE_TDGSTCLUST32:
3091 		if ((UINT32_MAX - weight) < VSD(tdgstclust32, tdgst)->smplcnt)
3092 			error = EOVERFLOW;
3093 		smplcnt = VSD(tdgstclust32, tdgst)->smplcnt;
3094 		ctd32tree = &VSD(tdgstclust32, tdgst)->ctdtree;
3095 		is32bit = 1;
3096 		ctd64tree = NULL;
3097 		ctd64 = NULL;
3098 		break;
3099 	case VSD_DTYPE_TDGSTCLUST64:
3100 		if ((UINT64_MAX - weight) < VSD(tdgstclust64, tdgst)->smplcnt)
3101 			error = EOVERFLOW;
3102 		smplcnt = VSD(tdgstclust64, tdgst)->smplcnt;
3103 		ctd64tree = &VSD(tdgstclust64, tdgst)->ctdtree;
3104 		is32bit = 0;
3105 		ctd32tree = NULL;
3106 		ctd32 = NULL;
3107 		break;
3108 	default:
3109 		error = EINVAL;
3110 		break;
3111 	}
3112 
3113 	if (error)
3114 		return (error);
3115 
3116 	/*
3117 	 * Inspired by Ted Dunning's AVLTreeDigest.java
3118 	 */
3119 	do {
3120 #if defined(DIAGNOSTIC)
3121 		KASSERT(attempt < 5,
3122 		    ("%s: Too many attempts", __func__));
3123 #endif
3124 		if (attempt >= 5)
3125 			return (EAGAIN);
3126 
3127 		Q_SIFVAL(minz, Q_IFMAXVAL(minz));
3128 		closest = ub = NULL;
3129 		sum = tmpsum = 0;
3130 
3131 		if (is32bit)
3132 			lb = cur = (void *)(ctd32 = ARB_MIN(ctdth32, ctd32tree));
3133 		else
3134 			lb = cur = (void *)(ctd64 = ARB_MIN(ctdth64, ctd64tree));
3135 
3136 		if (lb == NULL) /* Empty tree. */
3137 			lb = (is32bit ? (void *)ARB_ROOT(ctd32tree) :
3138 			    (void *)ARB_ROOT(ctd64tree));
3139 
3140 		/*
3141 		 * Find the set of centroids with minimum distance to x and
3142 		 * compute the sum of counts for all centroids with mean less
3143 		 * than the first centroid in the set.
3144 		 */
3145 		for (; cur != NULL;
3146 		    cur = (is32bit ?
3147 		    (void *)(ctd32 = ARB_NEXT(ctdth32, ctd32tree, ctd32)) :
3148 		    (void *)(ctd64 = ARB_NEXT(ctdth64, ctd64tree, ctd64)))) {
3149 			if (is32bit) {
3150 				cnt = ctd32->cnt;
3151 				KASSERT(Q_PRECEQ(ctd32->mu, x),
3152 				    ("%s: Q_RELPREC(mu,x)=%d", __func__,
3153 				    Q_RELPREC(ctd32->mu, x)));
3154 				/* Ok to assign as both have same precision. */
3155 				z = ctd32->mu;
3156 			} else {
3157 				cnt = ctd64->cnt;
3158 				KASSERT(Q_PRECEQ(ctd64->mu, x),
3159 				    ("%s: Q_RELPREC(mu,x)=%d", __func__,
3160 				    Q_RELPREC(ctd64->mu, x)));
3161 				/* Ok to assign as both have same precision. */
3162 				z = ctd64->mu;
3163 			}
3164 
3165 			error = Q_QSUBQ(&z, x);
3166 #if defined(DIAGNOSTIC)
3167 			KASSERT(!error, ("%s: unexpected error %d", __func__,
3168 			    error));
3169 #endif
3170 			if (error)
3171 				return (error);
3172 
3173 			z = Q_QABS(z);
3174 			if (Q_QLTQ(z, minz)) {
3175 				minz = z;
3176 				lb = cur;
3177 				sum = tmpsum;
3178 				tmpsum += cnt;
3179 			} else if (Q_QGTQ(z, minz)) {
3180 				ub = cur;
3181 				break;
3182 			}
3183 		}
3184 
3185 		cur = (is32bit ?
3186 		    (void *)(ctd32 = (struct voistatdata_tdgstctd32 *)lb) :
3187 		    (void *)(ctd64 = (struct voistatdata_tdgstctd64 *)lb));
3188 
3189 		for (n = 0; cur != ub; cur = (is32bit ?
3190 		    (void *)(ctd32 = ARB_NEXT(ctdth32, ctd32tree, ctd32)) :
3191 		    (void *)(ctd64 = ARB_NEXT(ctdth64, ctd64tree, ctd64)))) {
3192 			if (is32bit)
3193 				cnt = ctd32->cnt;
3194 			else
3195 				cnt = ctd64->cnt;
3196 
3197 			q = Q_CTRLINI(16);
3198 			if (smplcnt == 1)
3199 				error = Q_QFRACI(&q, 1, 2);
3200 			else
3201 				/* [ sum + ((cnt - 1) / 2) ] / (smplcnt - 1) */
3202 				error = Q_QFRACI(&q, (sum << 1) + cnt - 1,
3203 				    (smplcnt - 1) << 1);
3204 			k = q;
3205 			/* k = q x 4 x samplcnt x attempt */
3206 			error |= Q_QMULI(&k, 4 * smplcnt * attempt);
3207 			/* k = k x (1 - q) */
3208 			error |= Q_QSUBI(&q, 1);
3209 			q = Q_QABS(q);
3210 			error |= Q_QMULQ(&k, q);
3211 #if defined(DIAGNOSTIC)
3212 #if !defined(_KERNEL)
3213 			double q_dbl, k_dbl, q2d, k2d;
3214 			q2d = Q_Q2D(q);
3215 			k2d = Q_Q2D(k);
3216 			q_dbl = smplcnt == 1 ? 0.5 :
3217 			    (sum + ((cnt - 1)  / 2.0)) / (double)(smplcnt - 1);
3218 			k_dbl = 4 * smplcnt * q_dbl * (1.0 - q_dbl) * attempt;
3219 			/*
3220 			 * If the difference between q and q_dbl is greater than
3221 			 * the fractional precision of q, something is off.
3222 			 * NB: q is holding the value of 1 - q
3223 			 */
3224 			q_dbl = 1.0 - q_dbl;
3225 			KASSERT((q_dbl > q2d ? q_dbl - q2d : q2d - q_dbl) <
3226 			    (1.05 * ((double)1 / (double)(1ULL << Q_NFBITS(q)))),
3227 			    ("Q-type q bad precision"));
3228 			KASSERT((k_dbl > k2d ? k_dbl - k2d : k2d - k_dbl) <
3229 			    1.0 + (0.01 * smplcnt),
3230 			    ("Q-type k bad precision"));
3231 #endif /* !_KERNEL */
3232 			KASSERT(!error, ("%s: unexpected error %d", __func__,
3233 			    error));
3234 #endif /* DIAGNOSTIC */
3235 			if (error)
3236 				return (error);
3237 			if ((is32bit && ((ctd32->cnt + weight) <=
3238 			    (uint64_t)Q_GIVAL(k))) ||
3239 			    (!is32bit && ((ctd64->cnt + weight) <=
3240 			    (uint64_t)Q_GIVAL(k)))) {
3241 				n++;
3242 				/* random() produces 31 bits. */
3243 				if (random() < (INT32_MAX / n))
3244 					closest = cur;
3245 			}
3246 			sum += cnt;
3247 		}
3248 	} while (closest == NULL &&
3249 	    (is32bit ? ARB_FULL(ctd32tree) : ARB_FULL(ctd64tree)) &&
3250 	    (error = stats_v1_vsd_tdgst_compress(vs_dtype, tdgst,
3251 	    attempt++)) == 0);
3252 
3253 	if (error)
3254 		return (error);
3255 
3256 	if (closest != NULL) {
3257 		/* Merge with an existing centroid. */
3258 		if (is32bit) {
3259 			ctd32 = (struct voistatdata_tdgstctd32 *)closest;
3260 			error = Q_QSUBQ(&x, ctd32->mu);
3261 			error = error ? error :
3262 			    Q_QDIVI(&x, ctd32->cnt + weight);
3263 			if (error || (error = Q_QADDQ(&ctd32->mu, x))) {
3264 #ifdef DIAGNOSTIC
3265 				KASSERT(!error, ("%s: unexpected error %d",
3266 				    __func__, error));
3267 #endif
3268 				return (error);
3269 			}
3270 			ctd32->cnt += weight;
3271 			error = ARB_REINSERT(ctdth32, ctd32tree, ctd32) ==
3272 			    NULL ? 0 : EALREADY;
3273 #ifdef DIAGNOSTIC
3274 			RB_REINSERT(rbctdth32,
3275 			    &VSD(tdgstclust32, tdgst)->rbctdtree, ctd32);
3276 #endif
3277 		} else {
3278 			ctd64 = (struct voistatdata_tdgstctd64 *)closest;
3279 			error = Q_QSUBQ(&x, ctd64->mu);
3280 			error = error ? error :
3281 			    Q_QDIVI(&x, ctd64->cnt + weight);
3282 			if (error || (error = Q_QADDQ(&ctd64->mu, x))) {
3283 				KASSERT(!error, ("%s: unexpected error %d",
3284 				    __func__, error));
3285 				return (error);
3286 			}
3287 			ctd64->cnt += weight;
3288 			error = ARB_REINSERT(ctdth64, ctd64tree, ctd64) ==
3289 			    NULL ? 0 : EALREADY;
3290 #ifdef DIAGNOSTIC
3291 			RB_REINSERT(rbctdth64,
3292 			    &VSD(tdgstclust64, tdgst)->rbctdtree, ctd64);
3293 #endif
3294 		}
3295 	} else {
3296 		/*
3297 		 * Add a new centroid. If digest compression is working
3298 		 * correctly, there should always be at least one free.
3299 		 */
3300 		if (is32bit) {
3301 			ctd32 = ARB_GETFREE(ctd32tree, ctdlnk);
3302 #ifdef DIAGNOSTIC
3303 			KASSERT(ctd32 != NULL,
3304 			    ("%s: t-digest@%p has no free centroids",
3305 			    __func__, tdgst));
3306 #endif
3307 			if (ctd32 == NULL)
3308 				return (EAGAIN);
3309 			if ((error = Q_QCPYVALQ(&ctd32->mu, x)))
3310 				return (error);
3311 			ctd32->cnt = weight;
3312 			error = ARB_INSERT(ctdth32, ctd32tree, ctd32) == NULL ?
3313 			    0 : EALREADY;
3314 #ifdef DIAGNOSTIC
3315 			RB_INSERT(rbctdth32,
3316 			    &VSD(tdgstclust32, tdgst)->rbctdtree, ctd32);
3317 #endif
3318 		} else {
3319 			ctd64 = ARB_GETFREE(ctd64tree, ctdlnk);
3320 #ifdef DIAGNOSTIC
3321 			KASSERT(ctd64 != NULL,
3322 			    ("%s: t-digest@%p has no free centroids",
3323 			    __func__, tdgst));
3324 #endif
3325 			if (ctd64 == NULL) /* Should not happen. */
3326 				return (EAGAIN);
3327 			/* Direct assignment ok as both have same type/prec. */
3328 			ctd64->mu = x;
3329 			ctd64->cnt = weight;
3330 			error = ARB_INSERT(ctdth64, ctd64tree, ctd64) == NULL ?
3331 			    0 : EALREADY;
3332 #ifdef DIAGNOSTIC
3333 			RB_INSERT(rbctdth64, &VSD(tdgstclust64,
3334 			    tdgst)->rbctdtree, ctd64);
3335 #endif
3336 		}
3337 	}
3338 
3339 	if (is32bit)
3340 		VSD(tdgstclust32, tdgst)->smplcnt += weight;
3341 	else {
3342 		VSD(tdgstclust64, tdgst)->smplcnt += weight;
3343 
3344 #ifdef DIAGNOSTIC
3345 		struct rbctdth64 *rbctdtree =
3346 		    &VSD(tdgstclust64, tdgst)->rbctdtree;
3347 		struct voistatdata_tdgstctd64 *rbctd64;
3348 		int i = 0;
3349 		ARB_FOREACH(ctd64, ctdth64, ctd64tree) {
3350 			rbctd64 = (i == 0 ? RB_MIN(rbctdth64, rbctdtree) :
3351 			    RB_NEXT(rbctdth64, rbctdtree, rbctd64));
3352 
3353 			if (i >= ARB_CURNODES(ctd64tree)
3354 			    || ctd64 != rbctd64
3355 			    || ARB_MIN(ctdth64, ctd64tree) !=
3356 			       RB_MIN(rbctdth64, rbctdtree)
3357 			    || ARB_MAX(ctdth64, ctd64tree) !=
3358 			       RB_MAX(rbctdth64, rbctdtree)
3359 			    || ARB_LEFTIDX(ctd64, ctdlnk) !=
3360 			       ARB_SELFIDX(ctd64tree, RB_LEFT(rbctd64, rblnk))
3361 			    || ARB_RIGHTIDX(ctd64, ctdlnk) !=
3362 			       ARB_SELFIDX(ctd64tree, RB_RIGHT(rbctd64, rblnk))
3363 			    || ARB_PARENTIDX(ctd64, ctdlnk) !=
3364 			       ARB_SELFIDX(ctd64tree,
3365 			       RB_PARENT(rbctd64, rblnk))) {
3366 				Q_TOSTR(ctd64->mu, -1, 10, qstr, sizeof(qstr));
3367 				printf("ARB ctd=%3d p=%3d l=%3d r=%3d c=%2d "
3368 				    "mu=%s\n",
3369 				    (int)ARB_SELFIDX(ctd64tree, ctd64),
3370 				    ARB_PARENTIDX(ctd64, ctdlnk),
3371 				    ARB_LEFTIDX(ctd64, ctdlnk),
3372 				    ARB_RIGHTIDX(ctd64, ctdlnk),
3373 				    ARB_COLOR(ctd64, ctdlnk),
3374 				    qstr);
3375 
3376 				Q_TOSTR(rbctd64->mu, -1, 10, qstr,
3377 				    sizeof(qstr));
3378 				printf(" RB ctd=%3d p=%3d l=%3d r=%3d c=%2d "
3379 				    "mu=%s\n",
3380 				    (int)ARB_SELFIDX(ctd64tree, rbctd64),
3381 				    (int)ARB_SELFIDX(ctd64tree,
3382 				      RB_PARENT(rbctd64, rblnk)),
3383 				    (int)ARB_SELFIDX(ctd64tree,
3384 				      RB_LEFT(rbctd64, rblnk)),
3385 				    (int)ARB_SELFIDX(ctd64tree,
3386 				      RB_RIGHT(rbctd64, rblnk)),
3387 				    RB_COLOR(rbctd64, rblnk),
3388 				    qstr);
3389 
3390 				panic("RB@%p and ARB@%p trees differ\n",
3391 				    rbctdtree, ctd64tree);
3392 			}
3393 			i++;
3394 		}
3395 #endif /* DIAGNOSTIC */
3396 	}
3397 
3398 	return (error);
3399 }
3400 
3401 static inline int
3402 stats_v1_voi_update_tdgst(enum vsd_dtype voi_dtype, struct voistatdata *voival,
3403     struct voistat *vs, struct voistatdata_tdgst *tdgst)
3404 {
3405 	s64q_t x;
3406 	int error;
3407 
3408 	error = 0;
3409 
3410 	switch (vs->dtype) {
3411 	case VSD_DTYPE_TDGSTCLUST32:
3412 		/* Use same precision as the user's centroids. */
3413 		Q_INI(&x, 0, 0, Q_NFBITS(
3414 		    ARB_CNODE(&VSD(tdgstclust32, tdgst)->ctdtree, 0)->mu));
3415 		break;
3416 	case VSD_DTYPE_TDGSTCLUST64:
3417 		/* Use same precision as the user's centroids. */
3418 		Q_INI(&x, 0, 0, Q_NFBITS(
3419 		    ARB_CNODE(&VSD(tdgstclust64, tdgst)->ctdtree, 0)->mu));
3420 		break;
3421 	default:
3422 		KASSERT(vs->dtype == VSD_DTYPE_TDGSTCLUST32 ||
3423 		    vs->dtype == VSD_DTYPE_TDGSTCLUST64,
3424 		    ("%s: vs->dtype(%d) != VSD_DTYPE_TDGSTCLUST<32|64>",
3425 		    __func__, vs->dtype));
3426 		return (EINVAL);
3427 	}
3428 
3429 	/*
3430 	 * XXXLAS: Should have both a signed and unsigned 'x' variable to avoid
3431 	 * returning EOVERFLOW if the voival would have fit in a u64q_t.
3432 	 */
3433 	switch (voi_dtype) {
3434 	case VSD_DTYPE_INT_S32:
3435 		error = Q_QCPYVALI(&x, voival->int32.s32);
3436 		break;
3437 	case VSD_DTYPE_INT_U32:
3438 		error = Q_QCPYVALI(&x, voival->int32.u32);
3439 		break;
3440 	case VSD_DTYPE_INT_S64:
3441 		error = Q_QCPYVALI(&x, voival->int64.s64);
3442 		break;
3443 	case VSD_DTYPE_INT_U64:
3444 		error = Q_QCPYVALI(&x, voival->int64.u64);
3445 		break;
3446 	case VSD_DTYPE_INT_SLONG:
3447 		error = Q_QCPYVALI(&x, voival->intlong.slong);
3448 		break;
3449 	case VSD_DTYPE_INT_ULONG:
3450 		error = Q_QCPYVALI(&x, voival->intlong.ulong);
3451 		break;
3452 	case VSD_DTYPE_Q_S32:
3453 		error = Q_QCPYVALQ(&x, voival->q32.sq32);
3454 		break;
3455 	case VSD_DTYPE_Q_U32:
3456 		error = Q_QCPYVALQ(&x, voival->q32.uq32);
3457 		break;
3458 	case VSD_DTYPE_Q_S64:
3459 		error = Q_QCPYVALQ(&x, voival->q64.sq64);
3460 		break;
3461 	case VSD_DTYPE_Q_U64:
3462 		error = Q_QCPYVALQ(&x, voival->q64.uq64);
3463 		break;
3464 	default:
3465 		error = EINVAL;
3466 		break;
3467 	}
3468 
3469 	if (error ||
3470 	    (error = stats_v1_vsd_tdgst_add(vs->dtype, tdgst, x, 1, 1)))
3471 		return (error);
3472 
3473 	vs->flags |= VS_VSDVALID;
3474 	return (0);
3475 }
3476 
3477 int
3478 stats_v1_voi_update(struct statsblobv1 *sb, int32_t voi_id,
3479     enum vsd_dtype voi_dtype, struct voistatdata *voival, uint32_t flags)
3480 {
3481 	struct voi *v;
3482 	struct voistat *vs;
3483 	void *statevsd, *vsd;
3484 	int error, i, tmperr;
3485 
3486 	error = 0;
3487 
3488 	if (sb == NULL || sb->abi != STATS_ABI_V1 || voi_id >= NVOIS(sb) ||
3489 	    voi_dtype == 0 || voi_dtype >= VSD_NUM_DTYPES || voival == NULL)
3490 		return (EINVAL);
3491 	v = &sb->vois[voi_id];
3492 	if (voi_dtype != v->dtype || v->id < 0 ||
3493 	    ((flags & SB_VOI_RELUPDATE) && !(v->flags & VOI_REQSTATE)))
3494 		return (EINVAL);
3495 
3496 	vs = BLOB_OFFSET(sb, v->stats_off);
3497 	if (v->flags & VOI_REQSTATE)
3498 		statevsd = BLOB_OFFSET(sb, vs->data_off);
3499 	else
3500 		statevsd = NULL;
3501 
3502 	if (flags & SB_VOI_RELUPDATE) {
3503 		switch (voi_dtype) {
3504 		case VSD_DTYPE_INT_S32:
3505 			voival->int32.s32 +=
3506 			    VSD(voistate, statevsd)->prev.int32.s32;
3507 			break;
3508 		case VSD_DTYPE_INT_U32:
3509 			voival->int32.u32 +=
3510 			    VSD(voistate, statevsd)->prev.int32.u32;
3511 			break;
3512 		case VSD_DTYPE_INT_S64:
3513 			voival->int64.s64 +=
3514 			    VSD(voistate, statevsd)->prev.int64.s64;
3515 			break;
3516 		case VSD_DTYPE_INT_U64:
3517 			voival->int64.u64 +=
3518 			    VSD(voistate, statevsd)->prev.int64.u64;
3519 			break;
3520 		case VSD_DTYPE_INT_SLONG:
3521 			voival->intlong.slong +=
3522 			    VSD(voistate, statevsd)->prev.intlong.slong;
3523 			break;
3524 		case VSD_DTYPE_INT_ULONG:
3525 			voival->intlong.ulong +=
3526 			    VSD(voistate, statevsd)->prev.intlong.ulong;
3527 			break;
3528 		case VSD_DTYPE_Q_S32:
3529 			error = Q_QADDQ(&voival->q32.sq32,
3530 			    VSD(voistate, statevsd)->prev.q32.sq32);
3531 			break;
3532 		case VSD_DTYPE_Q_U32:
3533 			error = Q_QADDQ(&voival->q32.uq32,
3534 			    VSD(voistate, statevsd)->prev.q32.uq32);
3535 			break;
3536 		case VSD_DTYPE_Q_S64:
3537 			error = Q_QADDQ(&voival->q64.sq64,
3538 			    VSD(voistate, statevsd)->prev.q64.sq64);
3539 			break;
3540 		case VSD_DTYPE_Q_U64:
3541 			error = Q_QADDQ(&voival->q64.uq64,
3542 			    VSD(voistate, statevsd)->prev.q64.uq64);
3543 			break;
3544 		default:
3545 			KASSERT(0, ("Unknown VOI data type %d", voi_dtype));
3546 			break;
3547 		}
3548 	}
3549 
3550 	if (error)
3551 		return (error);
3552 
3553 	for (i = v->voistatmaxid; i > 0; i--) {
3554 		vs = &((struct voistat *)BLOB_OFFSET(sb, v->stats_off))[i];
3555 		if (vs->stype < 0)
3556 			continue;
3557 
3558 		vsd = BLOB_OFFSET(sb, vs->data_off);
3559 
3560 		switch (vs->stype) {
3561 		case VS_STYPE_MAX:
3562 			tmperr = stats_v1_voi_update_max(voi_dtype, voival,
3563 			    vs, vsd);
3564 			break;
3565 		case VS_STYPE_MIN:
3566 			tmperr = stats_v1_voi_update_min(voi_dtype, voival,
3567 			    vs, vsd);
3568 			break;
3569 		case VS_STYPE_SUM:
3570 			tmperr = stats_v1_voi_update_sum(voi_dtype, voival,
3571 			    vs, vsd);
3572 			break;
3573 		case VS_STYPE_HIST:
3574 			tmperr = stats_v1_voi_update_hist(voi_dtype, voival,
3575 			    vs, vsd);
3576 			break;
3577 		case VS_STYPE_TDGST:
3578 			tmperr = stats_v1_voi_update_tdgst(voi_dtype, voival,
3579 			    vs, vsd);
3580 			break;
3581 		default:
3582 			KASSERT(0, ("Unknown VOI stat type %d", vs->stype));
3583 			break;
3584 		}
3585 
3586 		if (tmperr) {
3587 			error = tmperr;
3588 			VS_INCERRS(vs);
3589 		}
3590 	}
3591 
3592 	if (statevsd) {
3593 		switch (voi_dtype) {
3594 		case VSD_DTYPE_INT_S32:
3595 			VSD(voistate, statevsd)->prev.int32.s32 =
3596 			    voival->int32.s32;
3597 			break;
3598 		case VSD_DTYPE_INT_U32:
3599 			VSD(voistate, statevsd)->prev.int32.u32 =
3600 			    voival->int32.u32;
3601 			break;
3602 		case VSD_DTYPE_INT_S64:
3603 			VSD(voistate, statevsd)->prev.int64.s64 =
3604 			    voival->int64.s64;
3605 			break;
3606 		case VSD_DTYPE_INT_U64:
3607 			VSD(voistate, statevsd)->prev.int64.u64 =
3608 			    voival->int64.u64;
3609 			break;
3610 		case VSD_DTYPE_INT_SLONG:
3611 			VSD(voistate, statevsd)->prev.intlong.slong =
3612 			    voival->intlong.slong;
3613 			break;
3614 		case VSD_DTYPE_INT_ULONG:
3615 			VSD(voistate, statevsd)->prev.intlong.ulong =
3616 			    voival->intlong.ulong;
3617 			break;
3618 		case VSD_DTYPE_Q_S32:
3619 			error = Q_QCPYVALQ(
3620 			    &VSD(voistate, statevsd)->prev.q32.sq32,
3621 			    voival->q32.sq32);
3622 			break;
3623 		case VSD_DTYPE_Q_U32:
3624 			error = Q_QCPYVALQ(
3625 			    &VSD(voistate, statevsd)->prev.q32.uq32,
3626 			    voival->q32.uq32);
3627 			break;
3628 		case VSD_DTYPE_Q_S64:
3629 			error = Q_QCPYVALQ(
3630 			    &VSD(voistate, statevsd)->prev.q64.sq64,
3631 			    voival->q64.sq64);
3632 			break;
3633 		case VSD_DTYPE_Q_U64:
3634 			error = Q_QCPYVALQ(
3635 			    &VSD(voistate, statevsd)->prev.q64.uq64,
3636 			    voival->q64.uq64);
3637 			break;
3638 		default:
3639 			KASSERT(0, ("Unknown VOI data type %d", voi_dtype));
3640 			break;
3641 		}
3642 	}
3643 
3644 	return (error);
3645 }
3646 
3647 #ifdef _KERNEL
3648 
3649 static void
3650 stats_init(void *arg)
3651 {
3652 
3653 }
3654 SYSINIT(stats, SI_SUB_KDTRACE, SI_ORDER_FIRST, stats_init, NULL);
3655 
3656 /*
3657  * Sysctl handler to display the list of available stats templates.
3658  */
3659 static int
3660 stats_tpl_list_available(SYSCTL_HANDLER_ARGS)
3661 {
3662 	struct sbuf *s;
3663 	int err, i;
3664 
3665 	err = 0;
3666 
3667 	/* We can tolerate ntpl being stale, so do not take the lock. */
3668 	s = sbuf_new(NULL, NULL, /* +1 per tpl for , */
3669 	    ntpl * (STATS_TPL_MAX_STR_SPEC_LEN + 1), SBUF_FIXEDLEN);
3670 	if (s == NULL)
3671 		return (ENOMEM);
3672 
3673 	TPL_LIST_RLOCK();
3674 	for (i = 0; i < ntpl; i++) {
3675 		err = sbuf_printf(s, "%s\"%s\":%u", i ? "," : "",
3676 		    tpllist[i]->mb->tplname, tpllist[i]->mb->tplhash);
3677 		if (err) {
3678 			/* Sbuf overflow condition. */
3679 			err = EOVERFLOW;
3680 			break;
3681 		}
3682 	}
3683 	TPL_LIST_RUNLOCK();
3684 
3685 	if (!err) {
3686 		sbuf_finish(s);
3687 		err = sysctl_handle_string(oidp, sbuf_data(s), 0, req);
3688 	}
3689 
3690 	sbuf_delete(s);
3691 	return (err);
3692 }
3693 
3694 /*
3695  * Called by subsystem-specific sysctls to report and/or parse the list of
3696  * templates being sampled and their sampling rates. A stats_tpl_sr_cb_t
3697  * conformant function pointer must be passed in as arg1, which is used to
3698  * interact with the subsystem's stats template sample rates list. If arg2 > 0,
3699  * a zero-initialised allocation of arg2-sized contextual memory is
3700  * heap-allocated and passed in to all subsystem callbacks made during the
3701  * operation of stats_tpl_sample_rates().
3702  *
3703  * XXXLAS: Assumes templates are never removed, which is currently true but may
3704  * need to be reworked in future if dynamic template management becomes a
3705  * requirement e.g. to support kernel module based templates.
3706  */
3707 int
3708 stats_tpl_sample_rates(SYSCTL_HANDLER_ARGS)
3709 {
3710 	char kvpair_fmt[16], tplspec_fmt[16];
3711 	char tpl_spec[STATS_TPL_MAX_STR_SPEC_LEN];
3712 	char tpl_name[TPL_MAX_NAME_LEN + 2]; /* +2 for "" */
3713 	stats_tpl_sr_cb_t subsys_cb;
3714 	void *subsys_ctx;
3715 	char *buf, *new_rates_usr_str, *tpl_name_p;
3716 	struct stats_tpl_sample_rate *rates;
3717 	struct sbuf *s, _s;
3718 	uint32_t cum_pct, pct, tpl_hash;
3719 	int err, i, off, len, newlen, nrates;
3720 
3721 	buf = NULL;
3722 	rates = NULL;
3723 	err = nrates = 0;
3724 	subsys_cb = (stats_tpl_sr_cb_t)arg1;
3725 	KASSERT(subsys_cb != NULL, ("%s: subsys_cb == arg1 == NULL", __func__));
3726 	if (arg2 > 0)
3727 		subsys_ctx = malloc(arg2, M_TEMP, M_WAITOK | M_ZERO);
3728 	else
3729 		subsys_ctx = NULL;
3730 
3731 	/* Grab current count of subsystem rates. */
3732 	err = subsys_cb(TPL_SR_UNLOCKED_GET, NULL, &nrates, subsys_ctx);
3733 	if (err)
3734 		goto done;
3735 
3736 	/* +1 to ensure we can append '\0' post copyin, +5 per rate for =nnn, */
3737 	len = max(req->newlen + 1, nrates * (STATS_TPL_MAX_STR_SPEC_LEN + 5));
3738 
3739 	if (req->oldptr != NULL || req->newptr != NULL)
3740 		buf = malloc(len, M_TEMP, M_WAITOK);
3741 
3742 	if (req->oldptr != NULL) {
3743 		if (nrates == 0) {
3744 			/* No rates, so return an empty string via oldptr. */
3745 			err = SYSCTL_OUT(req, "", 1);
3746 			if (err)
3747 				goto done;
3748 			goto process_new;
3749 		}
3750 
3751 		s = sbuf_new(&_s, buf, len, SBUF_FIXEDLEN | SBUF_INCLUDENUL);
3752 
3753 		/* Grab locked count of, and ptr to, subsystem rates. */
3754 		err = subsys_cb(TPL_SR_RLOCKED_GET, &rates, &nrates,
3755 		    subsys_ctx);
3756 		if (err)
3757 			goto done;
3758 		TPL_LIST_RLOCK();
3759 		for (i = 0; i < nrates && !err; i++) {
3760 			err = sbuf_printf(s, "%s\"%s\":%u=%u", i ? "," : "",
3761 			    tpllist[rates[i].tpl_slot_id]->mb->tplname,
3762 			    tpllist[rates[i].tpl_slot_id]->mb->tplhash,
3763 			    rates[i].tpl_sample_pct);
3764 		}
3765 		TPL_LIST_RUNLOCK();
3766 		/* Tell subsystem that we're done with its rates list. */
3767 		err = subsys_cb(TPL_SR_RUNLOCK, &rates, &nrates, subsys_ctx);
3768 		if (err)
3769 			goto done;
3770 
3771 		err = sbuf_finish(s);
3772 		if (err)
3773 			goto done; /* We lost a race for buf to be too small. */
3774 
3775 		/* Return the rendered string data via oldptr. */
3776 		err = SYSCTL_OUT(req, sbuf_data(s), sbuf_len(s));
3777 	} else {
3778 		/* Return the upper bound size for buffer sizing requests. */
3779 		err = SYSCTL_OUT(req, NULL, len);
3780 	}
3781 
3782 process_new:
3783 	if (err || req->newptr == NULL)
3784 		goto done;
3785 
3786 	newlen = req->newlen - req->newidx;
3787 	err = SYSCTL_IN(req, buf, newlen);
3788 	if (err)
3789 		goto done;
3790 
3791 	/*
3792 	 * Initialise format strings at run time.
3793 	 *
3794 	 * Write the max template spec string length into the
3795 	 * template_spec=percent key-value pair parsing format string as:
3796 	 *     " %<width>[^=]=%u %n"
3797 	 *
3798 	 * Write the max template name string length into the tplname:tplhash
3799 	 * parsing format string as:
3800 	 *     "%<width>[^:]:%u"
3801 	 *
3802 	 * Subtract 1 for \0 appended by sscanf().
3803 	 */
3804 	sprintf(kvpair_fmt, " %%%zu[^=]=%%u %%n", sizeof(tpl_spec) - 1);
3805 	sprintf(tplspec_fmt, "%%%zu[^:]:%%u", sizeof(tpl_name) - 1);
3806 
3807 	/*
3808 	 * Parse each CSV key-value pair specifying a template and its sample
3809 	 * percentage. Whitespace either side of a key-value pair is ignored.
3810 	 * Templates can be specified by name, hash, or name and hash per the
3811 	 * following formats (chars in [] are optional):
3812 	 *    ["]<tplname>["]=<percent>
3813 	 *    :hash=pct
3814 	 *    ["]<tplname>["]:hash=<percent>
3815 	 */
3816 	cum_pct = nrates = 0;
3817 	rates = NULL;
3818 	buf[newlen] = '\0'; /* buf is at least newlen+1 in size. */
3819 	new_rates_usr_str = buf;
3820 	while (isspace(*new_rates_usr_str))
3821 		new_rates_usr_str++; /* Skip leading whitespace. */
3822 	while (*new_rates_usr_str != '\0') {
3823 		tpl_name_p = tpl_name;
3824 		tpl_name[0] = '\0';
3825 		tpl_hash = 0;
3826 		off = 0;
3827 
3828 		/*
3829 		 * Parse key-value pair which must perform 2 conversions, then
3830 		 * parse the template spec to extract either name, hash, or name
3831 		 * and hash depending on the three possible spec formats. The
3832 		 * tplspec_fmt format specifier parses name or name and hash
3833 		 * template specs, while the ":%u" format specifier parses
3834 		 * hash-only template specs. If parsing is successfull, ensure
3835 		 * the cumulative sampling percentage does not exceed 100.
3836 		 */
3837 		err = EINVAL;
3838 		if (2 != sscanf(new_rates_usr_str, kvpair_fmt, tpl_spec, &pct,
3839 		    &off))
3840 			break;
3841 		if ((1 > sscanf(tpl_spec, tplspec_fmt, tpl_name, &tpl_hash)) &&
3842 		    (1 != sscanf(tpl_spec, ":%u", &tpl_hash)))
3843 			break;
3844 		if ((cum_pct += pct) > 100)
3845 			break;
3846 		err = 0;
3847 
3848 		/* Strip surrounding "" from template name if present. */
3849 		len = strlen(tpl_name);
3850 		if (len > 0) {
3851 			if (tpl_name[len - 1] == '"')
3852 				tpl_name[--len] = '\0';
3853 			if (tpl_name[0] == '"') {
3854 				tpl_name_p++;
3855 				len--;
3856 			}
3857 		}
3858 
3859 		rates = stats_realloc(rates, 0, /* oldsz is unused in kernel. */
3860 		    (nrates + 1) * sizeof(*rates), M_WAITOK);
3861 		rates[nrates].tpl_slot_id =
3862 		    stats_tpl_fetch_allocid(len ? tpl_name_p : NULL, tpl_hash);
3863 		if (rates[nrates].tpl_slot_id < 0) {
3864 			err = -rates[nrates].tpl_slot_id;
3865 			break;
3866 		}
3867 		rates[nrates].tpl_sample_pct = pct;
3868 		nrates++;
3869 		new_rates_usr_str += off;
3870 		if (*new_rates_usr_str != ',')
3871 			break; /* End-of-input or malformed. */
3872 		new_rates_usr_str++; /* Move past comma to next pair. */
3873 	}
3874 
3875 	if (!err) {
3876 		if ((new_rates_usr_str - buf) < newlen) {
3877 			/* Entire input has not been consumed. */
3878 			err = EINVAL;
3879 		} else {
3880 			/*
3881 			 * Give subsystem the new rates. They'll return the
3882 			 * appropriate rates pointer for us to garbage collect.
3883 			 */
3884 			err = subsys_cb(TPL_SR_PUT, &rates, &nrates,
3885 			    subsys_ctx);
3886 		}
3887 	}
3888 	stats_free(rates);
3889 
3890 done:
3891 	free(buf, M_TEMP);
3892 	free(subsys_ctx, M_TEMP);
3893 	return (err);
3894 }
3895 
3896 SYSCTL_NODE(_kern, OID_AUTO, stats, CTLFLAG_RW, NULL,
3897     "stats(9) MIB");
3898 
3899 SYSCTL_PROC(_kern_stats, OID_AUTO, templates, CTLTYPE_STRING|CTLFLAG_RD,
3900     NULL, 0, stats_tpl_list_available, "A",
3901     "list the name/hash of all available stats(9) templates");
3902 
3903 #else /* ! _KERNEL */
3904 
3905 static void __attribute__ ((constructor))
3906 stats_constructor(void)
3907 {
3908 
3909 	pthread_rwlock_init(&tpllistlock, NULL);
3910 }
3911 
3912 static void __attribute__ ((destructor))
3913 stats_destructor(void)
3914 {
3915 
3916 	pthread_rwlock_destroy(&tpllistlock);
3917 }
3918 
3919 #endif /* _KERNEL */
3920