mtree/BSD.root.dist: Use spaces.
[dragonfly.git] / sys / kern / kern_collect.c
blobc4b42f612903f297fc50622dab5fab9edb867091
1 /*
2 * Copyright (c) 2017 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
33 * Collects general statistics on a 10-second interval.
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/proc.h>
40 #include <sys/kinfo.h>
41 #include <sys/queue.h>
42 #include <sys/sysctl.h>
43 #include <sys/kthread.h>
44 #include <machine/cpu.h>
45 #include <sys/lock.h>
46 #include <sys/spinlock.h>
47 #include <sys/kcollect.h>
49 #include <sys/thread2.h>
50 #include <sys/spinlock2.h>
52 #include <vm/vm.h>
53 #include <vm/vm_param.h>
54 #include <vm/vm_kern.h>
55 #include <vm/vm_object.h>
56 #include <vm/vm_page.h>
57 #include <vm/vm_map.h>
58 #include <vm/vm_pager.h>
59 #include <vm/vm_extern.h>
61 #include <machine/stdarg.h>
62 #include <machine/smp.h>
63 #include <machine/clock.h>
65 static uint32_t kcollect_samples = -1; /* 0 to disable */
66 TUNABLE_INT("kern.collect_samples", &kcollect_samples);
67 SYSCTL_UINT(_kern, OID_AUTO, collect_samples, CTLFLAG_RD,
68 &kcollect_samples, 0, "number of 10-second samples");
70 static uint64_t kcollect_index;
71 static const char *kcollect_slots[KCOLLECT_ENTRIES];
72 static kcallback_t kcollect_callback[KCOLLECT_ENTRIES];
73 static kcollect_t kcollect_scale;
74 static kcollect_t *kcollect_ary;
75 static struct lock kcollect_lock = LOCK_INITIALIZER("kcolk", 0, 0);
77 MALLOC_DEFINE(M_KCOLLECT, "kcollect", "kcollect array");
79 int
80 kcollect_register(int which, const char *id, kcallback_t func, uint64_t scale)
82 int n;
84 lockmgr(&kcollect_lock, LK_EXCLUSIVE);
85 if (which >= 0) {
86 n = which;
87 } else {
88 for (n = KCOLLECT_DYNAMIC_START; n < KCOLLECT_ENTRIES; ++n) {
89 if (kcollect_slots[n] == NULL)
90 break;
93 if (n < 0 || n >= KCOLLECT_ENTRIES) {
94 n = -1;
95 } else {
96 kcollect_slots[n] = id;
97 kcollect_callback[n] = func;
98 kcollect_scale.data[n] = scale;
100 lockmgr(&kcollect_lock, LK_RELEASE);
102 return n;
105 void
106 kcollect_unregister(int n)
108 lockmgr(&kcollect_lock, LK_EXCLUSIVE);
109 if (n >= 0 && n < KCOLLECT_ENTRIES) {
110 kcollect_slots[n] = NULL;
111 kcollect_callback[n] = NULL;
113 lockmgr(&kcollect_lock, LK_RELEASE);
117 * Typically called by a rollup function in the callback from the
118 * collection thread. Not usually called ad-hoc. This allows a
119 * subsystem to register several collection ids but only one callback
120 * which populates all of them.
122 void
123 kcollect_setvalue(int n, uint64_t value)
125 uint32_t i;
127 if (n >= 0 && n < KCOLLECT_ENTRIES) {
128 i = kcollect_index % kcollect_samples;
129 kcollect_ary[i].data[n] = value;
134 * Callback to change scale adjustment, if necessary. Certain statistics
135 * have scale info available (such as KCOLLECT_SWAPANO and SWAPCAC).
137 void
138 kcollect_setscale(int n, uint64_t value)
140 if (n >= 0 && n < KCOLLECT_ENTRIES) {
141 kcollect_scale.data[n] = value;
145 static
146 void
147 kcollect_thread(void *dummy)
149 uint32_t i;
150 int n;
152 for (;;) {
153 lockmgr(&kcollect_lock, LK_EXCLUSIVE);
154 i = kcollect_index % kcollect_samples;
155 bzero(&kcollect_ary[i], sizeof(kcollect_ary[i]));
156 crit_enter();
157 kcollect_ary[i].ticks = ticks;
158 getmicrotime(&kcollect_ary[i].realtime);
159 crit_exit();
160 for (n = 0; n < KCOLLECT_ENTRIES; ++n) {
161 if (kcollect_callback[n]) {
162 kcollect_ary[i].data[n] =
163 kcollect_callback[n](n);
166 cpu_sfence();
167 ++kcollect_index;
168 lockmgr(&kcollect_lock, LK_RELEASE);
169 tsleep(&dummy, 0, "sleep", hz * KCOLLECT_INTERVAL);
174 * No requirements.
176 static int
177 sysctl_kcollect_data(SYSCTL_HANDLER_ARGS)
179 int error;
180 uint32_t i;
181 uint32_t start;
182 uint64_t count;
183 kcollect_t scale;
184 kcollect_t id;
186 if (kcollect_samples == (uint32_t)-1 ||
187 kcollect_samples == 0) {
188 return EINVAL;
191 error = 0;
192 count = kcollect_index;
193 start = count % kcollect_samples;
194 if (count >= kcollect_samples)
195 count = kcollect_samples - 1;
198 * Sizing request
200 if (req->oldptr == NULL) {
201 error = SYSCTL_OUT(req, 0, sizeof(kcollect_t) * (count + 2));
202 return error;
206 * Output request. We output a scale record, a string record, and
207 * N collection records. The strings in the string record can be
208 * up to 8 characters long, and if a string is 8 characters long it
209 * will not be zero-terminated.
211 * The low byte of the scale record specifies the format. To get
212 * the scale value shift right by 8.
214 if (kcollect_ary == NULL)
215 return ENOTSUP;
217 lockmgr(&kcollect_lock, LK_EXCLUSIVE);
218 scale = kcollect_scale;
219 scale.ticks = ticks;
220 scale.hz = hz;
222 bzero(&id, sizeof(id));
223 for (i = 0; i < KCOLLECT_ENTRIES; ++i) {
224 if (kcollect_slots[i]) {
225 char *ptr = (char *)&id.data[i];
226 size_t len = strlen(kcollect_slots[i]);
227 if (len > sizeof(id.data[0]))
228 len = sizeof(id.data[0]);
229 bcopy(kcollect_slots[i], ptr, len);
232 lockmgr(&kcollect_lock, LK_RELEASE);
234 error = SYSCTL_OUT(req, &scale, sizeof(scale));
235 if (error == 0)
236 error = SYSCTL_OUT(req, &id, sizeof(id));
239 * Start at the current entry (not yet populated) and work
240 * backwards. This allows callers of the sysctl to acquire
241 * a lesser amount of data aligned to the most recent side of
242 * the array.
244 i = start;
245 while (count) {
246 if (req->oldlen - req->oldidx < sizeof(kcollect_t))
247 break;
248 if (i == 0)
249 i = kcollect_samples - 1;
250 else
251 --i;
252 error = SYSCTL_OUT(req, &kcollect_ary[i], sizeof(kcollect_t));
253 if (error)
254 break;
255 --count;
257 return error;
259 SYSCTL_PROC(_kern, OID_AUTO, collect_data,
260 CTLFLAG_RD | CTLTYPE_STRUCT, 0, 0,
261 sysctl_kcollect_data, "S,kcollect", "Dump collected statistics");
263 static
264 void
265 kcollect_thread_init(void)
267 thread_t td = NULL;
270 * Autosize sample retention (10 second interval)
272 if ((int)kcollect_samples < 0) {
273 if (kmem_lim_size() < 1024)
274 kcollect_samples = 1024;
275 else
276 kcollect_samples = 8192;
279 if (kcollect_samples) {
280 kcollect_ary = kmalloc(kcollect_samples * sizeof(kcollect_t),
281 M_KCOLLECT, M_WAITOK | M_ZERO);
282 lwkt_create(kcollect_thread, NULL, &td, NULL, 0, 0, "kcollect");
285 SYSINIT(kcol, SI_SUB_HELPER_THREADS, SI_ORDER_ANY, kcollect_thread_init, 0);