1 /* 2 * Copyright (c) 2017 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 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. 17 * 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. 30 */ 31 32 /* 33 * Collects general statistics on a 10-second interval. 34 */ 35 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> 48 49 #include <sys/thread2.h> 50 #include <sys/spinlock2.h> 51 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> 60 61 #include <machine/stdarg.h> 62 #include <machine/smp.h> 63 #include <machine/clock.h> 64 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"); 69 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); 76 77 MALLOC_DEFINE(M_KCOLLECT, "kcollect", "kcollect array"); 78 79 int 80 kcollect_register(int which, const char *id, kcallback_t func, uint64_t scale) 81 { 82 int n; 83 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; 91 } 92 } 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; 99 } 100 lockmgr(&kcollect_lock, LK_RELEASE); 101 102 return n; 103 } 104 105 void 106 kcollect_unregister(int n) 107 { 108 lockmgr(&kcollect_lock, LK_EXCLUSIVE); 109 if (n >= 0 && n < KCOLLECT_ENTRIES) { 110 kcollect_slots[n] = NULL; 111 kcollect_callback[n] = NULL; 112 } 113 lockmgr(&kcollect_lock, LK_RELEASE); 114 } 115 116 /* 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. 121 */ 122 void 123 kcollect_setvalue(int n, uint64_t value) 124 { 125 uint32_t i; 126 127 if (n >= 0 && n < KCOLLECT_ENTRIES) { 128 i = kcollect_index % kcollect_samples; 129 kcollect_ary[i].data[n] = value; 130 } 131 } 132 133 /* 134 * Callback to change scale adjustment, if necessary. Certain statistics 135 * have scale info available (such as KCOLLECT_SWAPANO and SWAPCAC). 136 */ 137 void 138 kcollect_setscale(int n, uint64_t value) 139 { 140 if (n >= 0 && n < KCOLLECT_ENTRIES) { 141 kcollect_scale.data[n] = value; 142 } 143 } 144 145 static 146 void 147 kcollect_thread(void *dummy) 148 { 149 uint32_t i; 150 int n; 151 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); 164 } 165 } 166 cpu_sfence(); 167 ++kcollect_index; 168 lockmgr(&kcollect_lock, LK_RELEASE); 169 tsleep(&dummy, 0, "sleep", hz * KCOLLECT_INTERVAL); 170 } 171 } 172 173 /* 174 * No requirements. 175 */ 176 static int 177 sysctl_kcollect_data(SYSCTL_HANDLER_ARGS) 178 { 179 int error; 180 uint32_t i; 181 uint32_t start; 182 uint64_t count; 183 kcollect_t scale; 184 kcollect_t id; 185 186 if (kcollect_samples == (uint32_t)-1 || 187 kcollect_samples == 0) { 188 return EINVAL; 189 } 190 191 error = 0; 192 count = kcollect_index; 193 start = count % kcollect_samples; 194 if (count >= kcollect_samples) 195 count = kcollect_samples - 1; 196 197 /* 198 * Sizing request 199 */ 200 if (req->oldptr == NULL) { 201 error = SYSCTL_OUT(req, 0, sizeof(kcollect_t) * (count + 2)); 202 return error; 203 } 204 205 /* 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. 210 * 211 * The low byte of the scale record specifies the format. To get 212 * the scale value shift right by 8. 213 */ 214 if (kcollect_ary == NULL) 215 return ENOTSUP; 216 217 lockmgr(&kcollect_lock, LK_EXCLUSIVE); 218 scale = kcollect_scale; 219 scale.ticks = ticks; 220 scale.hz = hz; 221 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); 230 } 231 } 232 lockmgr(&kcollect_lock, LK_RELEASE); 233 234 error = SYSCTL_OUT(req, &scale, sizeof(scale)); 235 if (error == 0) 236 error = SYSCTL_OUT(req, &id, sizeof(id)); 237 238 /* 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. 243 */ 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; 256 } 257 return error; 258 } 259 SYSCTL_PROC(_kern, OID_AUTO, collect_data, 260 CTLFLAG_RD | CTLTYPE_STRUCT, 0, 0, 261 sysctl_kcollect_data, "S,kcollect", "Dump collected statistics"); 262 263 static 264 void 265 kcollect_thread_init(void) 266 { 267 thread_t td = NULL; 268 269 /* 270 * Autosize sample retention (10 second interval) 271 */ 272 if ((int)kcollect_samples < 0) { 273 if (kmem_lim_size() < 1024) 274 kcollect_samples = 1024; 275 else 276 kcollect_samples = 8192; 277 } 278 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"); 283 } 284 } 285 SYSINIT(kcol, SI_SUB_HELPER_THREADS, SI_ORDER_ANY, kcollect_thread_init, 0); 286